Version 0.5.7.0 .

svn merge -r 22491:22574 https://dart.googlecode.com/svn/branches/bleeding_edge trunk

git-svn-id: http://dart.googlecode.com/svn/trunk@22576 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/pkg/analyzer_experimental/bin/analyzer.dart b/pkg/analyzer_experimental/bin/analyzer.dart
index 2beb6aa..cf8018f 100644
--- a/pkg/analyzer_experimental/bin/analyzer.dart
+++ b/pkg/analyzer_experimental/bin/analyzer.dart
@@ -20,8 +20,8 @@
 import 'package:analyzer_experimental/src/generated/element.dart';
 import 'package:analyzer_experimental/options.dart';
 
-part 'package:analyzer_experimental/analyzer.dart';
-part 'package:analyzer_experimental/error_formatter.dart';
+import 'package:analyzer_experimental/src/analyzer_impl.dart';
+import 'package:analyzer_experimental/src/error_formatter.dart';
 
 void main() {
   var args = new Options().arguments;
@@ -51,10 +51,10 @@
       return ErrorSeverity.ERROR;
     }
     // start analysis
-    _ErrorFormatter formatter = new _ErrorFormatter(options.machineFormat ? stderr : stdout, options);
+    ErrorFormatter formatter = new ErrorFormatter(options.machineFormat ? stderr : stdout, options);
     formatter.startAnalysis();
     // do analyze
-    _AnalyzerImpl analyzer = new _AnalyzerImpl(options);
+    AnalyzerImpl analyzer = new AnalyzerImpl(options);
     analyzer.analyze(sourcePath);
     // pring errors
     formatter.formatErrors(analyzer.errorInfos);
diff --git a/pkg/analyzer_experimental/lib/analyzer.dart b/pkg/analyzer_experimental/lib/analyzer.dart
index 47f0129..30f8857 100644
--- a/pkg/analyzer_experimental/lib/analyzer.dart
+++ b/pkg/analyzer_experimental/lib/analyzer.dart
@@ -1,134 +1,65 @@
 // Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-part of analyzer;
 
-/// Analyzes single library [File].
-class _AnalyzerImpl {
-  final CommandLineOptions options;
-  DartSdk sdk;
+library analyzer;
 
-  ContentCache contentCache = new ContentCache();
-  SourceFactory sourceFactory;
-  AnalysisContext context;
+import 'dart:io';
 
-  /// All [Source]s references by the analyzed library.
-  final Set<Source> sources = new Set<Source>();
+import 'src/error.dart';
+import 'src/generated/ast.dart';
+import 'src/generated/error.dart';
+import 'src/generated/java_io.dart';
+import 'src/generated/parser.dart';
+import 'src/generated/scanner.dart';
+import 'src/generated/source_io.dart';
+import 'src/utils.dart';
 
-  /// All [AnalysisErrorInfo]s in the analyzed library.
-  final List<AnalysisErrorInfo> errorInfos = new List<AnalysisErrorInfo>();
+export 'src/error.dart';
+export 'src/generated/ast.dart';
+export 'src/generated/error.dart';
+export 'src/generated/utilities_dart.dart';
 
-  _AnalyzerImpl(CommandLineOptions this.options) {
-    sdk = new DirectoryBasedDartSdk(new JavaFile(options.dartSdkPath));
+/// Parses a Dart file into an AST.
+CompilationUnit parseDartFile(String path) {
+  var contents = new File(path).readAsStringSync();
+  var errorCollector = new _ErrorCollector();
+  var sourceFactory = new SourceFactory.con2([new FileUriResolver()]);
+  var source = sourceFactory.forUri(pathToFileUri(path).toString());
+  var scanner = new StringScanner(source, contents, errorCollector);
+  var token = scanner.tokenize();
+  var parser = new Parser(source, errorCollector);
+  var unit = parser.parseCompilationUnit(token);
+  unit.lineInfo = new LineInfo(scanner.lineStarts);
+
+  if (errorCollector.hasErrors) throw errorCollector.group;
+
+  return unit;
+}
+
+/// Converts an AST node representing a string literal into a [String].
+String stringLiteralToString(StringLiteral literal) {
+  if (literal is AdjacentStrings) {
+    return literal.strings.map(stringLiteralToString).join();
+  } else if (literal is SimpleStringLiteral) {
+    return literal.value;
+  } else {
+    throw new ArgumentError("Can't convert $literal to a Dart string.");
   }
+}
 
-  /**
-   * Treats the [sourcePath] as the top level library and analyzes it.
-   */
-  void analyze(String sourcePath) {
-    sources.clear();
-    errorInfos.clear();
-    if (sourcePath == null) {
-      throw new ArgumentError("sourcePath cannot be null");
-    }
-    var sourceFile = new JavaFile(sourcePath);
-    var librarySource = new FileBasedSource.con1(contentCache, sourceFile);
-    // resolve library
-    prepareAnalysisContext(sourceFile);
-    var libraryElement = context.computeLibraryElement(librarySource);
-    // prepare source and errors
-    prepareSources(libraryElement);
-    prepareErrors();
-  }
+/// A simple error listener that collects errors into an [AnalysisErrorGroup].
+class _ErrorCollector extends AnalysisErrorListener {
+  final _errors = <AnalysisError>[];
 
-  /// Returns the maximal [ErrorSeverity] of the recorded errors.
-  ErrorSeverity get maxErrorSeverity {
-    var status = ErrorSeverity.NONE;
-    for (AnalysisErrorInfo errorInfo in errorInfos) {
-      for (AnalysisError error in errorInfo.errors) {
-        var severity = error.errorCode.errorSeverity;
-        status = status.max(severity);
-      }
-    }
-    return status;
-  }
+  /// Whether any errors where collected.
+  bool get hasErrors => !_errors.isEmpty;
 
-  void prepareAnalysisContext(JavaFile sourceFile) {
-    List<UriResolver> resolvers = [new DartUriResolver(sdk), new FileUriResolver()];
-    // may be add package resolver
-    {
-      var packageDirectory = getPackageDirectoryFor(sourceFile);
-      if (packageDirectory != null) {
-        resolvers.add(new PackageUriResolver([packageDirectory]));
-      }
-    }
-    sourceFactory = new SourceFactory.con1(contentCache, resolvers);
-    context = AnalysisEngine.instance.createAnalysisContext();
-    context.sourceFactory = sourceFactory;
-  }
+  /// The group of errors collected.
+  AnalyzerErrorGroup get group =>
+    new AnalyzerErrorGroup.fromAnalysisErrors(_errors);
 
-  /// Fills [sources].
-  void prepareSources(LibraryElement library) {
-    var units = new Set<CompilationUnitElement>();
-    var libraries = new Set<LibraryElement>();
-    addLibrarySources(library, libraries, units);
-  }
+  _ErrorCollector();
 
-  void addCompilationUnitSource(CompilationUnitElement unit, Set<LibraryElement> libraries,
-      Set<CompilationUnitElement> units) {
-    if (unit == null || units.contains(unit)) {
-      return;
-    }
-    units.add(unit);
-    sources.add(unit.source);
-  }
-
-  void addLibrarySources(LibraryElement library, Set<LibraryElement> libraries,
-      Set<CompilationUnitElement> units) {
-    if (library == null || libraries.contains(library)) {
-      return;
-    }
-    libraries.add(library);
-    // may be skip library
-    {
-      UriKind uriKind = library.source.uriKind;
-      // Optionally skip package: libraries.
-      if (!options.showPackageWarnings && uriKind == UriKind.PACKAGE_URI) {
-        return;
-      }
-      // Optionally skip SDK libraries.
-      if (!options.showSdkWarnings && uriKind == UriKind.DART_URI) {
-        return;
-      }
-    }
-    // add compilation units
-    addCompilationUnitSource(library.definingCompilationUnit, libraries, units);
-    for (CompilationUnitElement child in library.parts) {
-      addCompilationUnitSource(child, libraries, units);
-    }
-    // add referenced libraries
-    for (LibraryElement child in library.importedLibraries) {
-      addLibrarySources(child, libraries, units);
-    }
-    for (LibraryElement child in library.exportedLibraries) {
-      addLibrarySources(child, libraries, units);
-    }
-  }
-
-  /// Fills [errorInfos].
-  void prepareErrors() {
-    for (Source source in sources) {
-      var sourceErrors = context.getErrors(source);
-      errorInfos.add(sourceErrors);
-    }
-  }
-
-  static JavaFile getPackageDirectoryFor(JavaFile sourceFile) {
-    JavaFile sourceFolder = sourceFile.getParentFile();
-    JavaFile packagesFolder = new JavaFile.relative(sourceFolder, "packages");
-    if (packagesFolder.exists()) {
-      return packagesFolder;
-    }
-    return null;
-  }
+  void onError(AnalysisError error) => _errors.add(error);
 }
diff --git a/pkg/analyzer_experimental/lib/src/analyzer_impl.dart b/pkg/analyzer_experimental/lib/src/analyzer_impl.dart
new file mode 100644
index 0000000..40b8deb
--- /dev/null
+++ b/pkg/analyzer_experimental/lib/src/analyzer_impl.dart
@@ -0,0 +1,148 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library analyzer_impl;
+
+import 'dart:async';
+import 'dart:io';
+
+import 'generated/java_io.dart';
+import 'generated/engine.dart';
+import 'generated/error.dart';
+import 'generated/source_io.dart';
+import 'generated/sdk.dart';
+import 'generated/sdk_io.dart';
+import 'generated/ast.dart';
+import 'generated/element.dart';
+import '../options.dart';
+
+/// Analyzes single library [File].
+class AnalyzerImpl {
+  final CommandLineOptions options;
+  DartSdk sdk;
+
+  ContentCache contentCache = new ContentCache();
+  SourceFactory sourceFactory;
+  AnalysisContext context;
+
+  /// All [Source]s references by the analyzed library.
+  final Set<Source> sources = new Set<Source>();
+
+  /// All [AnalysisErrorInfo]s in the analyzed library.
+  final List<AnalysisErrorInfo> errorInfos = new List<AnalysisErrorInfo>();
+
+  AnalyzerImpl(CommandLineOptions this.options) {
+    sdk = new DirectoryBasedDartSdk(new JavaFile(options.dartSdkPath));
+  }
+
+  /**
+   * Treats the [sourcePath] as the top level library and analyzes it.
+   */
+  void analyze(String sourcePath) {
+    sources.clear();
+    errorInfos.clear();
+    if (sourcePath == null) {
+      throw new ArgumentError("sourcePath cannot be null");
+    }
+    var sourceFile = new JavaFile(sourcePath);
+    var librarySource = new FileBasedSource.con1(contentCache, sourceFile);
+    // resolve library
+    prepareAnalysisContext(sourceFile);
+    var libraryElement = context.computeLibraryElement(librarySource);
+    // prepare source and errors
+    prepareSources(libraryElement);
+    prepareErrors();
+  }
+
+  /// Returns the maximal [ErrorSeverity] of the recorded errors.
+  ErrorSeverity get maxErrorSeverity {
+    var status = ErrorSeverity.NONE;
+    for (AnalysisErrorInfo errorInfo in errorInfos) {
+      for (AnalysisError error in errorInfo.errors) {
+        var severity = error.errorCode.errorSeverity;
+        status = status.max(severity);
+      }
+    }
+    return status;
+  }
+
+  void prepareAnalysisContext(JavaFile sourceFile) {
+    List<UriResolver> resolvers = [new DartUriResolver(sdk), new FileUriResolver()];
+    // may be add package resolver
+    {
+      var packageDirectory = getPackageDirectoryFor(sourceFile);
+      if (packageDirectory != null) {
+        resolvers.add(new PackageUriResolver([packageDirectory]));
+      }
+    }
+    sourceFactory = new SourceFactory.con1(contentCache, resolvers);
+    context = AnalysisEngine.instance.createAnalysisContext();
+    context.sourceFactory = sourceFactory;
+  }
+
+  /// Fills [sources].
+  void prepareSources(LibraryElement library) {
+    var units = new Set<CompilationUnitElement>();
+    var libraries = new Set<LibraryElement>();
+    addLibrarySources(library, libraries, units);
+  }
+
+  void addCompilationUnitSource(CompilationUnitElement unit, Set<LibraryElement> libraries,
+      Set<CompilationUnitElement> units) {
+    if (unit == null || units.contains(unit)) {
+      return;
+    }
+    units.add(unit);
+    sources.add(unit.source);
+  }
+
+  void addLibrarySources(LibraryElement library, Set<LibraryElement> libraries,
+      Set<CompilationUnitElement> units) {
+    if (library == null || libraries.contains(library)) {
+      return;
+    }
+    libraries.add(library);
+    // may be skip library
+    {
+      UriKind uriKind = library.source.uriKind;
+      // Optionally skip package: libraries.
+      if (!options.showPackageWarnings && uriKind == UriKind.PACKAGE_URI) {
+        return;
+      }
+      // Optionally skip SDK libraries.
+      if (!options.showSdkWarnings && uriKind == UriKind.DART_URI) {
+        return;
+      }
+    }
+    // add compilation units
+    addCompilationUnitSource(library.definingCompilationUnit, libraries, units);
+    for (CompilationUnitElement child in library.parts) {
+      addCompilationUnitSource(child, libraries, units);
+    }
+    // add referenced libraries
+    for (LibraryElement child in library.importedLibraries) {
+      addLibrarySources(child, libraries, units);
+    }
+    for (LibraryElement child in library.exportedLibraries) {
+      addLibrarySources(child, libraries, units);
+    }
+  }
+
+  /// Fills [errorInfos].
+  void prepareErrors() {
+    for (Source source in sources) {
+      var sourceErrors = context.getErrors(source);
+      errorInfos.add(sourceErrors);
+    }
+  }
+
+  static JavaFile getPackageDirectoryFor(JavaFile sourceFile) {
+    JavaFile sourceFolder = sourceFile.getParentFile();
+    JavaFile packagesFolder = new JavaFile.relative(sourceFolder, "packages");
+    if (packagesFolder.exists()) {
+      return packagesFolder;
+    }
+    return null;
+  }
+}
diff --git a/pkg/analyzer_experimental/lib/src/error.dart b/pkg/analyzer_experimental/lib/src/error.dart
new file mode 100644
index 0000000..668dedb
--- /dev/null
+++ b/pkg/analyzer_experimental/lib/src/error.dart
@@ -0,0 +1,104 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:collection';
+import 'dart:io';
+import 'dart:math' as math;
+
+import 'generated/error.dart';
+import 'generated/java_core.dart';
+import 'generated/source.dart';
+
+/// The maximum line length when printing extracted source code when converting
+/// an [AnalyzerError] to a string.
+final _MAX_ERROR_LINE_LENGTH = 120;
+
+/// An error class that collects multiple [AnalyzerError]s that are emitted
+/// during a single analysis.
+class AnalyzerErrorGroup implements Exception {
+  /// The errors in this collection.
+  List<AnalyzerError> get errors =>
+    new UnmodifiableListView<AnalyzerError>(_errors);
+  final List<AnalyzerError> _errors;
+
+  AnalyzerErrorGroup(Iterable<AnalyzerError> errors)
+      : _errors = errors.toList();
+
+  /// Creates an [AnalyzerErrorGroup] from a list of lower-level
+  /// [AnalysisError]s.
+  AnalyzerErrorGroup.fromAnalysisErrors(Iterable<AnalysisError> errors)
+      : this(errors.map((e) => new AnalyzerError(e)));
+
+  String get message => toString();
+  String toString() => errors.join("\n");
+}
+
+/// A wrapper around [AnalysisError] that provides a more user-friendly string
+/// representation.
+class AnalyzerError implements Exception {
+  final AnalysisError error;
+
+  AnalyzerError(this.error);
+
+  String get message => toString();
+
+  String toString() {
+    var builder = new StringBuffer();
+    var receiver = new _ContentReceiver();
+    error.source.getContents(receiver);
+    var beforeError = receiver.result.substring(0, error.offset);
+    var lineNumber = "\n".allMatches(beforeError).length + 1;
+    builder.writeln("Error on line $lineNumber of ${error.source.fullName}: "
+        "${error.message}");
+
+    var errorLineIndex = beforeError.lastIndexOf("\n") + 1;
+    var errorEndOfLineIndex = receiver.result.indexOf("\n", error.offset);
+    if (errorEndOfLineIndex == -1) errorEndOfLineIndex = receiver.result.length;
+    var errorLine = receiver.result.substring(
+        errorLineIndex, errorEndOfLineIndex);
+    var errorColumn = error.offset - errorLineIndex;
+    var errorLength = error.length;
+
+    // Ensure that the error line we display isn't too long.
+    if (errorLine.length > _MAX_ERROR_LINE_LENGTH) {
+      var leftLength = errorColumn;
+      var rightLength = errorLine.length - leftLength;
+      if (leftLength > _MAX_ERROR_LINE_LENGTH ~/ 2 &&
+          rightLength > _MAX_ERROR_LINE_LENGTH ~/ 2) {
+        errorLine = "..." + errorLine.substring(
+            errorColumn - _MAX_ERROR_LINE_LENGTH ~/ 2 + 3,
+            errorColumn + _MAX_ERROR_LINE_LENGTH ~/ 2 - 3)
+            + "...";
+        errorColumn = _MAX_ERROR_LINE_LENGTH ~/ 2;
+      } else if (rightLength > _MAX_ERROR_LINE_LENGTH ~/ 2) {
+        errorLine = errorLine.substring(0, _MAX_ERROR_LINE_LENGTH - 3) + "...";
+      } else {
+        assert(leftLength > _MAX_ERROR_LINE_LENGTH ~/ 2);
+        errorColumn -= errorLine.length - _MAX_ERROR_LINE_LENGTH;
+        errorLine = "..." + errorLine.substring(
+            errorLine.length - _MAX_ERROR_LINE_LENGTH + 3, errorLine.length);
+      }
+      errorLength = math.min(errorLength, _MAX_ERROR_LINE_LENGTH - errorColumn);
+    }
+    builder.writeln(errorLine);
+
+    for (var i = 0; i < errorColumn; i++) builder.write(" ");
+    for (var i = 0; i < errorLength; i++) builder.write("^");
+    builder.writeln();
+
+    return builder.toString();
+  }
+}
+
+// A content receiver that collects all the content into a string.
+class _ContentReceiver implements Source_ContentReceiver {
+  final _buffer = new StringBuffer();
+
+  String get result => _buffer.toString();
+
+  void accept1(CharBuffer contents, _) =>
+    _buffer.write(contents.subSequence(0, contents.length));
+
+  void accept2(String contents, _) => _buffer.write(contents);
+}
diff --git a/pkg/analyzer_experimental/lib/error_formatter.dart b/pkg/analyzer_experimental/lib/src/error_formatter.dart
similarity index 86%
rename from pkg/analyzer_experimental/lib/error_formatter.dart
rename to pkg/analyzer_experimental/lib/src/error_formatter.dart
index 9aafb9f..d36cab4 100644
--- a/pkg/analyzer_experimental/lib/error_formatter.dart
+++ b/pkg/analyzer_experimental/lib/src/error_formatter.dart
@@ -1,14 +1,31 @@
-part of analyzer;
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library error_formatter;
+
+import 'dart:async';
+import 'dart:io';
+
+import 'generated/java_io.dart';
+import 'generated/engine.dart';
+import 'generated/error.dart';
+import 'generated/source_io.dart';
+import 'generated/sdk.dart';
+import 'generated/sdk_io.dart';
+import 'generated/ast.dart';
+import 'generated/element.dart';
+import '../options.dart';
 
 /**
  * Helper for formatting [AnalysisError]s.
  * The two format options are a user consumable format and a machine consumable format.
  */
-class _ErrorFormatter {
+class ErrorFormatter {
   StringSink out;
   CommandLineOptions options;
 
-  _ErrorFormatter(this.out, this.options);
+  ErrorFormatter(this.out, this.options);
 
   void startAnalysis() {
     if (!options.machineFormat) {
diff --git a/pkg/analyzer_experimental/lib/src/utils.dart b/pkg/analyzer_experimental/lib/src/utils.dart
new file mode 100644
index 0000000..bb4a66f
--- /dev/null
+++ b/pkg/analyzer_experimental/lib/src/utils.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:io';
+import 'dart:uri';
+
+import 'package:pathos/path.dart' as pathos;
+
+/// Converts a local path string to a `file:` [Uri].
+Uri pathToFileUri(String pathString) {
+  pathString = pathos.absolute(pathString);
+  if (Platform.operatingSystem != 'windows') {
+    return Uri.parse('file://$pathString');
+  } else if (pathos.rootPrefix(pathString).startsWith('\\\\')) {
+    // Network paths become "file://hostname/path/to/file".
+    return Uri.parse('file:${pathString.replaceAll("\\", "/")}');
+  } else {
+    // Drive-letter paths become "file:///C:/path/to/file".
+    return Uri.parse('file:///${pathString.replaceAll("\\", "/")}');
+  }
+}
diff --git a/pkg/analyzer_experimental/pubspec.yaml b/pkg/analyzer_experimental/pubspec.yaml
index 616b361..848a362 100644
--- a/pkg/analyzer_experimental/pubspec.yaml
+++ b/pkg/analyzer_experimental/pubspec.yaml
@@ -4,5 +4,6 @@
 homepage: http://www.dartlang.org
 dependencies:
   args: any
+  pathos: any
 dev_dependencies:
   unittest: any
diff --git a/pkg/analyzer_experimental/test/error_test.dart b/pkg/analyzer_experimental/test/error_test.dart
new file mode 100644
index 0000000..4a513d9
--- /dev/null
+++ b/pkg/analyzer_experimental/test/error_test.dart
@@ -0,0 +1,62 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:unittest/unittest.dart';
+
+import 'utils.dart';
+
+void main() {
+  test("a valid Dart file doesn't throw any errors", () {
+    expect(
+        errorsForFile('void main() => print("Hello, world!");'),
+        isNull);
+  });
+
+  test("an error on the first line", () {
+    expect(errorsForFile('void foo;\n'),
+        equals('Error on line 1 of test.dart: ...\n'
+               'void foo;\n'
+               '^^^^\n'));
+  });
+
+  test("an error on the last line", () {
+    expect(errorsForFile('\nvoid foo;'),
+        equals('Error on line 2 of test.dart: ...\n'
+               'void foo;\n'
+               '^^^^\n'));
+  });
+
+  test("an error in the middle", () {
+    expect(errorsForFile('\nvoid foo;\n'),
+        equals('Error on line 2 of test.dart: ...\n'
+               'void foo;\n'
+               '^^^^\n'));
+  });
+
+  var veryLongString = new List.filled(107, ' ').join('');
+
+  test("an error at the end of a very long line", () {
+    expect(errorsForFile('$veryLongString     void foo;'),
+        equals('Error on line 1 of test.dart: ...\n'
+               '...$veryLongString void foo;\n'
+               '$veryLongString    ^^^^\n'));
+  });
+
+  test("an error at the beginning of a very long line", () {
+    expect(errorsForFile('void foo;     $veryLongString'),
+        equals('Error on line 1 of test.dart: ...\n'
+               'void foo; $veryLongString...\n'
+               '^^^^\n'));
+  });
+
+  test("an error in the middle of a very long line", () {
+    expect(errorsForFile('$veryLongString void foo;$veryLongString'),
+        equals('Error on line 1 of test.dart: ...\n'
+               '...                                                         '
+                   'void foo;                                                '
+                   '...\n'
+               '                                                            '
+                   '^^^^\n'));
+  });
+}
diff --git a/pkg/analyzer_experimental/test/utils.dart b/pkg/analyzer_experimental/test/utils.dart
new file mode 100644
index 0000000..92bf53e
--- /dev/null
+++ b/pkg/analyzer_experimental/test/utils.dart
@@ -0,0 +1,47 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library utils;
+
+import 'dart:io';
+
+import 'package:analyzer_experimental/analyzer.dart';
+import 'package:pathos/path.dart' as pathos;
+
+/// Returns the string representation of the [AnalyzerErrorGroup] thrown when
+/// parsing [contents] as a Dart file. If [contents] doesn't throw any errors,
+/// this will return null.
+///
+/// This replaces the filename in the error string with its basename, since the
+/// full path will vary from machine to machine. It also replaces the exception
+/// message with "..." to decouple these tests from the specific exception
+/// messages.
+String errorsForFile(String contents) {
+  return withTempDir((temp) {
+    var path = pathos.join(temp, 'test.dart');
+    new File(path).writeAsStringSync(contents);
+    try {
+      parseDartFile(path);
+    } on AnalyzerErrorGroup catch (e) {
+      return e.toString().replaceAllMapped(
+          new RegExp(r"^(Error on line \d+ of )((?:[A-Z]+:)?[^:]+): .*$",
+                     multiLine: true),
+          (match) => match[1] + pathos.basename(match[2]) + ': ...');
+    }
+    return null;
+  });
+}
+
+/// Creates a temporary directory and passes its path to [fn]. Once [fn]
+/// completes, the temporary directory and all its contents will be deleted.
+///
+/// Returns the return value of [fn].
+dynamic withTempDir(fn(String path)) {
+  var tempDir = new Directory('').createTempSync().path;
+  try {
+    return fn(tempDir);
+  } finally {
+    new Directory(tempDir).deleteSync(recursive: true);
+  }
+}
diff --git a/pkg/intl/lib/extract_messages.dart b/pkg/intl/lib/extract_messages.dart
index 47bcbbbeb..fc8bd09 100755
--- a/pkg/intl/lib/extract_messages.dart
+++ b/pkg/intl/lib/extract_messages.dart
@@ -21,14 +21,9 @@
  */
 library extract_messages;
 
-import 'package:analyzer_experimental/src/generated/ast.dart';
-import 'package:analyzer_experimental/src/generated/error.dart';
-import 'package:analyzer_experimental/src/generated/java_core.dart';
-import 'package:analyzer_experimental/src/generated/parser.dart';
-import 'package:analyzer_experimental/src/generated/scanner.dart';
-import 'package:analyzer_experimental/src/generated/source.dart';
-import 'package:analyzer_experimental/src/generated/utilities_dart.dart';
 import 'dart:io';
+
+import 'package:analyzer_experimental/analyzer.dart';
 import 'package:intl/src/intl_message.dart';
 
 /**
@@ -38,42 +33,14 @@
 bool suppressWarnings = false;
 
 /**
- * Parse the dart program represented in [sourceCode] and return a Map from
- * message names to [IntlMessage] instances. The [origin] is a string
- * describing where the source came from, and is used in error messages.
- */
-Map<String, IntlMessage> parseString(String sourceCode, [String origin]) {
-  var errorListener = new _ErrorCollector();
-  var scanner = new StringScanner(null, sourceCode, errorListener);
-  var token = scanner.tokenize();
-  var parser = new Parser(null, errorListener);
-  var unit = parser.parseCompilationUnit(token);
-  unit.lineInfo = new LineInfo(scanner.lineStarts);
-
-  var visitor = new MessageFindingVisitor(unit, origin);
-  unit.accept(visitor);
-  for (var error in errorListener.errors) {
-    print(error);
-  }
-  return visitor.messages;
-}
-
-/**
  * Parse the source of the Dart program file [file] and return a Map from
  * message names to [IntlMessage] instances.
  */
 Map<String, IntlMessage> parseFile(File file) {
-  var sourceCode = file.readAsStringSync();
-  return parseString(sourceCode, file.path);
-}
-
-/**
- * An error handler for parsing. Error handling is currently very primitive.
- */
-class _ErrorCollector extends AnalysisErrorListener {
-  List<AnalysisError> errors;
-  _ErrorCollector() : errors = new List<AnalysisError>();
-  onError(error) => errors.add(error);
+  var unit = parseDartFile(file.path);
+  var visitor = new MessageFindingVisitor(unit, file.path);
+  unit.accept(visitor);
+  return visitor.messages;
 }
 
 /**
diff --git a/pkg/mdv_observe/lib/mdv_observe.dart b/pkg/mdv_observe/lib/mdv_observe.dart
new file mode 100644
index 0000000..8864b36
--- /dev/null
+++ b/pkg/mdv_observe/lib/mdv_observe.dart
@@ -0,0 +1,104 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/**
+ * *Warning*: this library is experimental, and APIs are subject to change.
+ *
+ * This library is used to observe changes to [Observable] types. It also
+ * has helpers to implement [Observable] objects.
+ *
+ * For example:
+ *
+ *     class Monster extends Object with ObservableMixin {
+ *       static const _HEALTH = const Symbol('health');
+ *
+ *       int _health = 100;
+ *       get health => _health;
+ *       set health(value) {
+ *         _health = notifyChange(_HEALTH, _health, value);
+ *       }
+ *
+ *       void damage(int amount) {
+ *         print('$this takes $amount damage!');
+ *         health -= amount;
+ *       }
+ *
+ *       toString() => 'Monster with $health hit points';
+ *
+ *       // These methods are temporary until dart2js supports mirrors.
+ *       getValueWorkaround(key) {
+ *         if (key == _HEALTH) return health;
+ *         return null;
+ *       }
+ *       setValueWorkaround(key, val) {
+ *         if (key == _HEALTH) health = val;
+ *       }
+ *     }
+ *
+ *     main() {
+ *       var obj = new Monster();
+ *       obj.changes.listen((records) {
+ *         print('Changes to $obj were: $records');
+ *       });
+ *       // Schedules asynchronous delivery of these changes
+ *       obj.damage(10);
+ *       obj.damage(20);
+ *       print('done!');
+ *     }
+ */
+library mdv_observe;
+
+import 'dart:collection';
+
+// Import and reexport the observe implementation library. It contains the types
+// that are required to implement Model-Driven-Views in dart:html.
+// NOTE: always use package:mdv_observe (this package) in your code!
+// DO NOT import mdv_observe_impl; it may break unpredictably.
+import 'dart:mdv_observe_impl';
+export 'dart:mdv_observe_impl';
+
+part 'src/observable_box.dart';
+part 'src/observable_list.dart';
+part 'src/observable_map.dart';
+
+
+/**
+ * Converts the [Iterable] or [Map] to an [ObservableList] or [ObservableMap],
+ * respectively. This is a convenience function to make it easier to convert
+ * literals into the corresponding observable collection type.
+ *
+ * If [value] is not one of those collection types, or is already [Observable],
+ * it will be returned unmodified.
+ *
+ * If [value] is a [Map], the resulting value will use the appropriate kind of
+ * backing map: either [HashMap], [LinkedHashMap], or [SplayTreeMap].
+ *
+ * By default this performs a deep conversion, but you can set [deep] to false
+ * for a shallow conversion. This does not handle circular data structures.
+ */
+// TODO(jmesserly): ObservableSet?
+toObservable(value, {bool deep: true}) =>
+    deep ? _toObservableDeep(value) : _toObservableShallow(value);
+
+_toObservableShallow(value) {
+  if (value is Observable) return value;
+  if (value is Map) return new ObservableMap.from(value);
+  if (value is Iterable) return new ObservableList.from(value);
+  return value;
+}
+
+_toObservableDeep(value) {
+  if (value is Observable) return value;
+  if (value is Map) {
+    var result = new ObservableMap._createFromType(value);
+    value.forEach((k, v) {
+      result[_toObservableDeep(k)] = _toObservableDeep(v);
+    });
+    return result;
+  }
+  if (value is Iterable) {
+    return new ObservableList.from(value.map(_toObservableDeep));
+  }
+  return value;
+}
diff --git a/pkg/mdv_observe/lib/src/observable_box.dart b/pkg/mdv_observe/lib/src/observable_box.dart
new file mode 100644
index 0000000..484a900
--- /dev/null
+++ b/pkg/mdv_observe/lib/src/observable_box.dart
@@ -0,0 +1,37 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of mdv_observe;
+
+// TODO(jmesserly): should the property name be configurable?
+// That would be more convenient.
+/**
+ * An observable box that holds a value. Use this if you want to store a single
+ * value. For other cases, it is better to use [ObservableList],
+ * [ObservableMap], or a custom [Observable] implementation based on
+ * [ObservableMixin]. The property name for changes is "value".
+ */
+class ObservableBox<T> extends ObservableBase {
+  static const _VALUE = const Symbol('value');
+
+  T _value;
+
+  ObservableBox([T initialValue]) : _value = initialValue;
+
+  T get value => _value;
+
+  void set value(T newValue) {
+    _value = notifyPropertyChange(_VALUE, _value, newValue);
+  }
+
+  String toString() => '#<$runtimeType value: $value>';
+
+  getValueWorkaround(key) {
+    if (key == _VALUE) return value;
+    return null;
+  }
+  void setValueWorkaround(key, newValue) {
+    if (key == _VALUE) value = newValue;
+  }
+}
diff --git a/pkg/mdv_observe/lib/src/observable_list.dart b/pkg/mdv_observe/lib/src/observable_list.dart
new file mode 100644
index 0000000..c3ae880
--- /dev/null
+++ b/pkg/mdv_observe/lib/src/observable_list.dart
@@ -0,0 +1,288 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of mdv_observe;
+
+/**
+ * Represents an observable list of model values. If any items are added,
+ * removed, or replaced, then observers that are listening to [changes]
+ * will be notified.
+ */
+// TODO(jmesserly): remove implements List<E> once we can extend ListBase<E>
+class ObservableList<E> extends _ListBaseWorkaround with ObservableMixin
+    implements List<E> {
+  List<ListChangeRecord> _records;
+
+  static const _LENGTH = const Symbol('length');
+
+  /** The inner [List<E>] with the actual storage. */
+  final List<E> _list;
+
+  /**
+   * Creates an observable list of the given [length].
+   *
+   * If no [length] argument is supplied an extendable list of
+   * length 0 is created.
+   *
+   * If a [length] argument is supplied, a fixed size list of that
+   * length is created.
+   */
+  ObservableList([int length])
+      : _list = length != null ? new List<E>(length) : <E>[];
+
+  /**
+   * Creates an observable list with the elements of [other]. The order in
+   * the list will be the order provided by the iterator of [other].
+   */
+  factory ObservableList.from(Iterable<E> other) =>
+      new ObservableList<E>()..addAll(other);
+
+  // TODO(jmesserly): remove once we have mirrors
+  getValueWorkaround(key) => key == _LENGTH ? length : null;
+
+  setValueWorkaround(key, value) {
+    if (key == _LENGTH) length = value;
+  }
+
+  int get length => _list.length;
+
+  set length(int value) {
+    int len = _list.length;
+    if (len == value) return;
+
+    // Produce notifications if needed
+    if (hasObservers) {
+      if (value < len) {
+        // Remove items, then adjust length. Note the reverse order.
+        _recordChange(new ListChangeRecord(value, removedCount: len - value));
+      } else {
+        // Adjust length then add items
+        _recordChange(new ListChangeRecord(len, addedCount: value - len));
+      }
+    }
+
+    _list.length = value;
+  }
+
+  E operator [](int index) => _list[index];
+
+  void operator []=(int index, E value) {
+    var oldValue = _list[index];
+    if (hasObservers) {
+      _recordChange(new ListChangeRecord(index, addedCount: 1, removedCount: 1));
+    }
+    _list[index] = value;
+  }
+
+  // The following methods are here so that we can provide nice change events.
+
+  void setAll(int index, Iterable<E> iterable) {
+    if (iterable is! List && iterable is! Set) {
+      iterable = iterable.toList();
+    }
+    var len = iterable.length;
+    _list.setAll(index, iterable);
+    if (hasObservers && len > 0) {
+      _recordChange(
+          new ListChangeRecord(index, addedCount: len, removedCount: len));
+    }
+  }
+
+  void add(E value) {
+    int len = _list.length;
+    if (hasObservers) {
+      _recordChange(new ListChangeRecord(len, addedCount: 1));
+    }
+
+    _list.add(value);
+  }
+
+  void addAll(Iterable<E> iterable) {
+    int len = _list.length;
+    _list.addAll(iterable);
+    int added = _list.length - len;
+    if (hasObservers && added > 0) {
+      _recordChange(new ListChangeRecord(len, addedCount: added));
+    }
+  }
+
+  bool remove(Object element) {
+    for (int i = 0; i < this.length; i++) {
+      if (this[i] == element) {
+        removeRange(i, 1);
+        return true;
+      }
+    }
+    return false;
+  }
+
+  void removeRange(int start, int end) {
+    _rangeCheck(start, end);
+    int length = end - start;
+    _list.setRange(start, this.length - length, this, end);
+
+    int len = _list.length;
+    _list.length -= length;
+    if (hasObservers && length > 0) {
+      _recordChange(new ListChangeRecord(start, removedCount: length));
+    }
+  }
+
+  void insertAll(int index, Iterable<E> iterable) {
+    if (index < 0 || index > length) {
+      throw new RangeError.range(index, 0, length);
+    }
+    // TODO(floitsch): we can probably detect more cases.
+    if (iterable is! List && iterable is! Set) {
+      iterable = iterable.toList();
+    }
+    int insertionLength = iterable.length;
+    // There might be errors after the length change, in which case the list
+    // will end up being modified but the operation not complete. Unless we
+    // always go through a "toList" we can't really avoid that.
+    int len = _list.length;
+    _list.length += insertionLength;
+
+    _list.setRange(index + insertionLength, this.length, this, index);
+    _list.setAll(index, iterable);
+
+    if (hasObservers && insertionLength > 0) {
+      _recordChange(new ListChangeRecord(index, addedCount: insertionLength));
+    }
+  }
+
+  void insert(int index, E element) {
+    if (index < 0 || index > length) {
+      throw new RangeError.range(index, 0, length);
+    }
+    if (index == length) {
+      add(element);
+      return;
+    }
+    // We are modifying the length just below the is-check. Without the check
+    // Array.copy could throw an exception, leaving the list in a bad state
+    // (with a length that has been increased, but without a new element).
+    if (index is! int) throw new ArgumentError(index);
+    _list.length++;
+    _list.setRange(index + 1, length, this, index);
+    if (hasObservers) {
+      _recordChange(new ListChangeRecord(index, addedCount: 1));
+    }
+    _list[index] = element;
+  }
+
+
+  E removeAt(int index) {
+    E result = this[index];
+    removeRange(index, index + 1);
+    return result;
+  }
+
+  void _rangeCheck(int start, int end) {
+    if (start < 0 || start > this.length) {
+      throw new RangeError.range(start, 0, this.length);
+    }
+    if (end < start || end > this.length) {
+      throw new RangeError.range(end, start, this.length);
+    }
+  }
+
+  void _recordChange(ListChangeRecord record) {
+    if (_records == null) {
+      _records = [];
+      queueChangeRecords(_summarizeRecords);
+    }
+    _records.add(record);
+  }
+
+  /**
+   * We need to summarize change records. Consumers of these records want to
+   * apply the batch sequentially, and ensure that they can find inserted
+   * items by looking at that position in the list. This property does not
+   * hold in our record-as-you-go records. Consider:
+   *
+   *     var model = toObservable(['a', 'b']);
+   *     model.removeAt(1);
+   *     model.insertAll(0, ['c', 'd', 'e']);
+   *     model.removeRange(1, 3);
+   *     model.insert(1, 'f');
+   *
+   * Here, we inserted some records and then removed some of them.
+   * If someone processed these records naively, they would "play back" the
+   * insert incorrectly, because those items will be shifted.
+   *
+   * We summarize changes using a straightforward technique:
+   * Simulate the moves and use the final item positions to synthesize a
+   * new list of changes records. This has the advantage of not depending
+   * on the actual *values*, so we don't need to perform N^2 edit
+   */
+  // TODO(jmesserly): there's probably something smarter here, but this
+  // algorithm is pretty simple. It has complexity equivalent to the original
+  // list modifications.
+  // One simple idea: we can simply update the index map as we do the operations
+  // to the list, then produce the records at the end.
+  void _summarizeRecords() {
+    int oldLength = length;
+    for (var r in _records) {
+      oldLength += r.removedCount - r.addedCount;
+    }
+
+    if (length != oldLength) {
+      notifyPropertyChange(_LENGTH, oldLength, length);
+    }
+
+    if (_records.length == 1) {
+      notifyChange(_records[0]);
+      _records = null;
+      return;
+    }
+
+    var items = [];
+    for (int i = 0; i < oldLength; i++) items.add(i);
+    for (var r in _records) {
+      items.removeRange(r.index, r.index + r.removedCount);
+
+      // Represent inserts with -1.
+      items.insertAll(r.index, new List.filled(r.addedCount, -1));
+    }
+    assert(items.length == length);
+
+    _records = null;
+
+    int index = 0;
+    int offset = 0;
+    while (index < items.length) {
+      // Skip unchanged items.
+      while (index < items.length && items[index] == index + offset) {
+        index++;
+      }
+
+      // Find inserts
+      int startIndex = index;
+      while (index < items.length && items[index] == -1) {
+        index++;
+      }
+
+      int added = index - startIndex;
+
+      // Use the delta between our actual and expected position to determine
+      // how much was removed.
+      int actualItem = index < items.length ? items[index] : oldLength;
+      int expectedItem = startIndex + offset;
+
+      int removed = actualItem - expectedItem;
+
+      if (added > 0 || removed > 0) {
+        notifyChange(new ListChangeRecord(startIndex, addedCount: added,
+            removedCount: removed));
+      }
+
+      offset += removed - added;
+    }
+  }
+}
+
+// TODO(jmesserly): bogus type to workaround spurious VM bug with generic base
+// class and mixins.
+abstract class _ListBaseWorkaround extends ListBase<dynamic> {}
diff --git a/pkg/mdv_observe/lib/src/observable_map.dart b/pkg/mdv_observe/lib/src/observable_map.dart
new file mode 100644
index 0000000..009d692
--- /dev/null
+++ b/pkg/mdv_observe/lib/src/observable_map.dart
@@ -0,0 +1,149 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of mdv_observe;
+
+// TODO(jmesserly): this needs to be faster. We currently require multiple
+// lookups per key to get the old value.
+// TODO(jmesserly): this doesn't implement the precise interfaces like
+// LinkedHashMap, SplayTreeMap or HashMap. However it can use them for the
+// backing store.
+
+// TODO(jmesserly): should we summarize map changes like we do for list changes?
+class MapChangeRecord extends ChangeRecord {
+  /** The map key that changed. */
+  final key;
+
+  // TODO(jmesserly): we could store this more compactly if it matters.
+  /** True if this key was inserted. */
+  final bool isInsert;
+
+  /** True if this key was removed. */
+  final bool isRemove;
+
+  MapChangeRecord(this.key, {this.isInsert: false, this.isRemove: false}) {
+    if (isInsert && isRemove) {
+      throw new ArgumentError(
+          '$key cannot be inserted and removed in the same change');
+    }
+  }
+
+  // Use == on the key, to match equality semantics of most Maps.
+  bool changes(otherKey) => key == otherKey;
+
+  String toString() {
+    var kind = isInsert ? 'insert' : isRemove ? 'remove' : 'set';
+    return '#<MapChangeRecord $kind $key>';
+  }
+}
+
+/**
+ * Represents an observable map of model values. If any items are added,
+ * removed, or replaced, then observers that are listening to [changes]
+ * will be notified.
+ */
+class ObservableMap<K, V> extends ObservableBase implements Map<K, V> {
+  static const _LENGTH = const Symbol('length');
+
+  final Map<K, V> _map;
+
+  /** Creates an observable map. */
+  ObservableMap() : _map = new HashMap<K, V>();
+
+  /** Creates a new observable map using a [LinkedHashMap]. */
+  ObservableMap.linked() : _map = new LinkedHashMap<K, V>();
+
+  /** Creates a new observable map using a [SplayTreeMap]. */
+  ObservableMap.sorted() : _map = new SplayTreeMap<K, V>();
+
+  /**
+   * Creates an observable map that contains all key value pairs of [other].
+   * It will attempt to use the same backing map type if the other map is a
+   * [LinkedHashMap], [SplayTreeMap], or [HashMap]. Otherwise it defaults to
+   * [HashMap].
+   *
+   * Note this will perform a shallow conversion. If you want a deep conversion
+   * you should use [toObservable].
+   */
+  factory ObservableMap.from(Map<K, V> other) {
+    var result = new ObservableMap<K, V>._createFromType(other);
+    other.forEach((K key, V value) { result[key] = value; });
+    return result;
+  }
+
+  factory ObservableMap._createFromType(Map<K, V> other) {
+    ObservableMap result;
+    if (other is SplayTreeMap) {
+      result = new ObservableMap<K, V>.sorted();
+    } else if (other is LinkedHashMap) {
+      result = new ObservableMap<K, V>.linked();
+    } else {
+      result = new ObservableMap<K, V>();
+    }
+    return result;
+  }
+
+  Iterable<K> get keys => _map.keys;
+
+  Iterable<V> get values => _map.values;
+
+  int get length =>_map.length;
+
+  bool get isEmpty => length == 0;
+
+  bool containsValue(V value) => _map.containsValue(value);
+
+  bool containsKey(K key) => _map.containsKey(key);
+
+  V operator [](K key) => _map[key];
+
+  void operator []=(K key, V value) {
+    int len = _map.length;
+    V oldValue = _map[key];
+    _map[key] = value;
+    if (hasObservers) {
+      if (len != _map.length) {
+        notifyPropertyChange(_LENGTH, len, _map.length);
+        notifyChange(new MapChangeRecord(key, isInsert: true));
+      } else if (!identical(oldValue, value)) {
+        notifyChange(new MapChangeRecord(key));
+      }
+    }
+  }
+
+  V putIfAbsent(K key, V ifAbsent()) {
+    int len = _map.length;
+    V result = _map.putIfAbsent(key, ifAbsent);
+    if (hasObservers && len != _map.length) {
+      notifyPropertyChange(_LENGTH, len, _map.length);
+      notifyChange(new MapChangeRecord(key, isInsert: true));
+    }
+    return result;
+  }
+
+  V remove(K key) {
+    int len = _map.length;
+    V result =  _map.remove(key);
+    if (hasObservers && len != _map.length) {
+      notifyChange(new MapChangeRecord(key, isRemove: true));
+      notifyPropertyChange(_LENGTH, len, _map.length);
+    }
+    return result;
+  }
+
+  void clear() {
+    int len = _map.length;
+    if (hasObservers && len > 0) {
+      _map.forEach((key, value) {
+        notifyChange(new MapChangeRecord(key, isRemove: true));
+      });
+      notifyPropertyChange(_LENGTH, len, 0);
+    }
+    _map.clear();
+  }
+
+  void forEach(void f(K key, V value)) => _map.forEach(f);
+
+  String toString() => Maps.mapToString(this);
+}
diff --git a/pkg/mdv_observe/pubspec.yaml b/pkg/mdv_observe/pubspec.yaml
new file mode 100644
index 0000000..59da442
--- /dev/null
+++ b/pkg/mdv_observe/pubspec.yaml
@@ -0,0 +1,11 @@
+name: mdv_observe
+author: "Web UI Team <web-ui-dev@dartlang.org>"
+homepage: https://github.com/dart-lang/web-ui
+description: >
+  Observable properties and objects for use in Model-Driven-Views (MDV).
+  MDV extends HTML and the DOM APIs to support a sensible separation between the
+  UI (DOM) of a document or application and its underlying data (model).
+  Updates to the model are reflected in the DOM and user input into the DOM is
+  immediately assigned to the model.
+dev_dependencies:
+  unittest: any
diff --git a/pkg/mdv_observe/test/list_change_test.dart b/pkg/mdv_observe/test/list_change_test.dart
new file mode 100644
index 0000000..e7e1249
--- /dev/null
+++ b/pkg/mdv_observe/test/list_change_test.dart
@@ -0,0 +1,290 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:mdv_observe/mdv_observe.dart';
+import 'package:unittest/unittest.dart';
+import 'utils.dart';
+
+// This file contains code ported from:
+// https://github.com/rafaelw/ChangeSummary/blob/master/tests/test.js
+
+main() {
+  // TODO(jmesserly): rename this? Is summarizeListChanges coming back?
+  group('summarizeListChanges', listChangeTests);
+}
+
+// TODO(jmesserly): port or write array fuzzer tests
+listChangeTests() {
+
+  test('sequential adds', () {
+    var model = toObservable([]);
+    model.add(0);
+
+    var summary;
+    var sub = model.changes.listen((r) { summary = _filter(r); });
+
+    model.add(1);
+    model.add(2);
+
+    expect(summary, null);
+    deliverChangeRecords();
+
+    expectChanges(summary, [_delta(1, 0, 2)]);
+  });
+
+  test('List Splice Truncate And Expand With Length', () {
+    var model = toObservable(['a', 'b', 'c', 'd', 'e']);
+
+    var summary;
+    var sub = model.changes.listen((r) { summary = _filter(r); });
+
+    model.length = 2;
+
+    deliverChangeRecords();
+    expectChanges(summary, [_delta(2, 3, 0)]);
+    summary = null;
+
+    model.length = 5;
+
+    deliverChangeRecords();
+    expectChanges(summary, [_delta(2, 0, 3)]);
+  });
+
+  group('List deltas can be applied', () {
+
+    var summary = null;
+
+    observeArray(model) {
+      model.changes.listen((records) { summary = _filter(records); });
+    }
+
+    applyAndCheckDeltas(model, copy) {
+      summary = null;
+      deliverChangeRecords();
+
+      // apply deltas to the copy
+      for (var delta in summary) {
+        copy.removeRange(delta.index, delta.index + delta.removedCount);
+        for (int i = delta.addedCount - 1; i >= 0; i--) {
+          copy.insert(delta.index, model[delta.index + i]);
+        }
+      }
+
+      // Note: compare strings for easier debugging.
+      expect('$copy', '$model', reason: '!!! summary $summary');
+    }
+
+    test('Contained', () {
+      var model = toObservable(['a', 'b']);
+      var copy = model.toList();
+      observeArray(model);
+
+      model.removeAt(1);
+      model.insertAll(0, ['c', 'd', 'e']);
+      model.removeRange(1, 3);
+      model.insert(1, 'f');
+
+      applyAndCheckDeltas(model, copy);
+    });
+
+    test('Delete Empty', () {
+      var model = toObservable([1]);
+      var copy = model.toList();
+      observeArray(model);
+
+      model.removeAt(0);
+      model.insertAll(0, ['a', 'b', 'c']);
+
+      applyAndCheckDeltas(model, copy);
+    });
+
+    test('Right Non Overlap', () {
+      var model = toObservable(['a', 'b', 'c', 'd']);
+      var copy = model.toList();
+      observeArray(model);
+
+      model.removeRange(0, 1);
+      model.insert(0, 'e');
+      model.removeRange(2, 3);
+      model.insertAll(2, ['f', 'g']);
+
+      applyAndCheckDeltas(model, copy);
+    });
+
+    test('Left Non Overlap', () {
+      var model = toObservable(['a', 'b', 'c', 'd']);
+      var copy = model.toList();
+      observeArray(model);
+
+      model.removeRange(3, 4);
+      model.insertAll(3, ['f', 'g']);
+      model.removeRange(0, 1);
+      model.insert(0, 'e');
+
+      applyAndCheckDeltas(model, copy);
+    });
+
+    test('Right Adjacent', () {
+      var model = toObservable(['a', 'b', 'c', 'd']);
+      var copy = model.toList();
+      observeArray(model);
+
+      model.removeRange(1, 2);
+      model.insert(3, 'e');
+      model.removeRange(2, 3);
+      model.insertAll(0, ['f', 'g']);
+
+      applyAndCheckDeltas(model, copy);
+    });
+
+    test('Left Adjacent', () {
+      var model = toObservable(['a', 'b', 'c', 'd']);
+      var copy = model.toList();
+      observeArray(model);
+
+      model.removeRange(2, 4);
+      model.insert(2, 'e');
+
+      model.removeAt(1);
+      model.insertAll(1, ['f', 'g']);
+
+      applyAndCheckDeltas(model, copy);
+    });
+
+    test('Right Overlap', () {
+      var model = toObservable(['a', 'b', 'c', 'd']);
+      var copy = model.toList();
+      observeArray(model);
+
+      model.removeAt(1);
+      model.insert(1, 'e');
+      model.removeAt(1);
+      model.insertAll(1, ['f', 'g']);
+
+      applyAndCheckDeltas(model, copy);
+    });
+
+    test('Left Overlap', () {
+      var model = toObservable(['a', 'b', 'c', 'd']);
+      var copy = model.toList();
+      observeArray(model);
+
+      model.removeAt(2);
+      model.insertAll(2, ['e', 'f', 'g']);
+      // a b [e f g] d
+      model.removeRange(1, 3);
+      model.insertAll(1, ['h', 'i', 'j']);
+      // a [h i j] f g d
+
+      applyAndCheckDeltas(model, copy);
+    });
+
+    test('Prefix And Suffix One In', () {
+      var model = toObservable(['a', 'b', 'c', 'd']);
+      var copy = model.toList();
+      observeArray(model);
+
+      model.insert(0, 'z');
+      model.add('z');
+
+      applyAndCheckDeltas(model, copy);
+    });
+
+    test('Remove First', () {
+      var model = toObservable([16, 15, 15]);
+      var copy = model.toList();
+      observeArray(model);
+
+      model.removeAt(0);
+
+      applyAndCheckDeltas(model, copy);
+    });
+
+    test('Update Remove', () {
+      var model = toObservable(['a', 'b', 'c', 'd']);
+      var copy = model.toList();
+      observeArray(model);
+
+      model.removeAt(2);
+      model.insertAll(2, ['e', 'f', 'g']);  // a b [e f g] d
+      model[0] = 'h';
+      model.removeAt(1);
+
+      applyAndCheckDeltas(model, copy);
+    });
+
+    test('Remove Mid List', () {
+      var model = toObservable(['a', 'b', 'c', 'd']);
+      var copy = model.toList();
+      observeArray(model);
+
+      model.removeAt(2);
+
+      applyAndCheckDeltas(model, copy);
+    });
+  });
+
+  group('edit distance', () {
+    var summary = null;
+
+    observeArray(model) {
+      model.changes.listen((records) { summary = _filter(records); });
+    }
+
+    assertEditDistance(orig, expectDistance) {
+      summary = null;
+      deliverChangeRecords();
+      var actualDistance = 0;
+
+      if (summary != null) {
+        for (var delta in summary) {
+          actualDistance += delta.addedCount + delta.removedCount;
+        }
+      }
+
+      expect(actualDistance, expectDistance);
+    }
+
+    test('add items', () {
+      var model = toObservable([]);
+      observeArray(model);
+      model.addAll([1, 2, 3]);
+      assertEditDistance(model, 3);
+    });
+
+    test('trunacte and add, sharing a contiguous block', () {
+      var model = toObservable(['x', 'x', 'x', 'x', '1', '2', '3']);
+      observeArray(model);
+      model.length = 0;
+      model.addAll(['1', '2', '3', 'y', 'y', 'y', 'y']);
+      // Note: unlike the JS implementation, we don't perform a full diff.
+      // The change records are computed with no regards to the *contents* of
+      // the array. Thus, we get 14 instead of 8.
+      assertEditDistance(model, 14);
+    });
+
+    test('truncate and add, sharing a discontiguous block', () {
+      var model = toObservable(['1', '2', '3', '4', '5']);
+      observeArray(model);
+      model.length = 0;
+      model.addAll(['a', '2', 'y', 'y', '4', '5', 'z', 'z']);
+      // Note: unlike the JS implementation, we don't perform a full diff.
+      // The change records are computed with no regards to the *contents* of
+      // the array. Thus, we get 13 instead of 7.
+      assertEditDistance(model, 13);
+    });
+
+    test('insert at beginning and end', () {
+      var model = toObservable([2, 3, 4]);
+      observeArray(model);
+      model.insert(0, 5);
+      model[2] = 6;
+      model.add(7);
+      assertEditDistance(model, 4);
+    });
+  });
+}
+
+_delta(i, r, a) => new ListChangeRecord(i, removedCount: r, addedCount: a);
+_filter(records) => records.where((r) => r is ListChangeRecord).toList();
diff --git a/pkg/mdv_observe/test/observable_list_test.dart b/pkg/mdv_observe/test/observable_list_test.dart
new file mode 100644
index 0000000..3a4c945
--- /dev/null
+++ b/pkg/mdv_observe/test/observable_list_test.dart
@@ -0,0 +1,245 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:mdv_observe/mdv_observe.dart';
+import 'package:unittest/unittest.dart';
+import 'utils.dart';
+
+main() {
+  // TODO(jmesserly): need all standard List API tests.
+
+  const _LENGTH = const Symbol('length');
+
+  group('observe length', () {
+
+    ObservableList list;
+    List<ChangeRecord> changes;
+
+    setUp(() {
+      list = toObservable([1, 2, 3]);
+      changes = null;
+      list.changes.listen((records) {
+        changes = records.where((r) => r.changes(_LENGTH)).toList();
+      });
+    });
+
+    test('add changes length', () {
+      list.add(4);
+      expect(list, [1, 2, 3, 4]);
+      deliverChangeRecords();
+      expectChanges(changes, [_lengthChange]);
+    });
+
+    test('removeRange changes length', () {
+      list.add(4);
+      list.removeRange(1, 3);
+      expect(list, [1, 4]);
+      deliverChangeRecords();
+      expectChanges(changes, [_lengthChange]);
+    });
+
+    test('length= changes length', () {
+      list.length = 5;
+      expect(list, [1, 2, 3, null, null]);
+      deliverChangeRecords();
+      expectChanges(changes, [_lengthChange]);
+    });
+
+    test('[]= does not change length', () {
+      list[2] = 9000;
+      expect(list, [1, 2, 9000]);
+      deliverChangeRecords();
+      expectChanges(changes, []);
+    });
+
+    test('clear changes length', () {
+      list.clear();
+      expect(list, []);
+      deliverChangeRecords();
+      expectChanges(changes, [_lengthChange]);
+    });
+  });
+
+  group('observe index', () {
+    ObservableList list;
+    List<ChangeRecord> changes;
+
+    setUp(() {
+      list = toObservable([1, 2, 3]);
+      changes = null;
+      list.changes.listen((records) {
+        changes = records.where((r) => r.changes(1)).toList();
+      });
+    });
+
+    test('add does not change existing items', () {
+      list.add(4);
+      expect(list, [1, 2, 3, 4]);
+      deliverChangeRecords();
+      expectChanges(changes, []);
+    });
+
+    test('[]= changes item', () {
+      list[1] = 777;
+      expect(list, [1, 777, 3]);
+      deliverChangeRecords();
+      expectChanges(changes, [_change(1, addedCount: 1, removedCount: 1)]);
+    });
+
+    test('[]= on a different item does not fire change', () {
+      list[2] = 9000;
+      expect(list, [1, 2, 9000]);
+      deliverChangeRecords();
+      expectChanges(changes, []);
+    });
+
+    test('set multiple times results in one change', () {
+      list[1] = 777;
+      list[1] = 42;
+      expect(list, [1, 42, 3]);
+      deliverChangeRecords();
+      expectChanges(changes, [
+        _change(1, addedCount: 1, removedCount: 1),
+      ]);
+    });
+
+    test('set length without truncating item means no change', () {
+      list.length = 2;
+      expect(list, [1, 2]);
+      deliverChangeRecords();
+      expectChanges(changes, []);
+    });
+
+    test('truncate removes item', () {
+      list.length = 1;
+      expect(list, [1]);
+      deliverChangeRecords();
+      expectChanges(changes, [_change(1, removedCount: 2)]);
+    });
+
+    test('truncate and add new item', () {
+      list.length = 1;
+      list.add(42);
+      expect(list, [1, 42]);
+      deliverChangeRecords();
+      expectChanges(changes, [
+        _change(1, removedCount: 2, addedCount: 1)
+      ]);
+    });
+
+    test('truncate and add same item', () {
+      list.length = 1;
+      list.add(2);
+      expect(list, [1, 2]);
+      deliverChangeRecords();
+      expectChanges(changes, [
+        _change(1, removedCount: 2, addedCount: 1)
+      ]);
+    });
+  });
+
+  test('toString', () {
+    var list = toObservable([1, 2, 3]);
+    expect(list.toString(), '[1, 2, 3]');
+  });
+
+  group('change records', () {
+
+    List<ChangeRecord> records;
+    ObservableList list;
+
+    setUp(() {
+      list = toObservable([1, 2, 3, 1, 3, 4]);
+      records = null;
+      list.changes.listen((r) { records = r; });
+    });
+
+    test('read operations', () {
+      expect(list.length, 6);
+      expect(list[0], 1);
+      expect(list.indexOf(4), 5);
+      expect(list.indexOf(1), 0);
+      expect(list.indexOf(1, 1), 3);
+      expect(list.lastIndexOf(1), 3);
+      expect(list.last, 4);
+      var copy = new List<int>();
+      list.forEach((i) { copy.add(i); });
+      expect(copy, orderedEquals([1, 2, 3, 1, 3, 4]));
+      deliverChangeRecords();
+
+      // no change from read-only operators
+      expectChanges(records, null);
+    });
+
+    test('add', () {
+      list.add(5);
+      list.add(6);
+      expect(list, orderedEquals([1, 2, 3, 1, 3, 4, 5, 6]));
+
+      deliverChangeRecords();
+      expectChanges(records, [
+        _lengthChange,
+        _change(6, addedCount: 2)
+      ]);
+    });
+
+    test('[]=', () {
+      list[1] = list.last;
+      expect(list, orderedEquals([1, 4, 3, 1, 3, 4]));
+
+      deliverChangeRecords();
+      expectChanges(records, [ _change(1, addedCount: 1, removedCount: 1) ]);
+    });
+
+    test('removeLast', () {
+      expect(list.removeLast(), 4);
+      expect(list, orderedEquals([1, 2, 3, 1, 3]));
+
+      deliverChangeRecords();
+      expectChanges(records, [
+        _lengthChange,
+        _change(5, removedCount: 1)
+      ]);
+    });
+
+    test('removeRange', () {
+      list.removeRange(1, 4);
+      expect(list, orderedEquals([1, 3, 4]));
+
+      deliverChangeRecords();
+      expectChanges(records, [
+        _lengthChange,
+        _change(1, removedCount: 3),
+      ]);
+    });
+
+    test('sort', () {
+      list.sort((x, y) => x - y);
+      expect(list, orderedEquals([1, 1, 2, 3, 3, 4]));
+
+      deliverChangeRecords();
+      expectChanges(records, [
+        _change(1, addedCount: 5, removedCount: 5),
+      ]);
+    });
+
+    test('clear', () {
+      list.clear();
+      expect(list, []);
+
+      deliverChangeRecords();
+      expectChanges(records, [
+        _lengthChange,
+        _change(0, removedCount: 6)
+      ]);
+    });
+  });
+}
+
+const _LENGTH = const Symbol('length');
+
+final _lengthChange = new PropertyChangeRecord(_LENGTH);
+
+_change(index, {removedCount: 0, addedCount: 0}) => new ListChangeRecord(
+    index, removedCount: removedCount, addedCount: addedCount);
diff --git a/pkg/mdv_observe/test/observable_map_test.dart b/pkg/mdv_observe/test/observable_map_test.dart
new file mode 100644
index 0000000..155bfe1
--- /dev/null
+++ b/pkg/mdv_observe/test/observable_map_test.dart
@@ -0,0 +1,241 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:mdv_observe/mdv_observe.dart';
+import 'package:unittest/unittest.dart';
+import 'utils.dart';
+
+main() {
+  // TODO(jmesserly): need all standard Map API tests.
+
+  group('observe length', () {
+    ObservableMap map;
+    List<ChangeRecord> changes;
+
+    setUp(() {
+      map = toObservable({'a': 1, 'b': 2, 'c': 3});
+      changes = null;
+      map.changes.listen((records) {
+        changes = records.where((r) => r.changes(_LENGTH)).toList();
+      });
+    });
+
+    test('add item changes length', () {
+      map['d'] = 4;
+      expect(map, {'a': 1, 'b': 2, 'c': 3, 'd': 4});
+      deliverChangeRecords();
+      expectChanges(changes, [_lengthChange]);
+    });
+
+    test('putIfAbsent changes length', () {
+      map.putIfAbsent('d', () => 4);
+      expect(map, {'a': 1, 'b': 2, 'c': 3, 'd': 4});
+      deliverChangeRecords();
+      expectChanges(changes, [_lengthChange]);
+    });
+
+    test('remove changes length', () {
+      map.remove('c');
+      map.remove('a');
+      expect(map, {'b': 2});
+      deliverChangeRecords();
+      expectChanges(changes, [_lengthChange, _lengthChange]);
+    });
+
+    test('remove non-existent item does not change length', () {
+      map.remove('d');
+      expect(map, {'a': 1, 'b': 2, 'c': 3});
+      deliverChangeRecords();
+      expectChanges(changes, null);
+    });
+
+    test('set existing item does not change length', () {
+      map['c'] = 9000;
+      expect(map, {'a': 1, 'b': 2, 'c': 9000});
+      deliverChangeRecords();
+      expectChanges(changes, []);
+    });
+
+    test('clear changes length', () {
+      map.clear();
+      expect(map, {});
+      deliverChangeRecords();
+      expectChanges(changes, [_lengthChange]);
+    });
+  });
+
+  group('observe item', () {
+
+    ObservableMap map;
+    List<ChangeRecord> changes;
+
+    setUp(() {
+      map = toObservable({'a': 1, 'b': 2, 'c': 3});
+      changes = null;
+      map.changes.listen((records) {
+        changes = records.where((r) => r.changes('b')).toList();
+      });
+    });
+
+    test('putIfAbsent new item does not change existing item', () {
+      map.putIfAbsent('d', () => 4);
+      expect(map, {'a': 1, 'b': 2, 'c': 3, 'd': 4});
+      deliverChangeRecords();
+      expectChanges(changes, []);
+    });
+
+    test('set item to null', () {
+      map['b'] = null;
+      expect(map, {'a': 1, 'b': null, 'c': 3});
+      deliverChangeRecords();
+      expectChanges(changes, [_change('b')]);
+    });
+
+    test('set item to value', () {
+      map['b'] = 777;
+      expect(map, {'a': 1, 'b': 777, 'c': 3});
+      deliverChangeRecords();
+      expectChanges(changes, [_change('b')]);
+    });
+
+    test('putIfAbsent does not change if already there', () {
+      map.putIfAbsent('b', () => 1234);
+      expect(map, {'a': 1, 'b': 2, 'c': 3});
+      deliverChangeRecords();
+      expectChanges(changes, null);
+    });
+
+    test('change a different item', () {
+      map['c'] = 9000;
+      expect(map, {'a': 1, 'b': 2, 'c': 9000});
+      deliverChangeRecords();
+      expectChanges(changes, []);
+    });
+
+    test('change the item', () {
+      map['b'] = 9001;
+      map['b'] = 42;
+      expect(map, {'a': 1, 'b': 42, 'c': 3});
+      deliverChangeRecords();
+      expectChanges(changes, [_change('b'), _change('b')]);
+    });
+
+    test('remove other items', () {
+      map.remove('a');
+      expect(map, {'b': 2, 'c': 3});
+      deliverChangeRecords();
+      expectChanges(changes, []);
+    });
+
+    test('remove the item', () {
+      map.remove('b');
+      expect(map, {'a': 1, 'c': 3});
+      deliverChangeRecords();
+      expectChanges(changes, [_change('b', isRemove: true)]);
+    });
+
+    test('remove and add back', () {
+      map.remove('b');
+      map['b'] = 2;
+      expect(map, {'a': 1, 'b': 2, 'c': 3});
+      deliverChangeRecords();
+      expectChanges(changes,
+          [_change('b', isRemove: true), _change('b', isInsert: true)]);
+    });
+  });
+
+  test('toString', () {
+    var map = toObservable({'a': 1, 'b': 2});
+    expect(map.toString(), '{a: 1, b: 2}');
+  });
+
+  group('change records', () {
+    List<ChangeRecord> records;
+    ObservableMap map;
+
+    setUp(() {
+      map = toObservable({'a': 1, 'b': 2});
+      records = null;
+      map.changes.first.then((r) { records = r; });
+    });
+
+    test('read operations', () {
+      expect(map.length, 2);
+      expect(map.isEmpty, false);
+      expect(map['a'], 1);
+      expect(map.containsKey(2), false);
+      expect(map.containsValue(2), true);
+      expect(map.containsKey('b'), true);
+      expect(map.keys.toList(), ['a', 'b']);
+      expect(map.values.toList(), [1, 2]);
+      var copy = {};
+      map.forEach((k, v) { copy[k] = v; });
+      expect(copy, {'a': 1, 'b': 2});
+      deliverChangeRecords();
+
+      // no change from read-only operators
+      expect(records, null);
+    });
+
+    test('putIfAbsent', () {
+      map.putIfAbsent('a', () => 42);
+      expect(map, {'a': 1, 'b': 2});
+
+      map.putIfAbsent('c', () => 3);
+      expect(map, {'a': 1, 'b': 2, 'c': 3});
+
+      deliverChangeRecords();
+      expectChanges(records, [
+        _lengthChange,
+        _change('c', isInsert: true),
+      ]);
+    });
+
+    test('[]=', () {
+      map['a'] = 42;
+      expect(map, {'a': 42, 'b': 2});
+
+      map['c'] = 3;
+      expect(map, {'a': 42, 'b': 2, 'c': 3});
+
+      deliverChangeRecords();
+      expectChanges(records, [
+        _change('a'),
+        _lengthChange,
+        _change('c', isInsert: true)
+      ]);
+    });
+
+    test('remove', () {
+      map.remove('b');
+      expect(map, {'a': 1});
+
+      deliverChangeRecords();
+      expectChanges(records, [
+        _change('b', isRemove: true),
+        _lengthChange,
+      ]);
+    });
+
+    test('clear', () {
+      map.clear();
+      expect(map, {});
+
+      deliverChangeRecords();
+      expectChanges(records, [
+        _change('a', isRemove: true),
+        _change('b', isRemove: true),
+        _lengthChange,
+      ]);
+    });
+  });
+}
+
+
+const _LENGTH = const Symbol('length');
+
+final _lengthChange = new PropertyChangeRecord(_LENGTH);
+
+_change(key, {isInsert: false, isRemove: false}) =>
+    new MapChangeRecord(key, isInsert: isInsert, isRemove: isRemove);
diff --git a/pkg/mdv_observe/test/observe_test.dart b/pkg/mdv_observe/test/observe_test.dart
new file mode 100644
index 0000000..8d6e6d3b
--- /dev/null
+++ b/pkg/mdv_observe/test/observe_test.dart
@@ -0,0 +1,130 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'package:mdv_observe/mdv_observe.dart';
+import 'package:unittest/unittest.dart';
+import 'utils.dart';
+
+main() {
+  // Note: to test the basic Observable system, we use ObservableBox due to its
+  // simplicity.
+
+  const _VALUE = const Symbol('value');
+
+  group('ObservableBox', () {
+    test('no observers', () {
+      var t = new ObservableBox<int>(123);
+      expect(t.value, 123);
+      t.value = 42;
+      expect(t.value, 42);
+      expect(t.hasObservers, false);
+    });
+
+    test('listen adds an observer', () {
+      var t = new ObservableBox<int>(123);
+      expect(t.hasObservers, false);
+
+      t.changes.listen((n) {});
+      expect(t.hasObservers, true);
+    });
+
+    test('changes delived async', () {
+      var t = new ObservableBox<int>(123);
+      int called = 0;
+
+      t.changes.listen(expectAsync1((records) {
+        called++;
+        expectChanges(records, [_record(_VALUE), _record(_VALUE)]);
+      }));
+      t.value = 41;
+      t.value = 42;
+      expect(called, 0);
+    });
+
+    test('cause changes in handler', () {
+      var t = new ObservableBox<int>(123);
+      int called = 0;
+
+      t.changes.listen(expectAsync1((records) {
+        called++;
+        expectChanges(records, [_record(_VALUE)]);
+        if (called == 1) {
+          // Cause another change
+          t.value = 777;
+        }
+      }, count: 2));
+
+      t.value = 42;
+    });
+
+    test('multiple observers', () {
+      var t = new ObservableBox<int>(123);
+
+      verifyRecords(records) {
+        expectChanges(records, [_record(_VALUE), _record(_VALUE)]);
+      };
+
+      t.changes.listen(expectAsync1(verifyRecords));
+      t.changes.listen(expectAsync1(verifyRecords));
+
+      t.value = 41;
+      t.value = 42;
+    });
+
+    test('deliverChangeRecords', () {
+      var t = new ObservableBox<int>(123);
+      var records = [];
+      t.changes.listen((r) { records.addAll(r); });
+      t.value = 41;
+      t.value = 42;
+      expectChanges(records, [], reason: 'changes delived async');
+
+      deliverChangeRecords();
+      expectChanges(records,
+          [_record(_VALUE), _record(_VALUE)]);
+      records.clear();
+
+      t.value = 777;
+      expectChanges(records, [], reason: 'changes delived async');
+
+      deliverChangeRecords();
+      expectChanges(records, [_record(_VALUE)]);
+
+      // Has no effect if there are no changes
+      deliverChangeRecords();
+      expectChanges(records, [_record(_VALUE)]);
+    });
+
+    test('cancel listening', () {
+      var t = new ObservableBox<int>(123);
+      var sub;
+      sub = t.changes.listen(expectAsync1((records) {
+        expectChanges(records, [_record(_VALUE)]);
+        sub.cancel();
+        t.value = 777;
+      }));
+      t.value = 42;
+    });
+
+    test('cancel and reobserve', () {
+      var t = new ObservableBox<int>(123);
+      var sub;
+      sub = t.changes.listen(expectAsync1((records) {
+        expectChanges(records, [_record(_VALUE)]);
+        sub.cancel();
+
+        runAsync(expectAsync0(() {
+          sub = t.changes.listen(expectAsync1((records) {
+            expectChanges(records, [_record(_VALUE)]);
+          }));
+          t.value = 777;
+        }));
+      }));
+      t.value = 42;
+    });
+  });
+}
+
+_record(key) => new PropertyChangeRecord(key);
diff --git a/pkg/mdv_observe/test/utils.dart b/pkg/mdv_observe/test/utils.dart
new file mode 100644
index 0000000..5dd5e38
--- /dev/null
+++ b/pkg/mdv_observe/test/utils.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library mdv_observe.test.utils;
+
+import 'package:unittest/unittest.dart';
+
+// TODO(jmesserly): use matchers when this is supported. For now just
+// compare to toStrings.
+expectChanges(actual, expected, {reason}) =>
+    expect('$actual', '$expected', reason: reason);
diff --git a/pkg/pkg.status b/pkg/pkg.status
index c1abb38..6f9e10e 100644
--- a/pkg/pkg.status
+++ b/pkg/pkg.status
@@ -67,6 +67,7 @@
 analyzer_experimental/test/generated/parser_test: Skip # Imports dart:io.
 analyzer_experimental/test/generated/resolver_test: Skip # Imports dart:io.
 analyzer_experimental/test/generated/scanner_test: Skip # Imports dart:io.
+analyzer_experimental/test/error_test: Skip # Imports dart:io.
 
 # Issue 8440 forces us to use pathos in the scheduled_test tests, which would
 # otherwise be dart2js-compatible.
@@ -105,6 +106,7 @@
 analyzer_experimental/test/generated/parser_test: Skip
 analyzer_experimental/test/generated/resolver_test: Skip
 analyzer_experimental/test/generated/scanner_test: Skip
+analyzer_experimental/test/error_test: Skip # Imports dart:io.
 
 [ $compiler == dartc ]
 unittest/test/mock_regexp_negative_test: Fail
@@ -144,5 +146,6 @@
 [ $runtime == dartium || $runtime == drt ]
 serialization/test/no_library_test: Skip # Expected Failure
 
-[ $compiler == none && ($runtime == dartium || $runtime == drt) ]
-unittest/test/unittest_test: Timeout # http://dartbug.com/10470
+# Skip mdv_observe tests on command line VM, they only run in the browser.
+[ $runtime == vm ]
+mdv_observe: Skip
diff --git a/pkg/scheduled_test/lib/descriptor.dart b/pkg/scheduled_test/lib/descriptor.dart
index 1b2b988..1819247 100644
--- a/pkg/scheduled_test/lib/descriptor.dart
+++ b/pkg/scheduled_test/lib/descriptor.dart
@@ -100,6 +100,18 @@
 FileDescriptor binaryFile(String name, List<int> contents) =>
   new FileDescriptor.binary(name, contents);
 
+/// Creates a new text [FileDescriptor] with [name] that matches its String
+/// contents against [matcher]. If the file is created, it's considered to be
+/// empty.
+FileDescriptor matcherFile(String name, matcher) =>
+  new FileDescriptor.matcher(name, wrapMatcher(matcher));
+
+/// Creates a new binary [FileDescriptor] with [name] that matches its binary
+/// contents against [matcher]. If the file is created, it's considered to be
+/// empty.
+FileDescriptor binaryMatcherFile(String name, matcher) =>
+  new FileDescriptor.binaryMatcher(name, wrapMatcher(matcher));
+
 /// Creates a new [DirectoryDescriptor] descriptor with [name] and [contents].
 DirectoryDescriptor dir(String name, [Iterable<Descriptor> contents]) =>
    new DirectoryDescriptor(name, contents == null? <Descriptor>[] : contents);
diff --git a/pkg/scheduled_test/lib/src/descriptor/file_descriptor.dart b/pkg/scheduled_test/lib/src/descriptor/file_descriptor.dart
index f56f720..f6927e9 100644
--- a/pkg/scheduled_test/lib/src/descriptor/file_descriptor.dart
+++ b/pkg/scheduled_test/lib/src/descriptor/file_descriptor.dart
@@ -17,23 +17,34 @@
 
 /// A descriptor describing a single file.
 class FileDescriptor extends Descriptor {
-  /// Whether this descriptor describes a binary file. This is only used when
-  /// displaying error messages.
-  final bool isBinary;
-
   /// The contents of the file, in bytes.
   final List<int> contents;
 
   /// The contents of the file as a String. Assumes UTF-8 encoding.
   String get textContents => new String.fromCharCodes(contents);
 
-  FileDescriptor.binary(String name, List<int> contents)
-      : this._(name, contents, true);
+  /// Creates a new text [FileDescriptor] with [name] that matches its String
+  /// contents against [matcher]. If the file is created, it's considered to be
+  /// empty.
+  factory FileDescriptor.matcher(String name, Matcher matcher) =>
+    new _MatcherFileDescriptor(name, matcher, isBinary: false);
 
-  FileDescriptor(String name, String contents)
-      : this._(name, encodeUtf8(contents), false);
+  /// Creates a new binary [FileDescriptor] with [name] that matches its binary
+  /// contents against [matcher]. If the file is created, it's considered to be
+  /// empty.
+  factory FileDescriptor.binaryMatcher(String name, Matcher matcher) =>
+    new _MatcherFileDescriptor(name, matcher, isBinary: true);
 
-  FileDescriptor._(String name, this.contents, this.isBinary)
+  /// Creates a new binary [FileDescriptor] descriptor with [name] and
+  /// [contents].
+  factory FileDescriptor.binary(String name, List<int> contents) =>
+    new _BinaryFileDescriptor(name, contents);
+
+  /// Creates a new text [FileDescriptor] with [name] and [contents].
+  factory FileDescriptor(String name, String contents) =>
+    new _StringFileDescriptor(name, contents);
+
+  FileDescriptor._(String name, this.contents)
       : super(name);
 
   Future create([String parent]) => schedule(() {
@@ -51,21 +62,41 @@
       throw "File not found: '$fullPath'.";
     }
 
-    return new File(fullPath).readAsBytes()
-        .then((actualContents) {
-      if (orderedIterableEquals(contents, actualContents)) return;
-      if (isBinary) {
-        // TODO(nweiz): show a hex dump here if the data is small enough.
-        throw "File '$name' didn't contain the expected binary data.";
-      }
-      throw _textMismatchMessage(textContents,
-          new String.fromCharCodes(actualContents));
-    });
+    return new File(fullPath).readAsBytes().then(_validateNow);
   }
+  
+  // TODO(nweiz): rather than setting up an inheritance chain, just store a
+  // Matcher for validation. This would require better error messages from the
+  // matcher library, though.
+  /// A function that throws an error if [binaryContents] doesn't match the
+  /// expected contents of the descriptor.
+  void _validateNow(List<int> binaryContents);
 
   Stream<List<int>> read() => new Future.value(contents).asStream();
 
   String describe() => name;
+}
+
+class _BinaryFileDescriptor extends FileDescriptor {
+  _BinaryFileDescriptor(String name, List<int> contents)
+      : super._(name, contents);
+
+  Future _validateNow(List<int> actualContents) {
+    if (orderedIterableEquals(contents, actualContents)) return;
+    // TODO(nweiz): show a hex dump here if the data is small enough.
+    throw "File '$name' didn't contain the expected binary data.";
+  }
+}
+
+class _StringFileDescriptor extends FileDescriptor {
+  _StringFileDescriptor(String name, String contents)
+      : super._(name, encodeUtf8(contents));
+
+  Future _validateNow(List<int> actualContents) {
+    if (orderedIterableEquals(contents, actualContents)) return;
+    throw _textMismatchMessage(textContents,
+        new String.fromCharCodes(actualContents));
+  }
 
   String _textMismatchMessage(String expected, String actual) {
     final expectedLines = expected.split('\n');
@@ -102,3 +133,17 @@
         "${results.join('\n')}";
   }
 }
+
+class _MatcherFileDescriptor extends FileDescriptor {
+  final Matcher _matcher;
+  final bool _isBinary;
+
+  _MatcherFileDescriptor(String name, this._matcher, {bool isBinary})
+      : _isBinary = isBinary == true ? true : false,
+        super._(name, <int>[]);
+
+  void _validateNow(List<int> actualContents) =>
+      expect(
+          _isBinary ? actualContents : new String.fromCharCodes(actualContents),
+          _matcher);
+}
diff --git a/pkg/scheduled_test/test/descriptor/file_test.dart b/pkg/scheduled_test/test/descriptor/file_test.dart
index 877c02a..5b64577 100644
--- a/pkg/scheduled_test/test/descriptor/file_test.dart
+++ b/pkg/scheduled_test/test/descriptor/file_test.dart
@@ -180,4 +180,98 @@
       ]), verbose: true);
     });
   }, passing: ['test 2']);
-}
\ No newline at end of file
+
+  expectTestsPass('matcherFile().create() creates an empty file', () {
+    test('test', () {
+      scheduleSandbox();
+
+      d.matcherFile('name.txt', isNot(isEmpty)).create();
+
+      schedule(() {
+        expect(new File(path.join(sandbox, 'name.txt')).readAsString(),
+            completion(equals('')));
+      });
+    });
+  });
+
+  expectTestsPass('matcherFile().validate() completes successfully if the '
+      'string contents of the file matches the matcher', () {
+    test('test', () {
+      scheduleSandbox();
+
+      schedule(() {
+        return new File(path.join(sandbox, 'name.txt'))
+            .writeAsString('barfoobaz');
+      });
+
+      d.matcherFile('name.txt', contains('foo')).validate();
+    });
+  });
+
+  expectTestsPass("matcherFile().validate() fails if the string contents of "
+      "the file doesn't match the matcher", () {
+    var errors;
+    test('test 1', () {
+      scheduleSandbox();
+
+      currentSchedule.onException.schedule(() {
+        errors = currentSchedule.errors;
+      });
+
+      schedule(() {
+        return new File(path.join(sandbox, 'name.txt'))
+            .writeAsString('barfoobaz');
+      });
+
+      d.matcherFile('name.txt', contains('baaz')).validate();
+    });
+
+    test('test 2', () {
+      expect(errors, everyElement(new isInstanceOf<ScheduleError>()));
+      expect(errors.map((e) => e.error.message), equals([
+        "Expected: contains 'baaz'\n     but: was 'barfoobaz'.\n"
+      ]), verbose: true);
+    });
+  }, passing: ['test 2']);
+
+  expectTestsPass('binaryMatcherFile().validate() completes successfully if '
+      'the string contents of the file matches the matcher', () {
+    test('test', () {
+      scheduleSandbox();
+
+      schedule(() {
+        return new File(path.join(sandbox, 'name.txt'))
+            .writeAsString('barfoobaz');
+      });
+
+      d.binaryMatcherFile('name.txt', contains(111)).validate();
+    });
+  });
+
+  expectTestsPass("binaryMatcherFile().validate() fails if the string contents "
+      "of the file doesn't match the matcher", () {
+    var errors;
+    test('test 1', () {
+      scheduleSandbox();
+
+      currentSchedule.onException.schedule(() {
+        errors = currentSchedule.errors;
+      });
+
+      schedule(() {
+        return new File(path.join(sandbox, 'name.txt'))
+            .writeAsString('barfoobaz');
+      });
+
+      d.binaryMatcherFile('name.txt', contains(12)).validate();
+    });
+
+    test('test 2', () {
+      expect(errors, everyElement(new isInstanceOf<ScheduleError>()));
+      expect(errors.map((e) => e.error.message), equals([
+        "Expected: contains <12>\n"
+        "     but: was <[98, 97, 114, 102, 111, 111, 98, 97, 122]>.\n"
+      ]), verbose: true);
+    });
+  }, passing: ['test 2']);
+}
diff --git a/pkg/serialization/lib/serialization.dart b/pkg/serialization/lib/serialization.dart
index 88c30e8..c0ab0d0 100644
--- a/pkg/serialization/lib/serialization.dart
+++ b/pkg/serialization/lib/serialization.dart
@@ -227,13 +227,7 @@
    * The serialization is controlled by the list of Serialization rules. These
    * are most commonly added via [addRuleFor].
    */
-  List _rules = [];
-
-  /**
-   * The serialization is controlled by the list of Serialization rules. These
-   * are most commonly added via [addRuleFor].
-   */
-  List get rules => _rules;
+  final List<SerializationRule> rules = new List<SerializationRule>();
 
   /**
    * When reading, we may need to resolve references to existing objects in
@@ -264,7 +258,7 @@
     // TODO(alanknight): Should this be moved to the format?
     // TODO(alanknight): Allow self-describing in the presence of CustomRule.
     if (_selfDescribing != null) return _selfDescribing;
-    return !_rules.any((x) => x is CustomRule);
+    return !rules.any((x) => x is CustomRule);
   }
 
   /**
@@ -273,7 +267,9 @@
    * CustomRule subclasses, in which case it requires additional setup and
    * is off by default.
    */
-  set selfDescribing(x) => _selfDescribing = x;
+  void set selfDescribing(bool value) {
+    _selfDescribing = value;
+  }
 
   /**
    * Creates a new serialization with a default set of rules for primitives
@@ -350,8 +346,8 @@
    * method.
    */
   void addRule(SerializationRule rule) {
-    rule.number = _rules.length;
-    _rules.add(rule);
+    rule.number = rules.length;
+    rules.add(rule);
   }
 
   /**
@@ -416,10 +412,10 @@
     var target, candidateRules;
     if (object is DesignatedRuleForObject) {
       target = object.target;
-      candidateRules = object.possibleRules(_rules);
+      candidateRules = object.possibleRules(rules);
     } else {
       target = object;
-      candidateRules = _rules;
+      candidateRules = rules;
     }
     Iterable applicable = candidateRules.where(
         (each) => each.appliesTo(target, w));
diff --git a/pkg/serialization/lib/src/basic_rule.dart b/pkg/serialization/lib/src/basic_rule.dart
index dbf54ed..927f316 100644
--- a/pkg/serialization/lib/src/basic_rule.dart
+++ b/pkg/serialization/lib/src/basic_rule.dart
@@ -28,7 +28,7 @@
   Constructor constructor;
 
   /** This holds onto our list of fields, and can also calculate them. */
-  _FieldList fields;
+  _FieldList _fields;
 
   /**
    * Instances can either use maps or lists to hold the object's state. The list
@@ -63,7 +63,7 @@
       List excludeFields) {
     _findFields(constructorFields, regularFields, excludeFields);
     constructor = new Constructor(
-        type, constructorName, fields.constructorFieldIndices());
+        type, constructorName, _fields.constructorFieldIndices());
     configureForLists();
   }
 
@@ -92,9 +92,9 @@
    * Note that the function is passed the owning object as well as the field
    * value, but that it is passed as a mirror.
    */
-  setFieldWith(String fieldName, SetWithFunction setWith) {
-    fields.addAllByName([fieldName]);
-    _NamedField field = fields.named(_asSymbol(fieldName));
+  void setFieldWith(String fieldName, SetWithFunction setWith) {
+    _fields.addAllByName([fieldName]);
+    _NamedField field = _fields.named(_asSymbol(fieldName));
     Function setter = (setWith == null) ? field.defaultSetter : setWith;
     field.customSetter = setter;
   }
@@ -103,10 +103,10 @@
   String get constructorName => constructor.name;
 
   /** Return the list of field names to be passed to the constructor.*/
-  List<String> get constructorFields => fields.constructorFieldNames();
+  List<String> get constructorFields => _fields.constructorFieldNames();
 
   /** Return the list of field names not used in the constructor. */
-  List<String> get regularFields => fields.regularFieldNames();
+  List<String> get regularFields => _fields.regularFieldNames();
 
   String toString() => "Basic Rule for ${type.simpleName}";
 
@@ -116,7 +116,7 @@
    * is much more compact and used by default. The map representation is
    * much easier to debug. The default is to use lists.
    */
-  configureForMaps() {
+  void configureForMaps() {
     useMaps = true;
   }
 
@@ -126,7 +126,7 @@
    * is much more compact and used by default. The map representation is
    * much easier to debug. The default is to use lists.
    */
-  configureForLists() {
+  void configureForLists() {
     useMaps = false;
   }
 
@@ -138,9 +138,9 @@
    * If a list is returned, it is growable.
    */
    createStateHolder() {
-     if (useMaps) return new _MapWrapper(fields.contents);
+     if (useMaps) return new _MapWrapper(_fields.contents);
      List list = [];
-     list.length = fields.length;
+     list.length = _fields.length;
      return list;
    }
 
@@ -161,18 +161,18 @@
        var newKey = (each is Reference) ? each.inflated() : each;
        newState[newKey] = state[each];
      }
-     return new _MapWrapper.fromMap(newState, fields.contents);
+     return new _MapWrapper.fromMap(newState, _fields.contents);
    }
 
   /**
    * Extract the state from [object] using an instanceMirror and the field
-   * names in [fields]. Call the function [callback] on each value.
+   * names in [_fields]. Call the function [callback] on each value.
    */
   extractState(object, Function callback, Writer w) {
     var result = createStateHolder();
     var mirror = reflect(object);
 
-    keysAndValues(fields).forEach(
+    keysAndValues(_fields).forEach(
         (index, field) {
           var value = _value(mirror, field);
           callback(field.name);
@@ -203,7 +203,7 @@
    * to designating the rule, since we so far only have one rule per object.
    */
   checkForEssentialLists(index, value) {
-    if (value is List && fields.contents[index].isEssential) {
+    if (value is List && _fields.contents[index].isEssential) {
       return new DesignatedRuleForObject(value,
           (SerializationRule rule) => rule is ListRuleEssential);
     } else {
@@ -230,7 +230,7 @@
   inflateNonEssential(rawState, object, Reader reader) {
     InstanceMirror mirror = reflect(object);
     var state = makeIndexableByNumber(rawState);
-    fields.forEachRegularField( (_Field field) {
+    _fields.forEachRegularField( (_Field field) {
       var value = reader.inflateReference(state[field.index]);
       field.setValue(mirror, value);
     });
@@ -249,17 +249,17 @@
    */
   void _findFields(List constructorFields, List regularFields,
       List excludeFields) {
-    fields = new _FieldList(type);
-    fields.constructorFields = constructorFields;
-    fields.regular = regularFields;
+    _fields = new _FieldList(type);
+    _fields.constructorFields = constructorFields;
+    _fields.regular = regularFields;
     // TODO(alanknight): The order of this matters. It shouldn't.
-    fields.exclude = excludeFields;
-    fields.figureOutFields();
+    _fields.exclude = excludeFields;
+    _fields.figureOutFields();
   }
 
   bool get hasVariableLengthEntries => false;
 
-  int get dataLength => fields.length;
+  int get dataLength => _fields.length;
 
   /**
    * Extract the value of [field] from the object reflected
@@ -342,7 +342,7 @@
   // Because [x] may not be a named field, we compare the toString. We don't
   // care that much where constants come in the sort order as long as it's
   // consistent.
-  compareTo(_Field x) => toString().compareTo(x.toString());
+  int compareTo(_Field x) => toString().compareTo(x.toString());
 }
 
 /**
@@ -430,7 +430,7 @@
  * are kept in a separate object, which also has the ability to compute the
  * default fields to use reflectively.
  */
-class _FieldList extends IterableBase {
+class _FieldList extends IterableBase<_Field> {
   /**
    * All of our fields, indexed by name. Note that the names are
    * typically Symbols, but can also be arbitrary constants.
@@ -523,7 +523,7 @@
     contents;
   }
 
-  Iterator get iterator => contents.iterator;
+  Iterator<_Field> get iterator => contents.iterator;
 
   /** Return a cached, sorted list of all the fields. */
   List<_Field> get contents {
@@ -650,8 +650,8 @@
  * from the index into a field name and then looks it up in the map.
  */
 class _MapWrapper {
-  final _map;
-  List fieldList;
+  final Map _map;
+  final List fieldList;
   _MapWrapper(this.fieldList) : _map = new Map();
   _MapWrapper.fromMap(this._map, this.fieldList);
 
@@ -660,7 +660,7 @@
   operator []=(key, value) { _map[fieldList[key].name] = value; }
   get length => _map.length;
 
-  asMap() => _map;
+  Map asMap() => _map;
 }
 
 /**
@@ -668,7 +668,7 @@
  * Symbol. If it is any other type, or if the string is an
  * invalid symbol, return null;
  */
-_asSymbol(value) {
+Symbol _asSymbol(value) {
   if (value is Symbol) return value;
   if (value is String) {
     try {
diff --git a/pkg/serialization/lib/src/format.dart b/pkg/serialization/lib/src/format.dart
index d42707d..e45a7f0 100644
--- a/pkg/serialization/lib/src/format.dart
+++ b/pkg/serialization/lib/src/format.dart
@@ -5,6 +5,9 @@
  * is read or written to a particular output mechanism.
  */
 abstract class Format {
+
+  const Format();
+
   /**
    * Return true if this format stores primitives in their own area and uses
    * references to them (e.g. [SimpleFlatFormat]) and false if primitives
@@ -40,6 +43,8 @@
  */
 class SimpleMapFormat extends Format {
 
+  const SimpleMapFormat();
+
   /**
    * Generate output for this format from [w] and return it as a String which
    * is the [json] representation of a nested Map structure. The top level has
@@ -98,12 +103,12 @@
   /**
    * If we store the rule numbers, what key should we use to store them.
    */
-  static final String RULE = "_rule";
-  static final String RULES = "_rules";
-  static final String DATA = "_data";
-  static final String ROOTS = "_root";
+  static const String RULE = "_rule";
+  static const String RULES = "_rules";
+  static const String DATA = "_data";
+  static const String ROOTS = "_root";
 
-  SimpleJsonFormat({this.storeRoundTripInfo : false});
+  const SimpleJsonFormat({this.storeRoundTripInfo : false});
 
   /**
    * Generate output for this format from [w] and return it as
@@ -237,9 +242,11 @@
    * For each rule we store data to indicate whether it will be reconstructed
    * as a primitive, a list or a map.
    */
-  static final int STORED_AS_LIST = 1;
-  static final int STORED_AS_MAP = 2;
-  static final int STORED_AS_PRIMITIVE = 3;
+  static const int STORED_AS_LIST = 1;
+  static const int STORED_AS_MAP = 2;
+  static const int STORED_AS_PRIMITIVE = 3;
+
+  const SimpleFlatFormat();
 
   /**
    * Generate output for this format from [w]. This will return a List with
@@ -290,7 +297,7 @@
    * right length when we read it back. We expect everything in the list to be
    * a reference, which is stored as two numbers.
    */
-  writeLists(SerializationRule rule, List<List> entries, List target) {
+  void writeLists(SerializationRule rule, List<List> entries, List target) {
     target.add(STORED_AS_LIST);
     for (var eachEntry in entries) {
       if (rule.hasVariableLengthEntries) {
@@ -310,7 +317,7 @@
    * values. We expect the values to be references, which we store as
    * two numbers.
    */
-  writeMaps(SerializationRule rule, List<Map> entries, List target) {
+  void writeMaps(SerializationRule rule, List<Map> entries, List target) {
     target.add(STORED_AS_MAP);
     for (var eachEntry in entries) {
       if (rule.hasVariableLengthEntries) {
@@ -327,7 +334,7 @@
    * Write [entries], which contains simple objects which we can put directly
    * into [target].
    */
-  writeObjects(List entries, List target) {
+  void writeObjects(List entries, List target) {
     target.add(STORED_AS_PRIMITIVE);
     for (var each in entries) {
       if (!isPrimitive(each)) throw new SerializationException("Invalid data");
@@ -453,7 +460,7 @@
   }
 
   /** Read the next Reference from the input. */
-  nextReferenceFrom(Iterator input, Reader r) {
+  Reference nextReferenceFrom(Iterator input, Reader r) {
     var a = _next(input);
     var b = _next(input);
     if (a == null) {
diff --git a/pkg/serialization/lib/src/reader_writer.dart b/pkg/serialization/lib/src/reader_writer.dart
index e25a3ec..38ff10a 100644
--- a/pkg/serialization/lib/src/reader_writer.dart
+++ b/pkg/serialization/lib/src/reader_writer.dart
@@ -31,7 +31,7 @@
    */
   bool selfDescribing;
 
-  Format format = new SimpleMapFormat();
+  final Format format;
 
   /**
    * Objects that cannot be represented in-place in the serialized form need
@@ -53,7 +53,7 @@
   final List<List> states = new List<List>();
 
   /** Return the list of rules we use. */
-  List<SerializationRule> get rules => serialization._rules;
+  List<SerializationRule> get rules => serialization.rules;
 
   /**
    * Creates a new [Writer] that uses the rules from its parent
@@ -61,10 +61,10 @@
    * related to a particular read/write, so the same one can be used
    * for multiple different Readers/Writers.
    */
-  Writer(this.serialization, [Format newFormat]) {
+  Writer(this.serialization, [Format newFormat]) :
+    format = (newFormat == null) ? const SimpleMapFormat() : newFormat {
     trace = new Trace(this);
     selfDescribing = serialization.selfDescribing;
-    if (newFormat != null) format = newFormat;
   }
 
   /**
@@ -145,7 +145,7 @@
     var meta = serialization.ruleSerialization();
     var writer = new Writer(meta, format);
     writer.selfDescribing = false;
-    return writer.write(serialization._rules);
+    return writer.write(serialization.rules);
   }
 
   /** Record a [state] entry for a particular rule. */
@@ -197,7 +197,7 @@
    * Return a list of [Reference] objects pointing to our roots. This will be
    * stored in the output under "roots" in the default format.
    */
-  _rootReferences() => trace.roots.map(_referenceFor).toList();
+  List _rootReferences() => trace.roots.map(_referenceFor).toList();
 
   /**
    * Given an object, return a reference for it if one exists. If there's
@@ -217,13 +217,13 @@
   // TODO(alanknight): Should the writer also have its own namedObjects
   // collection specific to the particular write, or is that just adding
   // complexity for little value?
-  hasNameFor(object) => serialization._hasNameFor(object);
+  bool hasNameFor(object) => serialization._hasNameFor(object);
 
   /**
    * Return the name we have for this object in the [Serialization.namedObjects]
    * collection.
    */
-  nameFor(object) => serialization._nameFor(object);
+  String nameFor(object) => serialization._nameFor(object);
 
   // For debugging/testing purposes. Find what state a reference points to.
   stateForReference(Reference r) => states[r.ruleNumber][r.objectNumber];
@@ -285,7 +285,7 @@
    */
   List<List> objects;
 
-  Format format = new SimpleMapFormat();
+  final Format format;
 
   /**
    * Creates a new [Reader] that uses the rules from its parent
@@ -293,9 +293,9 @@
    * a particular read or write operation, so the same one can be used
    * for multiple different Writers/Readers.
    */
-  Reader(this.serialization, [Format newFormat]) {
+  Reader(this.serialization, [Format newFormat]) :
+    format = (newFormat == null) ? const SimpleMapFormat() : newFormat  {
     selfDescribing = serialization.selfDescribing;
-    if (newFormat != null) format = newFormat;
   }
 
   /**
@@ -329,7 +329,7 @@
    * Return the list of rules to be used when writing. These come from the
    * [serialization].
    */
-  List<SerializationRule> get rules => serialization._rules;
+  List<SerializationRule> get rules => serialization.rules;
 
   /**
    * Internal use only, for testing purposes. Set the data for this reader
@@ -374,7 +374,7 @@
    * non-essential state, because all the objects will have already been
    * created.
    */
-  inflateForRule(rule) {
+  void inflateForRule(rule) {
     var dataForThisRule = _data[rule.number];
     keysAndValues(dataForThisRule).forEach((position, state) {
       inflateOne(rule, position, state);
@@ -442,7 +442,7 @@
 
   /** Given a reference, return the rule it references. */
   SerializationRule ruleFor(Reference reference) =>
-      serialization._rules[reference.ruleNumber];
+      serialization.rules[reference.ruleNumber];
 
   /**
    * Return the primitive rule we are using. This is an ugly mechanism to
@@ -505,7 +505,7 @@
   final Queue queue = new Queue();
 
   /** The root objects from which we will be tracing. */
-  List roots = [];
+  final List roots = [];
 
   Trace(this.writer);
 
@@ -585,19 +585,19 @@
    */
   // TODO(alanknight): This is a hack both in defining a toJson specific to a
   // particular representation, and the use of a bogus sentinel "__Ref"
-  toJson() => {
+  Map<String, dynamic> toJson() => {
     "__Ref" : true,
     "rule" : ruleNumber,
     "object" : objectNumber
   };
 
   /** Write our information to [list]. Useful in writing to flat formats.*/
-  writeToList(List list) {
+  void writeToList(List list) {
     list.add(ruleNumber);
     list.add(objectNumber);
   }
 
-  toString() => "Reference($ruleNumber, $objectNumber)";
+  String toString() => "Reference($ruleNumber, $objectNumber)";
 }
 
 /**
@@ -608,10 +608,10 @@
  * for an example. It knows how to return its object and how to filter.
  */
 class DesignatedRuleForObject {
-  Function rulePredicate;
+  final Function rulePredicate;
   final target;
 
   DesignatedRuleForObject(this.target, this.rulePredicate);
 
-  possibleRules(List rules) => rules.where(rulePredicate).toList();
+  List possibleRules(List rules) => rules.where(rulePredicate).toList();
 }
diff --git a/pkg/serialization/lib/src/serialization_rule.dart b/pkg/serialization/lib/src/serialization_rule.dart
index d7560b7..1a592a7 100644
--- a/pkg/serialization/lib/src/serialization_rule.dart
+++ b/pkg/serialization/lib/src/serialization_rule.dart
@@ -27,10 +27,10 @@
    * Rules belong uniquely to a particular Serialization instance, and can
    * be identified within it by number.
    */
-  void set number(x) {
-    if (_number != null && _number != x) throw
+  void set number(int value) {
+    if (_number != null && _number != value) throw
         new SerializationException("Rule numbers cannot be changed, once set");
-    _number = x;
+    _number = value;
   }
 
   /**
@@ -91,7 +91,7 @@
    * to see how bad it is.
    */
   // TODO(alanknight): Reconsider whether this should be handled differently.
-  get mustBePrimary => false;
+  bool get mustBePrimary => false;
 
   /**
    * Create the new object corresponding to [state] using the rules
diff --git a/pkg/serialization/test/serialization_test.dart b/pkg/serialization/test/serialization_test.dart
index 47c8a6ae..db5b5f1 100644
--- a/pkg/serialization/test/serialization_test.dart
+++ b/pkg/serialization/test/serialization_test.dart
@@ -18,8 +18,8 @@
   a1.street = 'N 34th';
   a1.city = 'Seattle';
 
-  var formats = [new SimpleFlatFormat(), new SimpleMapFormat(),
-                 new SimpleJsonFormat(storeRoundTripInfo: true)];
+  var formats = [const SimpleFlatFormat(), const SimpleMapFormat(),
+                 const SimpleJsonFormat(storeRoundTripInfo: true)];
 
   test('Basic extraction of a simple object', () {
     // TODO(alanknight): Switch these to use literal types. Issue
@@ -347,7 +347,7 @@
 
   test("Straight JSON format", () {
     var s = new Serialization();
-    var writer = s.newWriter(new SimpleJsonFormat());
+    var writer = s.newWriter(const SimpleJsonFormat());
     var out = json.stringify(writer.write(a1));
     var reconstituted = json.parse(out);
     expect(reconstituted.length, 4);
@@ -359,7 +359,7 @@
     var s = new Serialization()..selfDescribing = false;
     var addressRule = s.addRuleFor(a1)..configureForMaps();
     var personRule = s.addRuleFor(p1)..configureForMaps();
-    var writer = s.newWriter(new SimpleJsonFormat(storeRoundTripInfo: true));
+    var writer = s.newWriter(const SimpleJsonFormat(storeRoundTripInfo: true));
     var out = json.stringify(writer.write(p1));
     var reconstituted = json.parse(out);
     var expected = {
@@ -386,7 +386,7 @@
       ..addRuleFor(a1)
       ..addRuleFor(p1).configureForMaps();
     var p2 = writeAndReadBack(s,
-        new SimpleJsonFormat(storeRoundTripInfo: true), p1);
+        const SimpleJsonFormat(storeRoundTripInfo: true), p1);
     expect(p2.name, "Alice");
     var a2 = p2.address;
     expect(a2.street, "N 34th");
@@ -402,7 +402,7 @@
     var s = new Serialization()
         ..addRule(new PersonRuleReturningMapWithNonStringKey());
     var p2 = writeAndReadBack(s,
-        new SimpleJsonFormat(storeRoundTripInfo: true), p1);
+        const SimpleJsonFormat(storeRoundTripInfo: true), p1);
     expect(p2.name, "Alice");
     expect(p2.address.street, "N 34th");
   });
@@ -462,9 +462,9 @@
       ..addRuleFor(a1)
       ..addRuleFor(p1).configureForMaps()
       ..namedObjects["foo"] = a1;
-    var writer = s.newWriter(new SimpleJsonFormat(storeRoundTripInfo: true));
+    var writer = s.newWriter(const SimpleJsonFormat(storeRoundTripInfo: true));
     var out = writer.write(p1);
-    var reader = s.newReader(new SimpleJsonFormat(storeRoundTripInfo: true));
+    var reader = s.newReader(const SimpleJsonFormat(storeRoundTripInfo: true));
     var p2 = reader.read(out, {"foo" : 12});
     expect(p2.name, "Alice");
     var a2 = p2.address;
@@ -508,7 +508,7 @@
     var s = new Serialization()..addRuleFor(new Person());
     var data = {"abc" : 1, "def" : "ghi"};
     data["person"] = new Person()..name = "Foo";
-    var output = s.write(data, new SimpleMapFormat());
+    var output = s.write(data, const SimpleMapFormat());
     var mapRule = s.rules.firstWhere((x) => x is MapRule);
     var map = output["data"][mapRule.number][0];
     expect(map is Map, isTrue);
@@ -701,10 +701,10 @@
   n2.parent = n1;
   n3.parent = n1;
   var s = serializerSetUp(n1);
-  var output = s.write(n2, new SimpleFlatFormat());
+  var output = s.write(n2, const SimpleFlatFormat());
   expect(output is List, isTrue);
   var s2 = serializerSetUp(n1);
-  var reader = new Reader(s2, new SimpleFlatFormat());
+  var reader = new Reader(s2, const SimpleFlatFormat());
   var m2 = reader.read(output);
   var m1 = m2.parent;
   expect(m1 is Node, isTrue);
diff --git a/pkg/unittest/lib/matcher.dart b/pkg/unittest/lib/matcher.dart
index 8110432..fd6412e 100644
--- a/pkg/unittest/lib/matcher.dart
+++ b/pkg/unittest/lib/matcher.dart
@@ -11,12 +11,16 @@
  * file.
  *
  *     dependencies:
- *       matcher: any
+ *       unittest: any
  *
  * Then run `pub install`.
  *
- * For more information, see the
- * [matcher package on pub.dartlang.org][pkg].
+ * Import this into your Dart code with:
+ *
+ *     import 'package:unittest/matcher.dart';
+ *
+ * For more information, see the [unittest package on pub.dartlang.org].
+ * (http://pub.dartlang.org/packages/unittest).
  *
  * [pub]: http://pub.dartlang.org
  * [pkg]: http://pub.dartlang.org/packages/matcher
diff --git a/pkg/unittest/lib/mock.dart b/pkg/unittest/lib/mock.dart
index 1721dcf..385194f 100644
--- a/pkg/unittest/lib/mock.dart
+++ b/pkg/unittest/lib/mock.dart
@@ -11,12 +11,16 @@
  * file.
  *
  *     dependencies:
- *       mock: any
+ *       unittest: any
  *
  * Then run `pub install`.
  *
- * For more information, see the
- * [mock package on pub.dartlang.org](http://pub.dartlang.org/packages/mock).
+ * Import this into your Dart code with:
+ *
+ *     import 'package:unittest/mock.dart';
+ *
+ * For more information, see the [unittest package on pub.dartlang.org]
+ * (http://pub.dartlang.org/packages/unittest).
  *
  * ## Using ##
  *
diff --git a/pkg/unittest/lib/src/iterable_matchers.dart b/pkg/unittest/lib/src/iterable_matchers.dart
index e5b0e9a..acf41d0 100644
--- a/pkg/unittest/lib/src/iterable_matchers.dart
+++ b/pkg/unittest/lib/src/iterable_matchers.dart
@@ -184,3 +184,62 @@
     }
   }
 }
+
+/**
+ * A pairwise matcher for iterable. You can pass an arbitrary [comparator]
+ * function that takes an expected and actual argument which will be applied
+ * to each pair in order. [description]  should be a meaningful name for
+ * the comparator.
+ */
+Matcher pairwiseCompare(Iterable expected, Function comparator,
+    String description) =>
+        new _PairwiseCompare(expected, comparator, description);
+
+class _PairwiseCompare extends _IterableMatcher {
+  Iterable _expected;
+  Function _comparator;
+  String _description;
+
+  _PairwiseCompare(this._expected, this._comparator, this._description);
+
+  bool matches(item, MatchState matchState) {
+    if (item is! Iterable) return false;
+    if (item.length != _expected.length) return false;
+    var iterator = item.iterator;
+    var i = 0;
+    for (var e in _expected) {
+      iterator.moveNext();
+      if (!_comparator(e, iterator.current)) {
+        matchState.state = {
+            'index': i,
+            'expected': e,
+            'actual' : iterator.current,
+            'state': matchState.state
+        };
+        return false;
+      }
+      i++;
+    }
+    return true;
+  }
+    
+  Description describe(Description description) =>
+      description.add('pairwise $_description ').addDescriptionOf(_expected);
+
+  Description describeMismatch(item, Description mismatchDescription,
+                               MatchState matchState, bool verbose) {
+    if (item is !Iterable) {
+      return mismatchDescription.add('not an Iterable');
+    } else if (item.length != _expected.length) {
+      return mismatchDescription.
+          add('length was ${item.length} instead of ${_expected.length}');
+    } else {
+      return mismatchDescription.
+          addDescriptionOf(matchState.state["actual"]).
+          add(' not $_description ').
+          addDescriptionOf(matchState.state["expected"]).
+          add(' at position ${matchState.state["index"]}');
+    }
+  }
+}
+
diff --git a/pkg/unittest/lib/unittest.dart b/pkg/unittest/lib/unittest.dart
index 6562509..479e392 100644
--- a/pkg/unittest/lib/unittest.dart
+++ b/pkg/unittest/lib/unittest.dart
@@ -173,10 +173,6 @@
 import 'matcher.dart';
 export 'matcher.dart';
 
-// TODO(amouravski): We should not need to import mock here, but it's necessary
-// to enable dartdoc on the mock library, as it's not picked up normally.
-import 'mock.dart';
-
 part 'src/config.dart';
 part 'src/test_case.dart';
 
diff --git a/pkg/unittest/test/matchers_test.dart b/pkg/unittest/test/matchers_test.dart
index 4f1ae6e..10592da 100644
--- a/pkg/unittest/test/matchers_test.dart
+++ b/pkg/unittest/test/matchers_test.dart
@@ -523,6 +523,27 @@
           "Expected: equals <[3, 1]> unordered "
           "but: has no match for element <3> at position 0.");
     });
+
+    test('pairwise compare', () {
+      var c = [1, 2];
+      var d = [1, 2, 3];
+      var e = [1, 4, 9];
+      shouldFail('x', pairwiseCompare(e, (e,a) => a <= e,
+          "less than or equal"),
+          "Expected: pairwise less than or equal <[1, 4, 9]> but: "
+          "not an Iterable.");
+      shouldFail(c, pairwiseCompare(e, (e,a) => a <= e, "less than or equal"),
+          "Expected: pairwise less than or equal <[1, 4, 9]> but: "
+          "length was 2 instead of 3.");
+      shouldPass(d, pairwiseCompare(e, (e,a) => a <= e, "less than or equal"));
+      shouldFail(d, pairwiseCompare(e, (e,a) => a < e, "less than"),
+          "Expected: pairwise less than <[1, 4, 9]> but: "
+          "<1> not less than <1> at position 0.");
+      shouldPass(d, pairwiseCompare(e, (e,a) => a * a == e, "square root of"));
+      shouldFail(d, pairwiseCompare(e, (e,a) => a + a == e, "double"),
+          "Expected: pairwise double <[1, 4, 9]> but: "
+          "<1> not double <1> at position 0.");
+    });
   });
 
   group('Map Matchers', () {
diff --git a/runtime/bin/bin.gypi b/runtime/bin/bin.gypi
index ffc22e2..d493619 100644
--- a/runtime/bin/bin.gypi
+++ b/runtime/bin/bin.gypi
@@ -37,7 +37,7 @@
         {
           'action_name': 'generate_builtin_cc',
           'inputs': [
-            '../tools/create_string_literal.py',
+            '../tools/gen_library_src_paths.py',
             '<(builtin_in_cc_file)',
             '<@(_sources)',
           ],
@@ -46,11 +46,12 @@
           ],
           'action': [
             'python',
-            'tools/create_string_literal.py',
+            'tools/gen_library_src_paths.py',
             '--output', '<(builtin_cc_file)',
             '--input_cc', '<(builtin_in_cc_file)',
             '--include', 'bin/builtin.h',
-            '--var_name', 'dart::bin::Builtin::builtin_source_',
+            '--var_name', 'dart::bin::Builtin::builtin_source_paths_',
+            '--library_name', 'dart:builtin',
             '<@(_sources)',
           ],
           'message': 'Generating ''<(builtin_cc_file)'' file.'
@@ -61,50 +62,32 @@
       'target_name': 'generate_io_cc_file',
       'type': 'none',
       'toolsets':['target','host'],
-      'variables': {
-        'io_dart': '<(gen_source_dir)/io_gen.dart',
-      },
       'sources': [
-        'io.dart',
+        '../../sdk/lib/io/io.dart',
       ],
       'includes': [
         '../../sdk/lib/io/iolib_sources.gypi',
       ],
       'actions': [
         {
-          'action_name': 'generate_io_dart',
-          'inputs': [
-            '../tools/concat_library.py',
-            '<@(_sources)',
-          ],
-          'outputs': [
-            '<(io_dart)',
-          ],
-          'action': [
-            'python',
-            '<@(_inputs)',
-            '--output', '<(io_dart)',
-          ],
-          'message': 'Generating ''<(io_dart)'' file.',
-        },
-        {
           'action_name': 'generate_io_cc',
           'inputs': [
-            '../tools/create_string_literal.py',
+            '../tools/gen_library_src_paths.py',
             '<(builtin_in_cc_file)',
-            '<(io_dart)',
+            '<@(_sources)',
           ],
           'outputs': [
             '<(io_cc_file)',
           ],
           'action': [
             'python',
-            'tools/create_string_literal.py',
+            'tools/gen_library_src_paths.py',
             '--output', '<(io_cc_file)',
             '--input_cc', '<(builtin_in_cc_file)',
             '--include', 'bin/builtin.h',
-            '--var_name', 'dart::bin::Builtin::io_source_',
-            '<(io_dart)',
+            '--var_name', 'dart::bin::Builtin::io_source_paths_',
+            '--library_name', 'dart:io',
+            '<@(_sources)',
           ],
           'message': 'Generating ''<(io_cc_file)'' file.'
         },
@@ -121,7 +104,7 @@
         {
           'action_name': 'generate_io_patch_cc',
           'inputs': [
-            '../tools/create_string_literal.py',
+            '../tools/gen_library_src_paths.py',
             '<(builtin_in_cc_file)',
             '<@(_sources)',
           ],
@@ -130,11 +113,12 @@
           ],
           'action': [
             'python',
-            'tools/create_string_literal.py',
+            'tools/gen_library_src_paths.py',
             '--output', '<(io_patch_cc_file)',
             '--input_cc', '<(builtin_in_cc_file)',
             '--include', 'bin/builtin.h',
-            '--var_name', 'dart::bin::Builtin::io_patch_',
+            '--var_name', 'dart::bin::Builtin::io_patch_paths_',
+            '--library_name', 'dart:io',
             '<@(_sources)',
           ],
           'message': 'Generating ''<(io_patch_cc_file)'' file.'
@@ -154,9 +138,6 @@
         '..',
       ],
       'sources': [
-        'builtin_natives.cc',
-        'builtin.h',
-        'io_natives.h',
         'log_android.cc',
         'log_linux.cc',
         'log_macos.cc',
@@ -270,8 +251,10 @@
       ],
       'sources': [
         'gen_snapshot.cc',
-        # Only looks up native functions in libdart_builtin, not libdart_io.
+        # Very limited native resolver provided.
         'builtin_gen_snapshot.cc',
+        'builtin.cc',
+        'builtin.h',
         # Include generated source files.
         '<(builtin_cc_file)',
         '<(io_cc_file)',
@@ -285,7 +268,7 @@
        }],
         ['OS=="android"', {
           'link_settings': {
-            'libraries': [ '-llog' ],
+            'libraries': [ '-ldl', '-lrt' ],
           },
        }]
       ],
@@ -393,7 +376,10 @@
       ],
       'sources': [
         'main.cc',
+        'builtin_natives.cc',
         'builtin_nolib.cc',
+        'builtin.h',
+        'io_natives.h',
         'resources.h',
         'vmstats.h',
         'vmstats_impl.cc',
@@ -443,6 +429,9 @@
       'sources': [
         'main.cc',
         'builtin.cc',
+        'builtin_natives.cc',
+        'builtin.h',
+        'io_natives.h',
         'resources.h',
         'vmstats.h',
         'vmstats_impl.cc',
@@ -503,6 +492,9 @@
       'sources': [
         'run_vm_tests.cc',
         'builtin.cc',
+        'builtin_natives.cc',
+        'builtin.h',
+        'io_natives.h',
         # Include generated source files.
         '<(builtin_cc_file)',
         '<(io_cc_file)',
@@ -521,6 +513,7 @@
         ['exclude', '\\.(cc|h)$'],
         ['include', 'run_vm_tests.cc'],
         ['include', 'builtin.cc'],
+        ['include', 'builtin_natives.cc'],
         ['include', '_gen\\.cc$'],
         ['include', '_test\\.(cc|h)$'],
       ],
diff --git a/runtime/bin/builtin.cc b/runtime/bin/builtin.cc
index a2d646c..182f2ff 100644
--- a/runtime/bin/builtin.cc
+++ b/runtime/bin/builtin.cc
@@ -8,36 +8,79 @@
 
 #include "bin/builtin.h"
 #include "bin/dartutils.h"
-#include "bin/io_natives.h"
 
 namespace dart {
 namespace bin {
 
 Builtin::builtin_lib_props Builtin::builtin_libraries_[] = {
   /* { url_, source_, patch_url_, patch_source_, has_natives_ } */
-  { DartUtils::kBuiltinLibURL, builtin_source_, NULL, NULL, true },
-  { DartUtils::kIOLibURL, io_source_,
-    DartUtils::kIOLibPatchURL, io_patch_, true },
+  { DartUtils::kBuiltinLibURL, builtin_source_paths_, NULL, NULL, true },
+  { DartUtils::kIOLibURL, io_source_paths_,
+    DartUtils::kIOLibPatchURL, io_patch_paths_, true },
 };
 
 
+// Patch all the specified patch files in the array 'patch_files' into the
+// library specified in 'library'.
+static void LoadPatchFiles(Dart_Handle library,
+                           const char* patch_uri,
+                           const char** patch_files) {
+  for (intptr_t j = 0; patch_files[j] != NULL; j += 2) {
+    Dart_Handle patch_src = DartUtils::ReadStringFromFile(patch_files[j + 1]);
+
+    // Prepend the patch library URI to form a unique script URI for the patch.
+    intptr_t len = snprintf(NULL, 0, "%s/%s", patch_uri, patch_files[j]);
+    char* patch_filename = reinterpret_cast<char*>(malloc(len + 1));
+    snprintf(patch_filename, len + 1, "%s/%s", patch_uri, patch_files[j]);
+    Dart_Handle patch_file_uri = DartUtils::NewString(patch_filename);
+    free(patch_filename);
+
+    DART_CHECK_VALID(Dart_LoadPatch(library, patch_file_uri, patch_src));
+  }
+}
+
+
 Dart_Handle Builtin::Source(BuiltinLibraryId id) {
   ASSERT((sizeof(builtin_libraries_) / sizeof(builtin_lib_props)) ==
          kInvalidLibrary);
   ASSERT(id >= kBuiltinLibrary && id < kInvalidLibrary);
-  return DartUtils::NewString(builtin_libraries_[id].source_);
+
+  // Try to read the source using the path specified for the uri.
+  const char* uri = builtin_libraries_[id].url_;
+  const char** source_paths = builtin_libraries_[id].source_paths_;
+  return GetSource(source_paths, uri);
 }
 
-/**
- * Looks up native functions in both libdart_builtin and libdart_io.
- */
-Dart_NativeFunction Builtin::NativeLookup(Dart_Handle name,
-                                          int argument_count) {
-  Dart_NativeFunction result = BuiltinNativeLookup(name, argument_count);
-  if (result != NULL) return result;
-  return IONativeLookup(name, argument_count);
+
+Dart_Handle Builtin::PartSource(BuiltinLibraryId id, const char* part_uri) {
+  ASSERT((sizeof(builtin_libraries_) / sizeof(builtin_lib_props)) ==
+         kInvalidLibrary);
+  ASSERT(id >= kBuiltinLibrary && id < kInvalidLibrary);
+
+  // Try to read the source using the path specified for the uri.
+  const char** source_paths = builtin_libraries_[id].source_paths_;
+  return GetSource(source_paths, part_uri);
 }
 
+
+Dart_Handle Builtin::GetSource(const char** source_paths, const char* uri) {
+  if (source_paths == NULL) {
+    return Dart_Null();  // No path mapping information exists for library.
+  }
+  const char* source_path = NULL;
+  for (intptr_t i = 0; source_paths[i] != NULL; i += 2) {
+    if (!strcmp(uri, source_paths[i])) {
+      source_path = source_paths[i + 1];
+      break;
+    }
+  }
+  if (source_path == NULL) {
+    return Dart_Null();  // Uri does not exist in path mapping information.
+  }
+  return DartUtils::ReadStringFromFile(source_path);
+}
+
+
 void Builtin::SetNativeResolver(BuiltinLibraryId id) {
   UNREACHABLE();
 }
@@ -56,12 +99,10 @@
       DART_CHECK_VALID(Dart_SetNativeResolver(library, NativeLookup));
     }
     if (builtin_libraries_[id].patch_url_ != NULL) {
-      ASSERT(builtin_libraries_[id].patch_source_ != NULL);
-      Dart_Handle patch_url =
-          DartUtils::NewString(builtin_libraries_[id].patch_url_);
-      Dart_Handle patch_source =
-          DartUtils::NewString(builtin_libraries_[id].patch_source_);
-      DART_CHECK_VALID(Dart_LoadPatch(library, patch_url, patch_source));
+      ASSERT(builtin_libraries_[id].patch_paths_ != NULL);
+      LoadPatchFiles(library,
+                     builtin_libraries_[id].patch_url_,
+                     builtin_libraries_[id].patch_paths_);
     }
   }
   DART_CHECK_VALID(library);
diff --git a/runtime/bin/builtin.h b/runtime/bin/builtin.h
index 67fc9fb..90a121e 100644
--- a/runtime/bin/builtin.h
+++ b/runtime/bin/builtin.h
@@ -9,6 +9,7 @@
 #include <stdlib.h>
 
 #include "include/dart_api.h"
+
 #include "platform/assert.h"
 #include "platform/globals.h"
 
@@ -33,27 +34,37 @@
     kInvalidLibrary,
   };
 
+  // Get source corresponding to built in library specified in 'id'.
   static Dart_Handle Source(BuiltinLibraryId id);
+
+  // Get source of part file specified in 'uri'.
+  static Dart_Handle PartSource(BuiltinLibraryId id, const char* part_uri);
+
+  // Setup native resolver method built in library specified in 'id'.
   static void SetNativeResolver(BuiltinLibraryId id);
+
+  // Check if built in library specified in 'id' is already loaded, if not
+  // load it.
   static Dart_Handle LoadAndCheckLibrary(BuiltinLibraryId id);
-  static void PrintString(FILE* out, Dart_Handle object);
 
  private:
+  // Map specified URI to an actual file name from 'source_paths' and read
+  // the file.
+  static Dart_Handle GetSource(const char** source_paths, const char* uri);
+
+  // Native method support.
   static Dart_NativeFunction NativeLookup(Dart_Handle name,
                                           int argument_count);
-  static Dart_NativeFunction BuiltinNativeLookup(Dart_Handle name,
-                                                 int argument_count);
 
-  static const char builtin_source_[];
-  static const char io_source_[];
-  static const char io_patch_[];
-  static const char web_source_[];
+  static const char* builtin_source_paths_[];
+  static const char* io_source_paths_[];
+  static const char* io_patch_paths_[];
 
   typedef struct {
     const char* url_;
-    const char* source_;
+    const char** source_paths_;
     const char* patch_url_;
-    const char* patch_source_;
+    const char** patch_paths_;
     bool has_natives_;
   } builtin_lib_props;
   static builtin_lib_props builtin_libraries_[];
diff --git a/runtime/bin/builtin_gen_snapshot.cc b/runtime/bin/builtin_gen_snapshot.cc
index c76cd55..9a89adb 100644
--- a/runtime/bin/builtin_gen_snapshot.cc
+++ b/runtime/bin/builtin_gen_snapshot.cc
@@ -2,71 +2,70 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+#include <stdlib.h>
 #include <stdio.h>
+#include <string.h>
 
 #include "include/dart_api.h"
 
 #include "bin/builtin.h"
-#include "bin/dartutils.h"
-
 
 namespace dart {
 namespace bin {
 
-Builtin::builtin_lib_props Builtin::builtin_libraries_[] = {
-  /* { url_, source_, patch_url_, patch_source_, has_natives_ } */
-  { DartUtils::kBuiltinLibURL, builtin_source_, NULL, NULL, true },
-  { DartUtils::kIOLibURL, io_source_,
-    DartUtils::kIOLibPatchURL, io_patch_, true },
+// Lists the native function implementing basic logging facility.
+#define BUILTIN_NATIVE_LIST(V)                                                 \
+  V(Logger_PrintString, 1)
+
+BUILTIN_NATIVE_LIST(DECLARE_FUNCTION);
+
+static struct NativeEntries {
+  const char* name_;
+  Dart_NativeFunction function_;
+  int argument_count_;
+} BuiltinEntries[] = {
+  BUILTIN_NATIVE_LIST(REGISTER_FUNCTION)
 };
 
 
-Dart_Handle Builtin::Source(BuiltinLibraryId id) {
-  ASSERT((sizeof(builtin_libraries_) / sizeof(builtin_lib_props)) ==
-         kInvalidLibrary);
-  ASSERT(id >= kBuiltinLibrary && id < kInvalidLibrary);
-  return DartUtils::NewString(builtin_libraries_[id].source_);
-}
-
-
 Dart_NativeFunction Builtin::NativeLookup(Dart_Handle name,
                                           int argument_count) {
-  UNREACHABLE();
+  const char* function_name = NULL;
+  Dart_Handle result = Dart_StringToCString(name, &function_name);
+  DART_CHECK_VALID(result);
+  ASSERT(function_name != NULL);
+  int num_entries = sizeof(BuiltinEntries) / sizeof(struct NativeEntries);
+  for (int i = 0; i < num_entries; i++) {
+    struct NativeEntries* entry = &(BuiltinEntries[i]);
+    if (!strcmp(function_name, entry->name_) &&
+        (entry->argument_count_ == argument_count)) {
+      return reinterpret_cast<Dart_NativeFunction>(entry->function_);
+    }
+  }
   return NULL;
 }
 
 
-void Builtin::SetNativeResolver(BuiltinLibraryId id) {
-  UNREACHABLE();
-}
-
-
-Dart_Handle Builtin::LoadAndCheckLibrary(BuiltinLibraryId id) {
-  ASSERT((sizeof(builtin_libraries_) / sizeof(builtin_lib_props)) ==
-         kInvalidLibrary);
-  ASSERT(id >= kBuiltinLibrary && id < kInvalidLibrary);
-  Dart_Handle url = DartUtils::NewString(builtin_libraries_[id].url_);
-  Dart_Handle library = Dart_LookupLibrary(url);
-  if (Dart_IsError(library)) {
-    library = Dart_LoadLibrary(url, Source(id));
-    if (!Dart_IsError(library) && (builtin_libraries_[id].has_natives_)) {
-      // Setup the native resolver for built in library functions.
-      // Looks up native functions only in libdart_builtin, not libdart_io.
-      // This is for use in the snapshot generator, which should be
-      // independent of most of the dart:io C++ code.
-      DART_CHECK_VALID(Dart_SetNativeResolver(library, BuiltinNativeLookup));
-    }
-    if (builtin_libraries_[id].patch_url_ != NULL) {
-      ASSERT(builtin_libraries_[id].patch_source_ != NULL);
-      Dart_Handle patch_url =
-          DartUtils::NewString(builtin_libraries_[id].patch_url_);
-      Dart_Handle patch_source =
-          DartUtils::NewString(builtin_libraries_[id].patch_source_);
-      DART_CHECK_VALID(Dart_LoadPatch(library, patch_url, patch_source));
-    }
+// Implementation of native functions which are used for some
+// test/debug functionality in standalone dart mode.
+void FUNCTION_NAME(Logger_PrintString)(Dart_NativeArguments args) {
+  Dart_EnterScope();
+  intptr_t length = 0;
+  uint8_t* chars = NULL;
+  Dart_Handle str = Dart_GetNativeArgument(args, 0);
+  Dart_Handle result = Dart_StringToUTF8(str, &chars, &length);
+  if (Dart_IsError(result)) {
+    // TODO(turnidge): Consider propagating some errors here.  What if
+    // an isolate gets interrupted by the embedder in the middle of
+    // Dart_StringToUTF8?  We need to make sure not to swallow the
+    // interrupt.
+    fputs(Dart_GetError(result), stdout);
+  } else {
+    fwrite(chars, sizeof(*chars), length, stdout);
   }
-  DART_CHECK_VALID(library);
-  return library;
+  fputc('\n', stdout);
+  fflush(stdout);
+  Dart_ExitScope();
 }
 
 }  // namespace bin
diff --git a/runtime/bin/builtin_in.cc b/runtime/bin/builtin_in.cc
index b2bdd6d..2f30aad 100644
--- a/runtime/bin/builtin_in.cc
+++ b/runtime/bin/builtin_in.cc
@@ -2,12 +2,12 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-#include "{{INCLUDE}}"
+#include "{{INCLUDE}}" // NOLINT
 
-// The string on the next line will be filled in with the contents of the
-// builtin.dart file.
-// This string forms the content of builtin functionality which is injected
-// into standalone dart to provide some test/debug functionality.
-const char {{VAR_NAME}}[] = {
-  {{DART_SOURCE}}
+// This file is used to generate the mapping of standalone dart libraries
+// to the corresponding files that implement them.
+const char* {{VAR_NAME}}[] = {
+{{LIBRARY_SOURCE_MAP}}
+{{PART_SOURCE_MAP}}
+  NULL, NULL
 };
diff --git a/runtime/bin/builtin_natives.cc b/runtime/bin/builtin_natives.cc
index a942b6a..fcfb1ee 100644
--- a/runtime/bin/builtin_natives.cc
+++ b/runtime/bin/builtin_natives.cc
@@ -2,15 +2,17 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-#include "bin/builtin.h"
-
 #include <stdlib.h>
+#include <stdio.h>
 #include <string.h>
 
-#include "bin/dartutils.h"
 #include "include/dart_api.h"
+
 #include "platform/assert.h"
 
+#include "bin/builtin.h"
+#include "bin/dartutils.h"
+#include "bin/io_natives.h"
 
 namespace dart {
 namespace bin {
@@ -69,8 +71,11 @@
 };
 
 
-Dart_NativeFunction Builtin::BuiltinNativeLookup(Dart_Handle name,
-                                                 int argument_count) {
+/**
+ * Looks up native functions in both libdart_builtin and libdart_io.
+ */
+Dart_NativeFunction Builtin::NativeLookup(Dart_Handle name,
+                                          int argument_count) {
   const char* function_name = NULL;
   Dart_Handle result = Dart_StringToCString(name, &function_name);
   DART_CHECK_VALID(result);
@@ -83,34 +88,29 @@
       return reinterpret_cast<Dart_NativeFunction>(entry->function_);
     }
   }
-  return NULL;
+  return IONativeLookup(name, argument_count);
 }
 
 
 // Implementation of native functions which are used for some
 // test/debug functionality in standalone dart mode.
-
-void Builtin::PrintString(FILE* out, Dart_Handle str) {
+void FUNCTION_NAME(Logger_PrintString)(Dart_NativeArguments args) {
+  Dart_EnterScope();
   intptr_t length = 0;
   uint8_t* chars = NULL;
+  Dart_Handle str = Dart_GetNativeArgument(args, 0);
   Dart_Handle result = Dart_StringToUTF8(str, &chars, &length);
   if (Dart_IsError(result)) {
     // TODO(turnidge): Consider propagating some errors here.  What if
     // an isolate gets interrupted by the embedder in the middle of
     // Dart_StringToUTF8?  We need to make sure not to swallow the
     // interrupt.
-    fputs(Dart_GetError(result), out);
+    fputs(Dart_GetError(result), stdout);
   } else {
-    fwrite(chars, sizeof(*chars), length, out);
+    fwrite(chars, sizeof(*chars), length, stdout);
   }
-  fputc('\n', out);
-  fflush(out);
-}
-
-
-void FUNCTION_NAME(Logger_PrintString)(Dart_NativeArguments args) {
-  Dart_EnterScope();
-  Builtin::PrintString(stdout, Dart_GetNativeArgument(args, 0));
+  fputc('\n', stdout);
+  fflush(stdout);
   Dart_ExitScope();
 }
 
diff --git a/runtime/bin/builtin_nolib.cc b/runtime/bin/builtin_nolib.cc
index 6ca4a20..214caba 100644
--- a/runtime/bin/builtin_nolib.cc
+++ b/runtime/bin/builtin_nolib.cc
@@ -22,21 +22,20 @@
 
 
 Dart_Handle Builtin::Source(BuiltinLibraryId id) {
-  ASSERT((sizeof(builtin_libraries_) / sizeof(builtin_lib_props)) ==
-         kInvalidLibrary);
   return Dart_NewApiError("Unreachable code in Builtin::Source (%d).", id);
 }
 
-/**
- * Looks up native functions in both libdart_builtin and libdart_io.
- */
-Dart_NativeFunction Builtin::NativeLookup(Dart_Handle name,
-                                          int argument_count) {
-  Dart_NativeFunction result = BuiltinNativeLookup(name, argument_count);
-  if (result != NULL) return result;
-  return IONativeLookup(name, argument_count);
+
+Dart_Handle Builtin::PartSource(BuiltinLibraryId id, const char* uri) {
+  return Dart_NewApiError("Unreachable code in Builtin::PartSource (%d).", id);
 }
 
+
+Dart_Handle Builtin::GetSource(const char** source_paths, const char* uri) {
+  return Dart_NewApiError("Unreachable code in Builtin::GetSource (%s).", uri);
+}
+
+
 void Builtin::SetNativeResolver(BuiltinLibraryId id) {
   ASSERT((sizeof(builtin_libraries_) / sizeof(builtin_lib_props)) ==
          kInvalidLibrary);
@@ -57,14 +56,6 @@
   ASSERT(id >= kBuiltinLibrary && id < kInvalidLibrary);
   Dart_Handle url = DartUtils::NewString(builtin_libraries_[id].url_);
   Dart_Handle library = Dart_LookupLibrary(url);
-  if (Dart_IsError(library)) {
-    ASSERT(id > kIOLibrary);
-    library = Dart_LoadLibrary(url, Source(id));
-    if (!Dart_IsError(library) && (builtin_libraries_[id].has_natives_)) {
-      // Setup the native resolver for built in library functions.
-      DART_CHECK_VALID(Dart_SetNativeResolver(library, NativeLookup));
-    }
-  }
   DART_CHECK_VALID(library);
   return library;
 }
diff --git a/runtime/bin/dartutils.cc b/runtime/bin/dartutils.cc
index 370eed0..0b5e016 100644
--- a/runtime/bin/dartutils.cc
+++ b/runtime/bin/dartutils.cc
@@ -4,13 +4,16 @@
 
 #include "bin/dartutils.h"
 
+#include "include/dart_api.h"
+
+#include "platform/assert.h"
+#include "platform/globals.h"
+
 #include "bin/extensions.h"
 #include "bin/directory.h"
 #include "bin/file.h"
 #include "bin/io_buffer.h"
-#include "include/dart_api.h"
-#include "platform/assert.h"
-#include "platform/globals.h"
+#include "bin/utils.h"
 
 namespace dart {
 namespace bin {
@@ -24,7 +27,6 @@
 const char* DartUtils::kIOLibURL = "dart:io";
 const char* DartUtils::kIOLibPatchURL = "dart:io-patch";
 const char* DartUtils::kUriLibURL = "dart:uri";
-const char* DartUtils::kUtfLibURL = "dart:utf";
 
 const char* DartUtils::kIdFieldName = "_id";
 
@@ -323,12 +325,22 @@
   if (Dart_IsError(result)) {
     return result;
   }
+  Dart_Handle library_url = Dart_LibraryUrl(library);
+  const char* library_url_string = NULL;
+  result = Dart_StringToCString(library_url, &library_url_string);
+  if (Dart_IsError(result)) {
+    return result;
+  }
+
   bool is_dart_scheme_url = DartUtils::IsDartSchemeURL(url_string);
+  bool is_io_library = DartUtils::IsDartIOLibURL(library_url_string);
   bool is_dart_extension_url = DartUtils::IsDartExtensionSchemeURL(url_string);
+
+  // Handle URI canonicalization requests.
   if (tag == kCanonicalizeUrl) {
-    // If this is a Dart Scheme URL then it is not modified as it will be
-    // handled by the VM internally.
-    if (is_dart_scheme_url) {
+    // If this is a Dart Scheme URL or 'part' of a io library
+    // then it is not modified as it will be handled internally.
+    if (is_dart_scheme_url || is_io_library) {
       return url;
     }
     // Resolve the url within the context of the library's URL.
@@ -340,30 +352,41 @@
     }
     return ResolveUri(library_url, url, builtin_lib);
   }
+
+  // Handle 'import' of dart scheme URIs (i.e they start with 'dart:').
   if (is_dart_scheme_url) {
     if (tag == kImportTag) {
       // Handle imports of other built-in libraries present in the SDK.
-      Builtin::BuiltinLibraryId id;
       if (DartUtils::IsDartIOLibURL(url_string)) {
-        id = Builtin::kIOLibrary;
-      } else {
-        return Dart_Error("Do not know how to load '%s'", url_string);
+        return Builtin::LoadAndCheckLibrary(Builtin::kIOLibrary);
       }
-      return Builtin::LoadAndCheckLibrary(id);
+      return Dart_Error("Do not know how to load '%s'", url_string);
     } else {
       ASSERT(tag == kSourceTag);
       return Dart_Error("Unable to load source '%s' ", url_string);
     }
-  } else {
-    // Get the file path out of the url.
-    Dart_Handle builtin_lib =
-        Builtin::LoadAndCheckLibrary(Builtin::kBuiltinLibrary);
-    Dart_Handle file_path = FilePathFromUri(url, builtin_lib);
-    if (Dart_IsError(file_path)) {
-      return file_path;
-    }
-    Dart_StringToCString(file_path, &url_string);
   }
+
+  // Handle 'part' of IO library.
+  if (is_io_library) {
+    if (tag == kSourceTag) {
+      return Dart_LoadSource(
+          library, url, Builtin::PartSource(Builtin::kIOLibrary, url_string));
+    } else {
+      ASSERT(tag == kImportTag);
+      return Dart_Error("Unable to import '%s' ", url_string);
+    }
+  }
+
+  // Handle 'import' or 'part' requests for all other URIs.
+  // Get the file path out of the url.
+  Dart_Handle builtin_lib =
+      Builtin::LoadAndCheckLibrary(Builtin::kBuiltinLibrary);
+  Dart_Handle file_path = FilePathFromUri(url, builtin_lib);
+  if (Dart_IsError(file_path)) {
+    return file_path;
+  }
+  Dart_StringToCString(file_path, &url_string);
   if (is_dart_extension_url) {
     if (tag != kImportTag) {
       return Dart_Error("Dart extensions must use import: '%s'", url_string);
diff --git a/runtime/bin/dartutils.h b/runtime/bin/dartutils.h
index a9d1393..a9b86f7 100644
--- a/runtime/bin/dartutils.h
+++ b/runtime/bin/dartutils.h
@@ -5,9 +5,9 @@
 #ifndef BIN_DARTUTILS_H_
 #define BIN_DARTUTILS_H_
 
-#include "bin/builtin.h"
-#include "bin/utils.h"
 #include "include/dart_api.h"
+
+#include "platform/assert.h"
 #include "platform/globals.h"
 
 namespace dart {
@@ -15,6 +15,7 @@
 
 // Forward declarations.
 class File;
+class OSError;
 
 /* Handles error handles returned from Dart API functions.  If a value
  * is an error, uses Dart_PropagateError to throw it to the enclosing
@@ -190,10 +191,6 @@
   static const char* kIOLibURL;
   static const char* kIOLibPatchURL;
   static const char* kUriLibURL;
-  static const char* kUtfLibURL;
-  static const char* kIsolateLibURL;
-  static const char* kScalarlistLibURL;
-  static const char* kWebLibURL;
 
   static const char* kIdFieldName;
 
diff --git a/runtime/bin/dbg_connection.cc b/runtime/bin/dbg_connection.cc
index dc6dc84..c2fc8c2 100644
--- a/runtime/bin/dbg_connection.cc
+++ b/runtime/bin/dbg_connection.cc
@@ -22,7 +22,7 @@
 namespace bin {
 
 int DebuggerConnectionHandler::listener_fd_ = -1;
-dart::Monitor DebuggerConnectionHandler::handler_lock_;
+dart::Monitor* DebuggerConnectionHandler::handler_lock_ = new dart::Monitor();
 
 // TODO(asiva): Remove this once we have support for multiple debugger
 // connections. For now we just store the single debugger connection
@@ -280,7 +280,8 @@
 
 void DebuggerConnectionHandler::StartHandler(const char* address,
                                              int port_number) {
-  MonitorLocker ml(&handler_lock_);
+  ASSERT(handler_lock_ != NULL);
+  MonitorLocker ml(handler_lock_);
   if (listener_fd_ != -1) {
     return;  // The debugger connection handler was already started.
   }
@@ -307,7 +308,8 @@
 
 
 void DebuggerConnectionHandler::WaitForConnection() {
-  MonitorLocker ml(&handler_lock_);
+  ASSERT(handler_lock_ != NULL);
+  MonitorLocker ml(handler_lock_);
   while (!IsConnected()) {
     dart::Monitor::WaitResult res = ml.Wait();
     ASSERT(res == dart::Monitor::kNotified);
@@ -316,13 +318,15 @@
 
 
 void DebuggerConnectionHandler::SendMsg(int debug_fd, dart::TextBuffer* msg) {
-  MonitorLocker ml(&handler_lock_);
+  ASSERT(handler_lock_ != NULL);
+  MonitorLocker ml(handler_lock_);
   SendMsgHelper(debug_fd, msg);
 }
 
 
 void DebuggerConnectionHandler::BroadcastMsg(dart::TextBuffer* msg) {
-  MonitorLocker ml(&handler_lock_);
+  ASSERT(handler_lock_ != NULL);
+  MonitorLocker ml(handler_lock_);
   // TODO(asiva): Once we support connection to multiple debuggers
   // we need to send the message to all of them.
   ASSERT(singleton_handler != NULL);
@@ -371,7 +375,8 @@
 void DebuggerConnectionHandler::AcceptDbgConnection(int debug_fd) {
   AddNewDebuggerConnection(debug_fd);
   {
-    MonitorLocker ml(&handler_lock_);
+    ASSERT(handler_lock_ != NULL);
+    MonitorLocker ml(handler_lock_);
     ml.NotifyAll();
   }
   // TODO(asiva): Once we implement support for multiple connections
diff --git a/runtime/bin/dbg_connection.h b/runtime/bin/dbg_connection.h
index 37080fb..0586190 100644
--- a/runtime/bin/dbg_connection.h
+++ b/runtime/bin/dbg_connection.h
@@ -97,7 +97,7 @@
   // mutex/condition variable used by isolates when writing back to the
   // debugger. This is also used to ensure that the isolate waits for
   // a debugger to be attached when that is requested on the command line.
-  static dart::Monitor handler_lock_;
+  static dart::Monitor* handler_lock_;
 
   // The socket that is listening for incoming debugger connections.
   // This descriptor is created and closed by a native thread.
diff --git a/runtime/bin/directory_win.cc b/runtime/bin/directory_win.cc
index f0f9f935..8f97f0e 100644
--- a/runtime/bin/directory_win.cc
+++ b/runtime/bin/directory_win.cc
@@ -7,6 +7,7 @@
 
 #include "bin/directory.h"
 #include "bin/file.h"
+#include "bin/utils.h"
 
 #include <errno.h>  // NOLINT
 #include <sys/stat.h>  // NOLINT
diff --git a/runtime/bin/eventhandler_patch.dart b/runtime/bin/eventhandler_patch.dart
index 8fb487a..1027708 100644
--- a/runtime/bin/eventhandler_patch.dart
+++ b/runtime/bin/eventhandler_patch.dart
@@ -2,6 +2,8 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+import 'dart:nativewrappers';
+
 patch class _EventHandler {
   /* patch */ static void _start() {
     if (_eventHandler == null) {
diff --git a/runtime/bin/file_win.cc b/runtime/bin/file_win.cc
index 6877fb3..4341edc 100644
--- a/runtime/bin/file_win.cc
+++ b/runtime/bin/file_win.cc
@@ -16,6 +16,7 @@
 
 #include "bin/builtin.h"
 #include "bin/log.h"
+#include "bin/utils.h"
 
 
 namespace dart {
diff --git a/runtime/bin/io.dart b/runtime/bin/io.dart
deleted file mode 100644
index 2bec045..0000000
--- a/runtime/bin/io.dart
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// We should be able to get rid of this file. This is used to set up
-// the dart:io library for the VM because it cannot use the actual library
-// file which is in lib/io/io.dart.
-
-library dart.io;
-import "dart:async";
-import "dart:collection";
-import "dart:crypto";
-import "dart:isolate";
-import 'dart:json' as JSON;
-import "dart:math";
-import "dart:nativewrappers";
-import "dart:typed_data";
-import "dart:uri";
-import "dart:utf";
diff --git a/runtime/bin/platform.cc b/runtime/bin/platform.cc
index c47194e..850be27 100644
--- a/runtime/bin/platform.cc
+++ b/runtime/bin/platform.cc
@@ -2,10 +2,13 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-#include "bin/file.h"
 #include "bin/platform.h"
+
 #include "include/dart_api.h"
 
+#include "bin/file.h"
+#include "bin/utils.h"
+
 namespace dart {
 namespace bin {
 
diff --git a/runtime/bin/platform_win.cc b/runtime/bin/platform_win.cc
index 3ba9303..85b6694 100644
--- a/runtime/bin/platform_win.cc
+++ b/runtime/bin/platform_win.cc
@@ -8,6 +8,7 @@
 #include "bin/platform.h"
 #include "bin/log.h"
 #include "bin/socket.h"
+#include "bin/utils.h"
 
 
 namespace dart {
diff --git a/runtime/bin/secure_socket.h b/runtime/bin/secure_socket.h
index 536a77a..88b4c73 100644
--- a/runtime/bin/secure_socket.h
+++ b/runtime/bin/secure_socket.h
@@ -15,11 +15,12 @@
 #include <prnetdb.h>
 #include <ssl.h>
 
-#include "bin/builtin.h"
-#include "bin/dartutils.h"
 #include "platform/globals.h"
 #include "platform/thread.h"
 
+#include "bin/builtin.h"
+#include "bin/dartutils.h"
+#include "bin/utils.h"
 
 namespace dart {
 namespace bin {
diff --git a/runtime/bin/socket_win.cc b/runtime/bin/socket_win.cc
index e8b0d47..60a6163 100644
--- a/runtime/bin/socket_win.cc
+++ b/runtime/bin/socket_win.cc
@@ -10,6 +10,7 @@
 #include "bin/file.h"
 #include "bin/log.h"
 #include "bin/socket.h"
+#include "bin/utils.h"
 
 
 namespace dart {
diff --git a/runtime/embedders/openglui/build_skia.sh b/runtime/embedders/openglui/build_skia.sh
index 5190f10..03f0f5b 100755
--- a/runtime/embedders/openglui/build_skia.sh
+++ b/runtime/embedders/openglui/build_skia.sh
@@ -65,6 +65,7 @@
   gclient sync
 
   export ANDROID_SDK_ROOT=`readlink -f ../android_tools/sdk`
+  export GSUTIL_LOCATION=`readlink -f ../gsutil`
 
   cd trunk
 
@@ -72,7 +73,7 @@
   if [ ${CLEAN} != 0 ] ; then
     ../android/bin/android_make -d $TARGET_ARCH -j clean
   else
-    env -i BUILDTYPE=$BUILD ANDROID_SDK_ROOT="${ANDROID_SDK_ROOT}" PATH="${PATH}:${DART_DIR}/third_party/gsutil" ../android/bin/android_make BUILDTYPE=$BUILD -d $TARGET_ARCH -j --debug=j
+    env -i BUILDTYPE=$BUILD ANDROID_SDK_ROOT="${ANDROID_SDK_ROOT}" PATH="${PATH}:${GSUTIL_LOCATION}" ../android/bin/android_make BUILDTYPE=$BUILD -d $TARGET_ARCH -j --debug=j
   fi
 
 else
diff --git a/runtime/lib/string_patch.dart b/runtime/lib/string_patch.dart
index 5d2f38e..214e1f0 100644
--- a/runtime/lib/string_patch.dart
+++ b/runtime/lib/string_patch.dart
@@ -336,12 +336,23 @@
    * into a result string.
    */
   static String _interpolate(List values) {
-    int numValues = values.length;
+    final int numValues = values.length;
     _ObjectArray stringList = new List(numValues);
+    bool isOneByteString = true;
+    int totalLength = 0;
     for (int i = 0; i < numValues; i++) {
-      stringList[i] = values[i].toString();
+      var s = values[i].toString();
+      if (isOneByteString && (s is _OneByteString)) {
+        totalLength += s.length;
+      } else {
+        isOneByteString = false;
+      }
+      stringList[i] = s;
     }
-    return _concatAll(stringList);
+    if (isOneByteString) {
+      return _OneByteString._concatAll(stringList, totalLength);
+    }
+    return _concatAllNative(stringList);
   }
 
   Iterable<Match> allMatches(String str) {
@@ -434,24 +445,56 @@
 
   static String concatAll(Iterable<String> strings) {
     _ObjectArray stringsArray;
+    final len = strings.length;
+    bool isOneByteString = true;
+    int totalLength = 0;
     if (strings is _ObjectArray) {
       stringsArray = strings;
-      for (int i = 0; i < strings.length; i++) {
-        if (strings[i] is! String) throw new ArgumentError(strings[i]);
+      for (int i = 0; i < len; i++) {
+        var string = strings[i];
+        if (string is _OneByteString) {
+          totalLength += string.length;
+        } else {
+          isOneByteString = false;
+          if (string is! String) throw new ArgumentError(string);
+        }
       }
     } else {
-      int len = strings.length;
+      // Copy into an _ObjectArray.
       stringsArray = new _ObjectArray(len);
       int i = 0;
-      for (String string in strings) {
-        if (string is! String) throw new ArgumentError(string);
+      for (int i = 0; i < len; i++) {
+        var string = strings[i];
+        if (string is _OneByteString) {
+          totalLength += s.length;
+        } else {
+          isOneByteString = false;
+          if (string is! String) throw new ArgumentError(string);
+        }
         stringsArray[i++] = string;
       }
     }
-    return _concatAll(stringsArray);
+    if (isOneByteString) {
+      return _OneByteString._concatAll(stringsArray, totalLength);
+    }
+    return _concatAllNative(stringsArray);
   }
 
-  static String _concatAll(_ObjectArray<String> strings)
+  static String _concatAll(_ObjectArray<String> strings) {
+    int totalLength = 0;
+    final stringsLength = strings.length;
+    for (int i = 0; i < stringsLength; i++) {
+      var e = strings[i];
+      if (e is! _OneByteString) {
+        return _concatAllNative(strings);
+      }
+      totalLength += e.length;
+    }
+    return _OneByteString._concatAll(strings, totalLength);
+  }
+
+  // Call this method if not all list elements are OneByteString-s.
+  static String _concatAllNative(_ObjectArray<String> strings)
       native "Strings_concatAll";
 }
 
@@ -486,11 +529,32 @@
     return super.split(pattern);
   }
 
+  // All element of 'strings' must be OneByteStrings.
+  static _concatAll(_ObjectArray<String> strings, int totalLength) {
+    // TODO(srdjan): Improve code below and raise or eliminate the limit.
+    if (totalLength > 128) {
+      // Native is quicker.
+      return _StringBase._concatAllNative(strings);
+    }
+    var res = _OneByteString._allocate(totalLength);
+    final stringsLength = strings.length;
+    int rIx = 0;
+    for (int i = 0; i < stringsLength; i++) {
+      _OneByteString e = strings[i];
+      final eLength = e.length;
+      for (int s = 0; s < eLength; s++) {
+        res._setAt(rIx++, e.codeUnitAt(s));
+      }
+    }
+    return res;
+  }
+
   // Allocates a string of given length, expecting its content to be
   // set using _setAt.
   static _OneByteString _allocate(int length) native "OneByteString_allocate";
 
-  // Code point value must be a valid Latin1 (0..0xFF). Index must be valid.
+  // This is internal helper method. Code point value must be a valid
+  // Latin1 value (0..0xFF), index must be valid.
   void _setAt(int index, int codePoint) native "OneByteString_setAt";
 }
 
diff --git a/runtime/tools/concat_library.py b/runtime/tools/concat_library.py
deleted file mode 100644
index dfb574a..0000000
--- a/runtime/tools/concat_library.py
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
-# for details. All rights reserved. Use of this source code is governed by a
-# BSD-style license that can be found in the LICENSE file.
-
-import optparse
-import shutil
-import sys
-
-def parse_options(argv):
-    parser = optparse.OptionParser(usage="Usage: %prog [options] files")
-    parser.add_option("--output",
-                      dest="output",
-                      help="Write output to FILE.",
-                      metavar="FILE")
-    (options, arguments) = parser.parse_args(argv[1:])
-    if not arguments:
-        parser.error("At least one input file must be provided.")
-    if not options.output:
-        parser.error("No --output provided.")
-    return (options, arguments)
-
-
-def main():
-    # Print the command that is being run. This is helpful when
-    # debugging build errors.
-    sys.stderr.write('%s\n' % ' '.join(sys.argv))
-    (options, arguments) = parse_options(sys.argv)
-    tmp_name = '%s.tmp' % options.output
-    with open(tmp_name, 'w') as output:
-        for source in arguments:
-            with open(source, 'r') as inpt:
-                for line in inpt:
-                    # Drop unneeded library tags as all the library's files
-                    # are concatenated into one big file here:
-                    # The 'part' and 'part of' library tags are removed.
-                    if line.startswith('#source') or line.startswith('part '):
-                        line = '// %s' % line
-                    output.write(line)
-    shutil.move(tmp_name, options.output)
-
-if __name__ == '__main__':
-    main()
diff --git a/runtime/tools/gyp/runtime-configurations.gypi b/runtime/tools/gyp/runtime-configurations.gypi
index 96ce6aa..a648a53 100644
--- a/runtime/tools/gyp/runtime-configurations.gypi
+++ b/runtime/tools/gyp/runtime-configurations.gypi
@@ -20,6 +20,14 @@
     ],
   },
 
+  'conditions': [
+    ['OS=="android"', {
+      'includes': [
+        'runtime_configurations_android.gypi',
+      ],
+    }],
+  ],
+
   'target_defaults': {
     'configurations': {
 
diff --git a/runtime/tools/gyp/runtime_configurations_android.gypi b/runtime/tools/gyp/runtime_configurations_android.gypi
new file mode 100644
index 0000000..15eafa5
--- /dev/null
+++ b/runtime/tools/gyp/runtime_configurations_android.gypi
@@ -0,0 +1,54 @@
+# Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Definitions for building Chrome with Dart on Android.
+# This is mostly excerpted from:
+# http://src.chromium.org/viewvc/chrome/trunk/src/build/common.gypi
+
+{
+  'target_defaults': {
+    'cflags': [
+      '-Wno-abi',
+      '-Wall',
+      '-W',
+      '-Wno-unused-parameter',
+      '-Wnon-virtual-dtor',
+      '-fno-rtti',
+      '-fno-exceptions',
+    ],
+    'target_conditions': [
+      ['_toolset=="target"', {
+        'cflags!': [
+          '-pthread',  # Not supported by Android toolchain.
+        ],
+        'cflags': [
+          '-U__linux__',  # Don't allow toolchain to claim -D__linux__
+          '-ffunction-sections',
+          '-funwind-tables',
+          '-fstack-protector',
+          '-fno-short-enums',
+          '-finline-limit=64',
+          '-Wa,--noexecstack',
+        ],
+        'defines': [
+          'ANDROID',
+          'USE_STLPORT=1',
+          '_STLP_USE_PTR_SPECIALIZATIONS=1',
+          '_STLP_NO_CSTD_FUNCTION_IMPORTS=1',
+          'HAVE_OFF64_T',
+          'HAVE_SYS_UIO_H',
+        ],
+        'ldflags!': [
+          '-pthread',  # Not supported by Android toolchain.
+        ],
+        'ldflags': [
+          '-nostdlib',
+          '-Wl,--no-undefined',
+          # Don't export symbols from statically linked libraries.
+          '-Wl,--exclude-libs=ALL',
+        ],
+      }],  # _toolset=="target"
+    ],  # target_conditions
+  },  # target_defaults
+}
diff --git a/runtime/vm/assembler_arm.cc b/runtime/vm/assembler_arm.cc
index 7a0f82f..dffa50b 100644
--- a/runtime/vm/assembler_arm.cc
+++ b/runtime/vm/assembler_arm.cc
@@ -5,16 +5,16 @@
 #include "vm/globals.h"
 #if defined(TARGET_ARCH_ARM)
 
-// An extra check since we are assuming the existence of /proc/cpuinfo below.
-#if !defined(USING_SIMULATOR) && !defined(__linux__)
-#error ARM cross-compile only supported on Linux
-#endif
-
 #include "vm/assembler.h"
 #include "vm/simulator.h"
 #include "vm/runtime_entry.h"
 #include "vm/stub_code.h"
 
+// An extra check since we are assuming the existence of /proc/cpuinfo below.
+#if !defined(USING_SIMULATOR) && !defined(__linux__)
+#error ARM cross-compile only supported on Linux
+#endif
+
 namespace dart {
 
 DEFINE_FLAG(bool, print_stop_message, true, "Print stop message.");
diff --git a/runtime/vm/bootstrap.cc b/runtime/vm/bootstrap.cc
index e95dc90..bdc5054 100644
--- a/runtime/vm/bootstrap.cc
+++ b/runtime/vm/bootstrap.cc
@@ -15,8 +15,6 @@
 
 namespace dart {
 
-DEFINE_FLAG(bool, print_bootstrap, false, "Print the bootstrap source.");
-
 #define INIT_LIBRARY(index, name, source, patch)                               \
   { index,                                                                     \
     "dart:"#name, source,                                                      \
@@ -148,11 +146,6 @@
 
 
 static RawError* Compile(const Library& library, const Script& script) {
-  if (FLAG_print_bootstrap) {
-    OS::Print("Bootstrap source '%s':\n%s\n",
-              String::Handle(script.url()).ToCString(),
-              String::Handle(script.Source()).ToCString());
-  }
   bool update_lib_status = (script.kind() == RawScript::kScriptTag ||
                             script.kind() == RawScript::kLibraryTag);
   if (update_lib_status) {
diff --git a/runtime/vm/bootstrap_nocorelib.cc b/runtime/vm/bootstrap_nocorelib.cc
index 99b2780..d2aa1e3 100644
--- a/runtime/vm/bootstrap_nocorelib.cc
+++ b/runtime/vm/bootstrap_nocorelib.cc
@@ -6,14 +6,10 @@
 
 #include "include/dart_api.h"
 
-#include "vm/dart_api_impl.h"
 #include "vm/object.h"
-#include "vm/object_store.h"
 
 namespace dart {
 
-DEFINE_FLAG(bool, print_bootstrap, false, "Print the bootstrap source.");
-
 
 RawError* Bootstrap::LoadandCompileScripts() {
   UNREACHABLE();
diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc
index 6acff6c..446aace 100644
--- a/runtime/vm/dart.cc
+++ b/runtime/vm/dart.cc
@@ -27,7 +27,6 @@
 
 DEFINE_FLAG(bool, heap_profile_initialize, false,
             "Writes a heap profile on isolate initialization.");
-DECLARE_FLAG(bool, print_bootstrap);
 DECLARE_FLAG(bool, print_class_table);
 DECLARE_FLAG(bool, trace_isolates);
 
@@ -146,32 +145,6 @@
 }
 
 
-static void PrintLibrarySources(Isolate* isolate) {
-  const GrowableObjectArray& libs = GrowableObjectArray::Handle(
-      isolate->object_store()->libraries());
-  intptr_t lib_count = libs.Length();
-  Library& lib = Library::Handle();
-  Array& scripts = Array::Handle();
-  Script& script = Script::Handle();
-  String& url = String::Handle();
-  String& source = String::Handle();
-  for (int i = 0; i < lib_count; i++) {
-    lib ^= libs.At(i);
-    url = lib.url();
-    OS::Print("Library %s:\n", url.ToCString());
-    scripts = lib.LoadedScripts();
-    intptr_t script_count = scripts.Length();
-    for (intptr_t i = 0; i < script_count; i++) {
-      script ^= scripts.At(i);
-      url = script.url();
-      source = script.Source();
-      OS::Print("Source for %s:\n", url.ToCString());
-      OS::Print("%s\n", source.ToCString());
-    }
-  }
-}
-
-
 RawError* Dart::InitializeIsolate(const uint8_t* snapshot_buffer, void* data) {
   // Initialize the new isolate.
   TIMERSCOPE(time_isolate_initialization);
@@ -205,9 +178,6 @@
       isolate->heap()->PrintSizes();
       isolate->megamorphic_cache_table()->PrintSizes();
     }
-    if (FLAG_print_bootstrap) {
-      PrintLibrarySources(isolate);
-    }
   }
 
   if (FLAG_heap_profile_initialize) {
diff --git a/runtime/vm/flow_graph_optimizer.cc b/runtime/vm/flow_graph_optimizer.cc
index 3db0a65..d37e602 100644
--- a/runtime/vm/flow_graph_optimizer.cc
+++ b/runtime/vm/flow_graph_optimizer.cc
@@ -1593,18 +1593,36 @@
     ReplaceCall(call, instr);
     return true;
   }
-  if ((recognized_kind == MethodRecognizer::kStringBaseCharAt) &&
-      (ic_data.NumberOfChecks() == 1) &&
-      (class_ids[0] == kOneByteStringCid)) {
-    // TODO(fschneider): Handle TwoByteString.
-    LoadIndexedInstr* load_char_code =
-        BuildStringCodeUnitAt(call, class_ids[0]);
-    InsertBefore(call, load_char_code, NULL, Definition::kValue);
-    StringFromCharCodeInstr* char_at =
-        new StringFromCharCodeInstr(new Value(load_char_code),
-                                    kOneByteStringCid);
-    ReplaceCall(call, char_at);
-    return true;
+  if ((class_ids[0] == kOneByteStringCid) && (ic_data.NumberOfChecks() == 1)) {
+    if (recognized_kind == MethodRecognizer::kStringBaseCharAt) {
+      // TODO(fschneider): Handle TwoByteString.
+      LoadIndexedInstr* load_char_code =
+          BuildStringCodeUnitAt(call, class_ids[0]);
+      InsertBefore(call, load_char_code, NULL, Definition::kValue);
+      StringFromCharCodeInstr* char_at =
+          new StringFromCharCodeInstr(new Value(load_char_code),
+                                      kOneByteStringCid);
+      ReplaceCall(call, char_at);
+      return true;
+    }
+    if (recognized_kind == MethodRecognizer::kOneByteStringSetAt) {
+      // This is an internal method, no need to check argument types nor
+      // range.
+      Definition* str = call->ArgumentAt(0);
+      Definition* index = call->ArgumentAt(1);
+      Definition* value = call->ArgumentAt(2);
+      StoreIndexedInstr* store_op = new StoreIndexedInstr(
+          new Value(str),
+          new Value(index),
+          new Value(value),
+          kNoStoreBarrier,
+          1,  // Index scale
+          kOneByteStringCid,
+          call->deopt_id());
+      ReplaceCall(call, store_op);
+      return true;
+    }
+    return false;
   }
 
   if ((recognized_kind == MethodRecognizer::kIntegerToDouble) &&
diff --git a/runtime/vm/intermediate_language.h b/runtime/vm/intermediate_language.h
index 87220a8..18e2ab3 100644
--- a/runtime/vm/intermediate_language.h
+++ b/runtime/vm/intermediate_language.h
@@ -62,6 +62,7 @@
   V(_StringBase, get:isEmpty, StringBaseIsEmpty, 1026765313)                   \
   V(_StringBase, codeUnitAt, StringBaseCodeUnitAt, 984449525)                  \
   V(_StringBase, [], StringBaseCharAt, 1062366987)                             \
+  V(_OneByteString, _setAt, OneByteStringSetAt, 342452817)                     \
   V(_IntegerImplementation, toDouble, IntegerToDouble, 1267108971)             \
   V(_Double, toInt, DoubleToInteger, 362666636)                                \
   V(_Double, truncateToDouble, DoubleTruncate, 620870996)                      \
diff --git a/runtime/vm/intermediate_language_arm.cc b/runtime/vm/intermediate_language_arm.cc
index b87afd7..e719d84 100644
--- a/runtime/vm/intermediate_language_arm.cc
+++ b/runtime/vm/intermediate_language_arm.cc
@@ -1053,6 +1053,7 @@
   ASSERT(idx == 2);
   switch (class_id_) {
     case kArrayCid:
+    case kOneByteStringCid:
     case kTypedDataInt8ArrayCid:
     case kTypedDataUint8ArrayCid:
     case kExternalTypedDataUint8ArrayCid:
@@ -1097,6 +1098,7 @@
     case kTypedDataInt8ArrayCid:
     case kTypedDataUint8ArrayCid:
     case kTypedDataUint8ClampedArrayCid:
+    case kOneByteStringCid:
       locs->set_in(2, Location::RegisterOrSmiConstant(value()));
       break;
     case kTypedDataInt16ArrayCid:
@@ -1179,7 +1181,8 @@
       break;
     case kTypedDataInt8ArrayCid:
     case kTypedDataUint8ArrayCid:
-    case kExternalTypedDataUint8ArrayCid: {
+    case kExternalTypedDataUint8ArrayCid:
+    case kOneByteStringCid: {
       if (locs()->in(2).IsConstant()) {
         const Smi& constant = Smi::Cast(locs()->in(2).constant());
         __ LoadImmediate(IP, static_cast<int8_t>(constant.Value()));
diff --git a/runtime/vm/intermediate_language_ia32.cc b/runtime/vm/intermediate_language_ia32.cc
index 42ef7c0..ac0c04f 100644
--- a/runtime/vm/intermediate_language_ia32.cc
+++ b/runtime/vm/intermediate_language_ia32.cc
@@ -1345,6 +1345,7 @@
   ASSERT(idx == 2);
   switch (class_id_) {
     case kArrayCid:
+    case kOneByteStringCid:
     case kTypedDataInt8ArrayCid:
     case kTypedDataUint8ArrayCid:
     case kExternalTypedDataUint8ArrayCid:
@@ -1398,6 +1399,7 @@
     case kTypedDataInt8ArrayCid:
     case kTypedDataUint8ArrayCid:
     case kTypedDataUint8ClampedArrayCid:
+    case kOneByteStringCid:
       // TODO(fschneider): Add location constraint for byte registers (EAX,
       // EBX, ECX, EDX) instead of using a fixed register.
       locs->set_in(2, Location::FixedRegisterOrSmiConstant(value(), EAX));
@@ -1474,6 +1476,7 @@
     case kTypedDataInt8ArrayCid:
     case kTypedDataUint8ArrayCid:
     case kExternalTypedDataUint8ArrayCid:
+    case kOneByteStringCid:
       if (locs()->in(2).IsConstant()) {
         const Smi& constant = Smi::Cast(locs()->in(2).constant());
         __ movb(element_address,
diff --git a/runtime/vm/intermediate_language_mips.cc b/runtime/vm/intermediate_language_mips.cc
index e33d78b..9b3d018 100644
--- a/runtime/vm/intermediate_language_mips.cc
+++ b/runtime/vm/intermediate_language_mips.cc
@@ -1087,6 +1087,7 @@
     case kTypedDataInt8ArrayCid:
     case kTypedDataUint8ArrayCid:
     case kTypedDataUint8ClampedArrayCid:
+    case kOneByteStringCid:
     case kTypedDataInt16ArrayCid:
     case kTypedDataUint16ArrayCid:
     case kTypedDataInt32ArrayCid:
@@ -1166,6 +1167,7 @@
     case kExternalTypedDataUint8ArrayCid:
     case kTypedDataUint8ClampedArrayCid:
     case kExternalTypedDataUint8ClampedArrayCid:
+    case kOneByteStringCid:
     case kTypedDataInt16ArrayCid:
     case kTypedDataUint16ArrayCid:
     case kTypedDataInt32ArrayCid:
diff --git a/runtime/vm/intermediate_language_x64.cc b/runtime/vm/intermediate_language_x64.cc
index d01c003..c79b1e4 100644
--- a/runtime/vm/intermediate_language_x64.cc
+++ b/runtime/vm/intermediate_language_x64.cc
@@ -1328,6 +1328,7 @@
   ASSERT(idx == 2);
   switch (class_id_) {
     case kArrayCid:
+    case kOneByteStringCid:
     case kTypedDataInt8ArrayCid:
     case kTypedDataUint8ArrayCid:
     case kExternalTypedDataUint8ArrayCid:
@@ -1380,6 +1381,7 @@
     case kTypedDataInt8ArrayCid:
     case kTypedDataUint8ArrayCid:
     case kTypedDataUint8ClampedArrayCid:
+    case kOneByteStringCid:
       // TODO(fschneider): Add location constraint for byte registers (RAX,
       // RBX, RCX, RDX) instead of using a fixed register.
       locs->set_in(2, Location::FixedRegisterOrSmiConstant(value(), RAX));
@@ -1452,6 +1454,7 @@
     case kTypedDataInt8ArrayCid:
     case kTypedDataUint8ArrayCid:
     case kExternalTypedDataUint8ArrayCid:
+    case kOneByteStringCid:
       if (locs()->in(2).IsConstant()) {
         const Smi& constant = Smi::Cast(locs()->in(2).constant());
         __ movb(element_address,
diff --git a/runtime/vm/unit_test.cc b/runtime/vm/unit_test.cc
index 09f3df6..bbde54f 100644
--- a/runtime/vm/unit_test.cc
+++ b/runtime/vm/unit_test.cc
@@ -63,11 +63,19 @@
   if (Dart_IsError(result)) {
     return Dart_Error("accessing url characters failed");
   }
+  Dart_Handle library_url = Dart_LibraryUrl(library);
+  const char* library_url_string = NULL;
+  result = Dart_StringToCString(library_url, &library_url_string);
+  if (Dart_IsError(result)) {
+    return result;
+  }
+
   bool is_dart_scheme_url = DartUtils::IsDartSchemeURL(url_chars);
+  bool is_io_library = DartUtils::IsDartIOLibURL(library_url_string);
   if (tag == kCanonicalizeUrl) {
     // If this is a Dart Scheme URL then it is not modified as it will be
     // handled by the VM internally.
-    if (is_dart_scheme_url) {
+    if (is_dart_scheme_url || is_io_library) {
       return url;
     }
     Dart_Handle builtin_lib =
@@ -86,6 +94,13 @@
       return Dart_Error("Do not know how to load '%s'", url_chars);
     }
   }
+  if (is_io_library) {
+    ASSERT(tag == kSourceTag);
+    return Dart_LoadSource(library,
+                           url,
+                           Builtin::PartSource(Builtin::kIOLibrary,
+                                               url_chars));
+  }
   return DartUtils::LoadSource(NULL,
                                library,
                                url,
diff --git a/runtime/vm/vm.gypi b/runtime/vm/vm.gypi
index 9e073d1..058f5ce 100644
--- a/runtime/vm/vm.gypi
+++ b/runtime/vm/vm.gypi
@@ -54,7 +54,6 @@
           'link_settings': {
             'libraries': [
               '-lc',
-              '-lpthread',
             ],
           },
         }],
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/builder.dart b/sdk/lib/_internal/compiler/implementation/ssa/builder.dart
index ba26fe2..487104c 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/builder.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/builder.dart
@@ -274,7 +274,8 @@
     });
     if (closureData.isClosure()) {
       // Inside closure redirect references to itself to [:this:].
-      HThis thisInstruction = new HThis(closureData.thisElement);
+      HThis thisInstruction = new HThis(closureData.thisElement,
+                                        HType.NON_NULL);
       builder.graph.thisInstruction = thisInstruction;
       builder.graph.entry.addAtEntry(thisInstruction);
       updateLocal(closureData.closureElement, thisInstruction);
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart b/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart
index 98b2725..c16dcef 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart
@@ -1173,7 +1173,7 @@
     while (instruction != null) {
       HInstruction next = instruction.next;
       if (instruction.useGvn()
-          && (instruction is !HCheck)
+          && !instruction.canThrow()
           && !instruction.sideEffects.dependsOn(dependsFlags)) {
         bool loopInvariantInputs = true;
         List<HInstruction> inputs = instruction.inputs;
diff --git a/sdk/lib/_internal/compiler/implementation/typechecker.dart b/sdk/lib/_internal/compiler/implementation/typechecker.dart
index 62de10d..9272289 100644
--- a/sdk/lib/_internal/compiler/implementation/typechecker.dart
+++ b/sdk/lib/_internal/compiler/implementation/typechecker.dart
@@ -240,8 +240,11 @@
   }
 
   DartType visitExpressionStatement(ExpressionStatement node) {
-    analyze(node.expression);
-    return StatementType.NOT_RETURNING;
+    Expression expression = node.expression;
+    analyze(expression);
+    return (expression.asThrow() != null)
+        ? StatementType.RETURNING
+        : StatementType.NOT_RETURNING;
   }
 
   /** Dart Programming Language Specification: 11.5.1 For Loop */
@@ -630,7 +633,7 @@
 
   DartType visitThrow(Throw node) {
     analyze(node.expression);
-    return StatementType.RETURNING;
+    return types.dynamicType;
   }
 
   DartType computeType(Element element) {
diff --git a/sdk/lib/_internal/dartdoc/lib/src/export_map.dart b/sdk/lib/_internal/dartdoc/lib/src/export_map.dart
index 4e892cc..512991d 100644
--- a/sdk/lib/_internal/dartdoc/lib/src/export_map.dart
+++ b/sdk/lib/_internal/dartdoc/lib/src/export_map.dart
@@ -11,11 +11,7 @@
 import 'dart:io';
 import 'dart:uri';
 
-import 'package:analyzer_experimental/src/generated/ast.dart';
-import 'package:analyzer_experimental/src/generated/error.dart';
-import 'package:analyzer_experimental/src/generated/parser.dart';
-import 'package:analyzer_experimental/src/generated/scanner.dart';
-import 'package:analyzer_experimental/src/generated/source.dart';
+import 'package:analyzer_experimental/analyzer.dart';
 import 'package:pathos/path.dart' as pathos;
 
 import 'dartdoc/utils.dart';
@@ -253,7 +249,7 @@
 Pair<List<String>, List<Export>> _importsAndExportsForFile(String file,
     String packageRoot) {
   var collector = new _ImportExportCollector();
-  _parseFile(file).accept(collector);
+  parseDartFile(file).accept(collector);
 
   var imports = collector.imports.map((import) {
     return _pathForDirective(import, pathos.dirname(file), packageRoot);
@@ -285,39 +281,12 @@
 /// [packageRoot] is the path from which `package:` imports should be resolved.
 String _pathForDirective(NamespaceDirective directive, String basePath,
     String packageRoot) {
-  var uri = Uri.parse(_stringLiteralToString(directive.uri));
+  var uri = Uri.parse(stringLiteralToString(directive.uri));
   var path = importUriToPath(uri, basePath: basePath, packageRoot: packageRoot);
   if (path == null) return null;
   return pathos.normalize(pathos.absolute(path));
 }
 
-/// Parses a Dart file into an AST.
-CompilationUnit _parseFile(String path) {
-  var contents = new File(path).readAsStringSync();
-  var errorCollector = new _ErrorCollector();
-  var scanner = new StringScanner(null, contents, errorCollector);
-  var token = scanner.tokenize();
-  var parser = new Parser(null, errorCollector);
-  var unit = parser.parseCompilationUnit(token);
-  unit.lineInfo = new LineInfo(scanner.lineStarts);
-
-  if (!errorCollector.errors.isEmpty) {
-    throw new FormatException(
-        errorCollector.errors.map((e) => e.toString()).join("\n"));
-  }
-
-  return unit;
-}
-
-/// A simple error listener that collects errors into a list.
-class _ErrorCollector extends AnalysisErrorListener {
-  final errors = <AnalysisError>[];
-
-  _ErrorCollector();
-
-  void onError(AnalysisError error) => errors.add(error);
-}
-
 /// A simple visitor that collects import and export nodes.
 class _ImportExportCollector extends GeneralizingASTVisitor {
   final imports = <ImportDirective>[];
@@ -328,15 +297,3 @@
   visitImportDirective(ImportDirective node) => imports.add(node);
   visitExportDirective(ExportDirective node) => exports.add(node);
 }
-
-// TODO(nweiz): fold this into the analyzer (issue 9781).
-/// Converts an AST node representing a string literal into a [String].
-String _stringLiteralToString(StringLiteral literal) {
-  if (literal is AdjacentStrings) {
-    return literal.strings.map(_stringLiteralToString).join();
-  } else if (literal is SimpleStringLiteral) {
-    return literal.value;
-  } else {
-    throw new ArgumentError('Unknown string type for $literal');
-  }
-}
diff --git a/sdk/lib/_internal/libraries.dart b/sdk/lib/_internal/libraries.dart
index 551ddab..ea69f77 100644
--- a/sdk/lib/_internal/libraries.dart
+++ b/sdk/lib/_internal/libraries.dart
@@ -86,6 +86,12 @@
       documented: false,
       platforms: VM_PLATFORM),
 
+  "mdv_observe_impl": const LibraryInfo(
+      "mdv_observe_impl/mdv_observe_impl.dart",
+      category: "Client",
+      documented: false,
+      implementation: true),
+
   "typed_data": const LibraryInfo(
       "typed_data/typed_data.dart",
       dart2jsPath: "typed_data/dart2js/typed_data_dart2js.dart"),
diff --git a/sdk/lib/_internal/pub/lib/src/utils.dart b/sdk/lib/_internal/pub/lib/src/utils.dart
index a3f0f97..80a8ec3 100644
--- a/sdk/lib/_internal/pub/lib/src/utils.dart
+++ b/sdk/lib/_internal/pub/lib/src/utils.dart
@@ -433,7 +433,7 @@
     error is HttpException ||
     error is HttpParserException ||
     error is LinkIOException ||
-    error is MimeParserException ||
+    error is MimeMultipartException ||
     error is OSError ||
     error is ProcessException ||
     error is SocketIOException ||
diff --git a/sdk/lib/html/dart2js/html_dart2js.dart b/sdk/lib/html/dart2js/html_dart2js.dart
index bd7050c..0e2dbb5 100644
--- a/sdk/lib/html/dart2js/html_dart2js.dart
+++ b/sdk/lib/html/dart2js/html_dart2js.dart
@@ -3,12 +3,13 @@
 
 import 'dart:async';
 import 'dart:collection';
-import 'dart:_collection-dev';
+import 'dart:_collection-dev' hide Symbol;
 import 'dart:html_common';
 import 'dart:indexed_db';
 import 'dart:isolate';
 import 'dart:json' as json;
 import 'dart:math';
+import 'dart:mdv_observe_impl';
 import 'dart:typed_data';
 import 'dart:svg' as svg;
 import 'dart:web_audio' as web_audio;
@@ -192,11 +193,11 @@
 @Experimental
 class AnimationEvent extends Event native "WebKitAnimationEvent" {
 
-  @DomName('AnimationEvent.animationName')
+  @DomName('WebKitAnimationEvent.animationName')
   @DocsEditable
   final String animationName;
 
-  @DomName('AnimationEvent.elapsedTime')
+  @DomName('WebKitAnimationEvent.elapsedTime')
   @DocsEditable
   final num elapsedTime;
 }
@@ -2104,11 +2105,6 @@
   @DocsEditable
   final CssRule parentRule;
 
-  @JSName('getPropertyValue')
-  @DomName('CSSStyleDeclaration.getPropertyValue')
-  @DocsEditable
-  String _getPropertyValue(String propertyName) native;
-
   @DomName('CSSStyleDeclaration.getPropertyPriority')
   @DocsEditable
   String getPropertyPriority(String propertyName) native;
@@ -2117,6 +2113,11 @@
   @DocsEditable
   String getPropertyShorthand(String propertyName) native;
 
+  @JSName('getPropertyValue')
+  @DomName('CSSStyleDeclaration.getPropertyValue')
+  @DocsEditable
+  String _getPropertyValue(String propertyName) native;
+
   @DomName('CSSStyleDeclaration.isPropertyImplicit')
   @DocsEditable
   bool isPropertyImplicit(String propertyName) native;
@@ -6037,6 +6038,8 @@
   @DocsEditable
   @Creates('Window|=Object')
   @Returns('Window|=Object')
+  @Creates('Window|=Object|Null')
+  @Returns('Window|=Object|Null')
   final dynamic _get_window;
 
   @DomName('Document.documentElement')
@@ -6643,7 +6646,7 @@
     e.innerHtml = value;
 
     // Copy list first since we don't want liveness during iteration.
-    List nodes = new List.from(e.nodes);
+    List nodes = new List.from(e.nodes, growable: false);
     this.nodes.addAll(nodes);
   }
 
@@ -6741,11 +6744,11 @@
     return errorName;
   }
 
-  @DomName('DOMCoreException.message')
+  @DomName('DOMException.message')
   @DocsEditable
   final String message;
 
-  @DomName('DOMCoreException.toString')
+  @DomName('DOMException.toString')
   @DocsEditable
   String toString() native;
 
@@ -6821,7 +6824,7 @@
 
 @DocsEditable
 @DomName('DOMStringList')
-class DomStringList implements JavaScriptIndexingBehavior, List<String> native "DOMStringList" {
+class DomStringList extends Object with ListMixin<String>, ImmutableListMixin<String> implements JavaScriptIndexingBehavior, List<String> native "DOMStringList" {
 
   @DomName('DOMStringList.length')
   @DocsEditable
@@ -6835,196 +6838,11 @@
   // -- start List<String> mixins.
   // String is the element type.
 
-  // From Iterable<String>:
 
-  Iterator<String> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<String>(this);
-  }
-
-  String reduce(String combine(String value, String element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, String element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  // contains() defined by IDL.
-
-  void forEach(void f(String element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(String element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<String> where(bool f(String element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(String element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(String element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(String element)) => IterableMixinWorkaround.any(this, f);
-
-  List<String> toList({ bool growable: true }) =>
-      new List<String>.from(this, growable: growable);
-
-  Set<String> toSet() => new Set<String>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<String> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<String> takeWhile(bool test(String value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<String> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<String> skipWhile(bool test(String value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  String firstWhere(bool test(String value), { String orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  String lastWhere(bool test(String value), {String orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  String singleWhere(bool test(String value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  String elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<String>:
-
-  void add(String value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<String> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<String>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<String> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(String a, String b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(String element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(String element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  String get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  String get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  String get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, String element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<String> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<String> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  String removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  String removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(String element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(String element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<String> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<String> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [String fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<String> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<String> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <String>[]);
-  }
-
-  Map<int, String> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<String> mixins.
 
   @DomName('DOMStringList.contains')
@@ -7210,7 +7028,7 @@
   }
 }
 
-/** 
+/**
  * An immutable list containing HTML elements. This list contains some
  * additional methods for ease of CSS manipulation on a group of elements.
  */
@@ -7692,6 +7510,255 @@
     throw new UnsupportedError("Not supported on this platform");
   }
 
+  @Creates('Null')
+  Map<String, StreamSubscription> _attributeBindings;
+
+  // TODO(jmesserly): I'm concerned about adding these to every element.
+  // Conceptually all of these belong on TemplateElement. They are here to
+  // support browsers that don't have <template> yet.
+  // However even in the polyfill they're restricted to certain tags
+  // (see [isTemplate]). So we can probably convert it to a (public) mixin, and
+  // only mix it in to the elements that need it.
+  @Creates('Null')  // Set from Dart code; does not instantiate a native type.
+  var _model;
+
+  @Creates('Null')  // Set from Dart code; does not instantiate a native type.
+  _TemplateIterator _templateIterator;
+
+  @Creates('Null')  // Set from Dart code; does not instantiate a native type.
+  Element _templateInstanceRef;
+
+  // Note: only used if `this is! TemplateElement`
+  @Creates('Null')  // Set from Dart code; does not instantiate a native type.
+  DocumentFragment _templateContent;
+
+  bool _templateIsDecorated;
+
+  // TODO(jmesserly): should path be optional, and default to empty path?
+  // It is used that way in at least one path in JS TemplateElement tests
+  // (see "BindImperative" test in original JS code).
+  @Experimental
+  void bind(String name, model, String path) {
+    _bindElement(this, name, model, path);
+  }
+
+  // TODO(jmesserly): this is static to work around http://dartbug.com/10166
+  // Similar issue for unbind/unbindAll below.
+  static void _bindElement(Element self, String name, model, String path) {
+    if (self._bindTemplate(name, model, path)) return;
+
+    if (self._attributeBindings == null) {
+      self._attributeBindings = new Map<String, StreamSubscription>();
+    }
+
+    self.attributes.remove(name);
+
+    var changed;
+    if (name.endsWith('?')) {
+      name = name.substring(0, name.length - 1);
+
+      changed = (value) {
+        if (_templateBooleanConversion(value)) {
+          self.attributes[name] = '';
+        } else {
+          self.attributes.remove(name);
+        }
+      };
+    } else {
+      changed = (value) {
+        // TODO(jmesserly): escape value if needed to protect against XSS.
+        // See https://github.com/toolkitchen/mdv/issues/58
+        self.attributes[name] = value == null ? '' : '$value';
+      };
+    }
+
+    self.unbind(name);
+
+    self._attributeBindings[name] =
+        new PathObserver(model, path).bindSync(changed);
+  }
+
+  @Experimental
+  void unbind(String name) {
+    _unbindElement(this, name);
+  }
+
+  static _unbindElement(Element self, String name) {
+    if (self._unbindTemplate(name)) return;
+    if (self._attributeBindings != null) {
+      var binding = self._attributeBindings.remove(name);
+      if (binding != null) binding.cancel();
+    }
+  }
+
+  @Experimental
+  void unbindAll() {
+    _unbindAllElement(this);
+  }
+
+  static void _unbindAllElement(Element self) {
+    self._unbindAllTemplate();
+
+    if (self._attributeBindings != null) {
+      for (var binding in self._attributeBindings.values) {
+        binding.cancel();
+      }
+      self._attributeBindings = null;
+    }
+  }
+
+  // TODO(jmesserly): unlike the JS polyfill, we can't mixin
+  // HTMLTemplateElement at runtime into things that are semantically template
+  // elements. So instead we implement it here with a runtime check.
+  // If the bind succeeds, we return true, otherwise we return false and let
+  // the normal Element.bind logic kick in.
+  bool _bindTemplate(String name, model, String path) {
+    if (isTemplate) {
+      switch (name) {
+        case 'bind':
+        case 'repeat':
+        case 'if':
+          _ensureTemplate();
+          if (_templateIterator == null) {
+            _templateIterator = new _TemplateIterator(this);
+          }
+          _templateIterator.inputs.bind(name, model, path);
+          return true;
+      }
+    }
+    return false;
+  }
+
+  bool _unbindTemplate(String name) {
+    if (isTemplate) {
+      switch (name) {
+        case 'bind':
+        case 'repeat':
+        case 'if':
+          _ensureTemplate();
+          if (_templateIterator != null) {
+            _templateIterator.inputs.unbind(name);
+          }
+          return true;
+      }
+    }
+    return false;
+  }
+
+  void _unbindAllTemplate() {
+    if (isTemplate) {
+      unbind('bind');
+      unbind('repeat');
+      unbind('if');
+    }
+  }
+
+  /**
+   * Gets the template this node refers to.
+   * This is only supported if [isTemplate] is true.
+   */
+  @Experimental
+  Element get ref {
+    _ensureTemplate();
+
+    Element ref = null;
+    var refId = attributes['ref'];
+    if (refId != null) {
+      ref = document.getElementById(refId);
+    }
+
+    return ref != null ? ref : _templateInstanceRef;
+  }
+
+  /**
+   * Gets the content of this template.
+   * This is only supported if [isTemplate] is true.
+   */
+  @Experimental
+  DocumentFragment get content {
+    _ensureTemplate();
+    return _templateContent;
+  }
+
+  /**
+   * Creates an instance of the template.
+   * This is only supported if [isTemplate] is true.
+   */
+  @Experimental
+  DocumentFragment createInstance() {
+    _ensureTemplate();
+
+    var template = ref;
+    if (template == null) template = this;
+
+    var instance = _createDeepCloneAndDecorateTemplates(template.content,
+        attributes['syntax']);
+
+    if (TemplateElement._instanceCreated != null) {
+      TemplateElement._instanceCreated.add(instance);
+    }
+    return instance;
+  }
+
+  /**
+   * The data model which is inherited through the tree.
+   * This is only supported if [isTemplate] is true.
+   *
+   * Setting this will destructive propagate the value to all descendant nodes,
+   * and reinstantiate all of the nodes expanded by this template.
+   *
+   * Currently this does not support propagation through Shadow DOMs.
+   */
+  @Experimental
+  get model => _model;
+
+  @Experimental
+  void set model(value) {
+    _ensureTemplate();
+
+    _model = value;
+    _addBindings(this, model);
+  }
+
+  // TODO(jmesserly): const set would be better
+  static const _TABLE_TAGS = const {
+    'caption': null,
+    'col': null,
+    'colgroup': null,
+    'tbody': null,
+    'td': null,
+    'tfoot': null,
+    'th': null,
+    'thead': null,
+    'tr': null,
+  };
+
+  bool get _isAttributeTemplate => attributes.containsKey('template') &&
+      (localName == 'option' || _TABLE_TAGS.containsKey(localName));
+
+  /**
+   * Returns true if this node is a template.
+   *
+   * A node is a template if [tagName] is TEMPLATE, or the node has the
+   * 'template' attribute and this tag supports attribute form for backwards
+   * compatibility with existing HTML parsers. The nodes that can use attribute
+   * form are table elments (THEAD, TBODY, TFOOT, TH, TR, TD, CAPTION, COLGROUP
+   * and COL) and OPTION.
+   */
+  // TODO(jmesserly): this is not a public MDV API, but it seems like a useful
+  // place to document which tags our polyfill considers to be templates.
+  // Otherwise I'd be repeating it in several other places.
+  // See if we can replace this with a TemplateMixin.
+  @Experimental
+  bool get isTemplate => tagName == 'TEMPLATE' || _isAttributeTemplate;
+
+  void _ensureTemplate() {
+    if (!isTemplate) {
+      throw new UnsupportedError('$this is not a template.');
+    }
+    TemplateElement.decorate(this);
+  }
+
 
   @DomName('Element.abortEvent')
   @DocsEditable
@@ -8436,6 +8503,7 @@
 
 }
 
+
 final _START_TAG_REGEXP = new RegExp('<(\\w+)');
 class _ElementFactoryProvider {
   static const _CUSTOM_PARENT_TAG_MAP = const {
@@ -8453,19 +8521,6 @@
     'track' : 'audio',
   };
 
-  // TODO(jmesserly): const set would be better
-  static const _TABLE_TAGS = const {
-    'caption': null,
-    'col': null,
-    'colgroup': null,
-    'tbody': null,
-    'td': null,
-    'tfoot': null,
-    'th': null,
-    'thead': null,
-    'tr': null,
-  };
-
   @DomName('Document.createElement')
   static Element createElement_html(String html) {
     // TODO(jacobr): this method can be made more robust and performant.
@@ -8479,7 +8534,7 @@
     final match = _START_TAG_REGEXP.firstMatch(html);
     if (match != null) {
       tag = match.group(1).toLowerCase();
-      if (Device.isIE && _TABLE_TAGS.containsKey(tag)) {
+      if (Device.isIE && Element._TABLE_TAGS.containsKey(tag)) {
         return _createTableForIE(html, tag);
       }
       parentTag = _CUSTOM_PARENT_TAG_MAP[tag];
@@ -9372,7 +9427,7 @@
 
 @DocsEditable
 @DomName('FileList')
-class FileList implements JavaScriptIndexingBehavior, List<File> native "FileList" {
+class FileList extends Object with ListMixin<File>, ImmutableListMixin<File> implements JavaScriptIndexingBehavior, List<File> native "FileList" {
 
   @DomName('FileList.length')
   @DocsEditable
@@ -9386,196 +9441,11 @@
   // -- start List<File> mixins.
   // File is the element type.
 
-  // From Iterable<File>:
 
-  Iterator<File> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<File>(this);
-  }
-
-  File reduce(File combine(File value, File element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, File element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(File element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(File element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(File element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<File> where(bool f(File element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(File element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(File element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(File element)) => IterableMixinWorkaround.any(this, f);
-
-  List<File> toList({ bool growable: true }) =>
-      new List<File>.from(this, growable: growable);
-
-  Set<File> toSet() => new Set<File>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<File> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<File> takeWhile(bool test(File value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<File> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<File> skipWhile(bool test(File value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  File firstWhere(bool test(File value), { File orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  File lastWhere(bool test(File value), {File orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  File singleWhere(bool test(File value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  File elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<File>:
-
-  void add(File value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<File> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<File>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<File> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(File a, File b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(File element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(File element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  File get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  File get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  File get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, File element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<File> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<File> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  File removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  File removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(File element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(File element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<File> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<File> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [File fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<File> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<File> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <File>[]);
-  }
-
-  Map<int, File> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<File> mixins.
 
   @DomName('FileList.item')
@@ -9938,7 +9808,7 @@
 @SupportedBrowser(SupportedBrowser.SAFARI)
 class FormData native "FormData" {
 
-  @DomName('DOMFormData.DOMFormData')
+  @DomName('FormData.DOMFormData')
   @DocsEditable
   factory FormData([FormElement form]) {
     if (?form) {
@@ -9952,7 +9822,7 @@
   /// Checks if this type is supported on the current platform.
   static bool get supported => JS('bool', '!!(window.FormData)');
 
-  @DomName('DOMFormData.append')
+  @DomName('FormData.append')
   @DocsEditable
   void append(String name, value, [String filename]) native;
 }
@@ -10354,7 +10224,7 @@
 
 @DocsEditable
 @DomName('HTMLAllCollection')
-class HtmlAllCollection implements JavaScriptIndexingBehavior, List<Node> native "HTMLAllCollection" {
+class HtmlAllCollection extends Object with ListMixin<Node>, ImmutableListMixin<Node> implements JavaScriptIndexingBehavior, List<Node> native "HTMLAllCollection" {
 
   @DomName('HTMLAllCollection.length')
   @DocsEditable
@@ -10368,196 +10238,11 @@
   // -- start List<Node> mixins.
   // Node is the element type.
 
-  // From Iterable<Node>:
 
-  Iterator<Node> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<Node>(this);
-  }
-
-  Node reduce(Node combine(Node value, Node element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, Node element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(Node element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(Node element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(Node element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<Node> where(bool f(Node element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(Node element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(Node element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(Node element)) => IterableMixinWorkaround.any(this, f);
-
-  List<Node> toList({ bool growable: true }) =>
-      new List<Node>.from(this, growable: growable);
-
-  Set<Node> toSet() => new Set<Node>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<Node> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<Node> takeWhile(bool test(Node value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<Node> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<Node> skipWhile(bool test(Node value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  Node firstWhere(bool test(Node value), { Node orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  Node lastWhere(bool test(Node value), {Node orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  Node singleWhere(bool test(Node value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  Node elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<Node>:
-
-  void add(Node value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<Node>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<Node> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(Node a, Node b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(Node element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(Node element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  Node get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  Node get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  Node get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, Node element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Node removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  Node removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(Node element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(Node element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<Node> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [Node fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<Node> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<Node> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <Node>[]);
-  }
-
-  Map<int, Node> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<Node> mixins.
 
   @DomName('HTMLAllCollection.item')
@@ -10581,7 +10266,7 @@
 
 @DocsEditable
 @DomName('HTMLCollection')
-class HtmlCollection implements JavaScriptIndexingBehavior, List<Node> native "HTMLCollection" {
+class HtmlCollection extends Object with ListMixin<Node>, ImmutableListMixin<Node> implements JavaScriptIndexingBehavior, List<Node> native "HTMLCollection" {
 
   @DomName('HTMLCollection.length')
   @DocsEditable
@@ -10595,196 +10280,11 @@
   // -- start List<Node> mixins.
   // Node is the element type.
 
-  // From Iterable<Node>:
 
-  Iterator<Node> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<Node>(this);
-  }
-
-  Node reduce(Node combine(Node value, Node element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, Node element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(Node element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(Node element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(Node element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<Node> where(bool f(Node element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(Node element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(Node element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(Node element)) => IterableMixinWorkaround.any(this, f);
-
-  List<Node> toList({ bool growable: true }) =>
-      new List<Node>.from(this, growable: growable);
-
-  Set<Node> toSet() => new Set<Node>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<Node> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<Node> takeWhile(bool test(Node value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<Node> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<Node> skipWhile(bool test(Node value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  Node firstWhere(bool test(Node value), { Node orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  Node lastWhere(bool test(Node value), {Node orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  Node singleWhere(bool test(Node value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  Node elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<Node>:
-
-  void add(Node value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<Node>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<Node> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(Node a, Node b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(Node element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(Node element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  Node get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  Node get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  Node get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, Node element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Node removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  Node removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(Node element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(Node element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<Node> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [Node fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<Node> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<Node> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <Node>[]);
-  }
-
-  Map<int, Node> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<Node> mixins.
 
   @DomName('HTMLCollection.item')
@@ -10952,6 +10452,11 @@
   @SupportedBrowser(SupportedBrowser.SAFARI)
   @Experimental
   String get visibilityState => $dom_webkitVisibilityState;
+
+
+  @Creates('Null')  // Set from Dart code; does not instantiate a native type.
+  // Note: used to polyfill <template>
+  Document _templateContentsOwner;
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -11788,6 +11293,62 @@
     return e;
   }
 
+  @Creates('Null')  // Set from Dart code; does not instantiate a native type.
+  _ValueBinding _valueBinding;
+
+  @Creates('Null')  // Set from Dart code; does not instantiate a native type.
+  _CheckedBinding _checkedBinding;
+
+  @Experimental
+  void bind(String name, model, String path) {
+    switch (name) {
+      case 'value':
+        unbind('value');
+        attributes.remove('value');
+        _valueBinding = new _ValueBinding(this, model, path);
+        break;
+      case 'checked':
+        unbind('checked');
+        attributes.remove('checked');
+        _checkedBinding = new _CheckedBinding(this, model, path);
+        break;
+      default:
+        // TODO(jmesserly): this should be "super" (http://dartbug.com/10166).
+        // Similar issue for unbind/unbindAll below.
+        Element._bindElement(this, name, model, path);
+        break;
+    }
+  }
+
+  @Experimental
+  void unbind(String name) {
+    switch (name) {
+      case 'value':
+        if (_valueBinding != null) {
+          _valueBinding.unbind();
+          _valueBinding = null;
+        }
+        break;
+      case 'checked':
+        if (_checkedBinding != null) {
+          _checkedBinding.unbind();
+          _checkedBinding = null;
+        }
+        break;
+      default:
+        Element._unbindElement(this, name);
+        break;
+    }
+  }
+
+  @Experimental
+  void unbindAll() {
+    unbind('value');
+    unbind('checked');
+    Element._unbindAllElement(this);
+  }
+
+
   @DomName('HTMLInputElement.webkitSpeechChangeEvent')
   @DocsEditable
   @SupportedBrowser(SupportedBrowser.CHROME)
@@ -12058,8 +11619,8 @@
 
 
 // Interfaces representing the InputElement APIs which are supported
-// for the various types of InputElement.
-// From http://dev.w3.org/html5/spec/the-input-element.html#the-input-element.
+// for the various types of InputElement. From:
+// http://www.w3.org/html/wg/drafts/html/master/forms.html#the-input-element.
 
 /**
  * Exposes the functionality common between all InputElement types.
@@ -12105,7 +11666,7 @@
 /**
  * Hidden input which is not intended to be seen or edited by the user.
  */
-abstract class HiddenInputElement implements Element {
+abstract class HiddenInputElement implements InputElementBase {
   factory HiddenInputElement() => new InputElement(type: 'hidden');
 }
 
@@ -13567,9 +13128,7 @@
 
   @DomName('MediaKeyEvent.initData')
   @DocsEditable
-  @Returns('Uint8List')
-  @Creates('Uint8List')
-  final List<int> initData;
+  final Uint8List initData;
 
   @DomName('MediaKeyEvent.keySystem')
   @DocsEditable
@@ -13577,9 +13136,7 @@
 
   @DomName('MediaKeyEvent.message')
   @DocsEditable
-  @Returns('Uint8List')
-  @Creates('Uint8List')
-  final List<int> message;
+  final Uint8List message;
 
   @DomName('MediaKeyEvent.sessionId')
   @DocsEditable
@@ -14225,19 +13782,19 @@
 @DomName('MimeType')
 class MimeType native "MimeType" {
 
-  @DomName('DOMMimeType.description')
+  @DomName('MimeType.description')
   @DocsEditable
   final String description;
 
-  @DomName('DOMMimeType.enabledPlugin')
+  @DomName('MimeType.enabledPlugin')
   @DocsEditable
   final Plugin enabledPlugin;
 
-  @DomName('DOMMimeType.suffixes')
+  @DomName('MimeType.suffixes')
   @DocsEditable
   final String suffixes;
 
-  @DomName('DOMMimeType.type')
+  @DomName('MimeType.type')
   @DocsEditable
   final String type;
 }
@@ -14248,9 +13805,9 @@
 
 @DocsEditable
 @DomName('MimeTypeArray')
-class MimeTypeArray implements JavaScriptIndexingBehavior, List<MimeType> native "MimeTypeArray" {
+class MimeTypeArray extends Object with ListMixin<MimeType>, ImmutableListMixin<MimeType> implements JavaScriptIndexingBehavior, List<MimeType> native "MimeTypeArray" {
 
-  @DomName('DOMMimeTypeArray.length')
+  @DomName('MimeTypeArray.length')
   @DocsEditable
   int get length => JS("int", "#.length", this);
 
@@ -14262,203 +13819,18 @@
   // -- start List<MimeType> mixins.
   // MimeType is the element type.
 
-  // From Iterable<MimeType>:
 
-  Iterator<MimeType> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<MimeType>(this);
-  }
-
-  MimeType reduce(MimeType combine(MimeType value, MimeType element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, MimeType element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(MimeType element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(MimeType element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(MimeType element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<MimeType> where(bool f(MimeType element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(MimeType element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(MimeType element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(MimeType element)) => IterableMixinWorkaround.any(this, f);
-
-  List<MimeType> toList({ bool growable: true }) =>
-      new List<MimeType>.from(this, growable: growable);
-
-  Set<MimeType> toSet() => new Set<MimeType>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<MimeType> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<MimeType> takeWhile(bool test(MimeType value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<MimeType> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<MimeType> skipWhile(bool test(MimeType value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  MimeType firstWhere(bool test(MimeType value), { MimeType orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  MimeType lastWhere(bool test(MimeType value), {MimeType orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  MimeType singleWhere(bool test(MimeType value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  MimeType elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<MimeType>:
-
-  void add(MimeType value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<MimeType> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<MimeType>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<MimeType> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(MimeType a, MimeType b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(MimeType element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(MimeType element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  MimeType get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  MimeType get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  MimeType get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, MimeType element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<MimeType> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<MimeType> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  MimeType removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  MimeType removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(MimeType element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(MimeType element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<MimeType> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<MimeType> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [MimeType fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<MimeType> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<MimeType> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <MimeType>[]);
-  }
-
-  Map<int, MimeType> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<MimeType> mixins.
 
-  @DomName('DOMMimeTypeArray.item')
+  @DomName('MimeTypeArray.item')
   @DocsEditable
   MimeType item(int index) native;
 
-  @DomName('DOMMimeTypeArray.namedItem')
+  @DomName('MimeTypeArray.namedItem')
   @DocsEditable
   MimeType namedItem(String name) native;
 }
@@ -14719,6 +14091,10 @@
 @Experimental
 class MutationObserver native "MutationObserver,WebKitMutationObserver" {
 
+  @DomName('MutationObserver.disconnect')
+  @DocsEditable
+  void disconnect() native;
+
   @DomName('MutationObserver.observe')
   @DocsEditable
   void _observe(Node target, Map options) {
@@ -14731,10 +14107,6 @@
   @DocsEditable
   void __observe_1(Node target, options) native;
 
-  @DomName('MutationObserver.disconnect')
-  @DocsEditable
-  void disconnect() native;
-
   @DomName('MutationObserver.takeRecords')
   @DocsEditable
   List<MutationRecord> takeRecords() native;
@@ -14867,47 +14239,47 @@
 @Experimental
 class NamedFlow extends EventTarget native "WebKitNamedFlow" {
 
-  @DomName('NamedFlow.firstEmptyRegionIndex')
+  @DomName('WebKitNamedFlow.firstEmptyRegionIndex')
   @DocsEditable
   final int firstEmptyRegionIndex;
 
-  @DomName('NamedFlow.name')
+  @DomName('WebKitNamedFlow.name')
   @DocsEditable
   final String name;
 
-  @DomName('NamedFlow.overset')
+  @DomName('WebKitNamedFlow.overset')
   @DocsEditable
   final bool overset;
 
   @JSName('addEventListener')
-  @DomName('NamedFlow.addEventListener')
+  @DomName('WebKitNamedFlow.addEventListener')
   @DocsEditable
   void $dom_addEventListener(String type, EventListener listener, [bool useCapture]) native;
 
-  @DomName('NamedFlow.dispatchEvent')
+  @DomName('WebKitNamedFlow.dispatchEvent')
   @DocsEditable
   bool dispatchEvent(Event event) native;
 
-  @DomName('NamedFlow.getContent')
+  @DomName('WebKitNamedFlow.getContent')
   @DocsEditable
   @Returns('NodeList')
   @Creates('NodeList')
   List<Node> getContent() native;
 
-  @DomName('NamedFlow.getRegions')
+  @DomName('WebKitNamedFlow.getRegions')
   @DocsEditable
   @Returns('NodeList')
   @Creates('NodeList')
   List<Node> getRegions() native;
 
-  @DomName('NamedFlow.getRegionsByContent')
+  @DomName('WebKitNamedFlow.getRegionsByContent')
   @DocsEditable
   @Returns('NodeList')
   @Creates('NodeList')
   List<Node> getRegionsByContent(Node contentNode) native;
 
   @JSName('removeEventListener')
-  @DomName('NamedFlow.removeEventListener')
+  @DomName('WebKitNamedFlow.removeEventListener')
   @DocsEditable
   void $dom_removeEventListener(String type, EventListener listener, [bool useCapture]) native;
 }
@@ -14923,15 +14295,15 @@
 @Experimental
 class NamedFlowCollection native "WebKitNamedFlowCollection" {
 
-  @DomName('DOMNamedFlowCollection.length')
+  @DomName('WebKitNamedFlowCollection.length')
   @DocsEditable
   final int length;
 
-  @DomName('DOMNamedFlowCollection.item')
+  @DomName('WebKitNamedFlowCollection.item')
   @DocsEditable
   NamedFlow item(int index) native;
 
-  @DomName('DOMNamedFlowCollection.namedItem')
+  @DomName('WebKitNamedFlowCollection.namedItem')
   @DocsEditable
   NamedFlow namedItem(String name) native;
 }
@@ -15248,7 +14620,7 @@
     // time.
     Node child = _this.$dom_firstChild;
     while (child != null) {
-      Node nextChild = child.nextSibling;
+      Node nextChild = child.nextNode;
       if (test(child) == removeMatching) {
         _this.$dom_removeChild(child);
       }
@@ -15373,95 +14745,50 @@
     }
   }
 
-  // Note that this may either be the locally set model or a cached value
-  // of the inherited model. This is cached to minimize model change
-  // notifications.
-  @Creates('Null')
-  var _model;
-  bool _hasLocalModel;
-  Set<StreamController<Node>> _modelChangedStreams;
-
-  /**
-   * The data model which is inherited through the tree.
-   *
-   * Setting this will propagate the value to all descendant nodes. If the
-   * model is not set on this node then it will be inherited from ancestor
-   * nodes.
-   *
-   * Currently this does not support propagation through Shadow DOMs.
-   *
-   * [clearModel] must be used to remove the model property from this node
-   * and have the model inherit from ancestor nodes.
-   */
-  @Experimental
-  get model {
-    // If we have a change handler then we've cached the model locally.
-    if (_modelChangedStreams != null && !_modelChangedStreams.isEmpty) {
-      return _model;
-    }
-    // Otherwise start looking up the tree.
-    for (var node = this; node != null; node = node.parentNode) {
-      if (node._hasLocalModel == true) {
-        return node._model;
-      }
-    }
-    return null;
-  }
-
-  @Experimental
-  void set model(value) {
-    var changed = model != value;
-    _model = value;
-    _hasLocalModel = true;
-    _ModelTreeObserver.initialize();
-
-    if (changed) {
-      if (_modelChangedStreams != null && !_modelChangedStreams.isEmpty) {
-        _modelChangedStreams.toList().forEach((stream) => stream.add(this));
-      }
-      // Propagate new model to all descendants.
-      _ModelTreeObserver.propagateModel(this, value, false);
-    }
-  }
-
-  /**
-   * Clears the locally set model and makes this model be inherited from parent
-   * nodes.
-   */
-  @Experimental
-  void clearModel() {
-    if (_hasLocalModel == true) {
-      _hasLocalModel = false;
-
-      // Propagate new model to all descendants.
-      if (parentNode != null) {
-        _ModelTreeObserver.propagateModel(this, parentNode.model, false);
-      } else {
-        _ModelTreeObserver.propagateModel(this, null, false);
-      }
-    }
-  }
-
-  /**
-   * Get a stream of models, whenever the model changes.
-   */
-  Stream<Node> get onModelChanged {
-    if (_modelChangedStreams == null) {
-      _modelChangedStreams = new Set<StreamController<Node>>();
-    }
-    var controller;
-    controller = new StreamController(
-        onListen: () { _modelChangedStreams.add(controller); },
-        onCancel: () { _modelChangedStreams.remove(controller); });
-    return controller.stream;
-  }
-
   /**
    * Print out a String representation of this Node.
    */
   String toString() => localName == null ?
       (nodeValue == null ? super.toString() : nodeValue) : localName;
 
+  /**
+   * Binds the attribute [name] to the [path] of the [model].
+   * Path is a String of accessors such as `foo.bar.baz`.
+   */
+  @Experimental
+  void bind(String name, model, String path) {
+    // TODO(jmesserly): should we throw instead?
+    window.console.error('Unhandled binding to Node: '
+        '$this $name $model $path');
+  }
+
+  /** Unbinds the attribute [name]. */
+  @Experimental
+  void unbind(String name) {}
+
+  /** Unbinds all bound attributes. */
+  @Experimental
+  void unbindAll() {}
+
+  TemplateInstance _templateInstance;
+
+  // TODO(arv): Consider storing all "NodeRareData" on a single object?
+  int __instanceTerminatorCount;
+  int get _instanceTerminatorCount {
+    if (__instanceTerminatorCount == null) return 0;
+    return __instanceTerminatorCount;
+  }
+  set _instanceTerminatorCount(int value) {
+    if (value == 0) value = null;
+    __instanceTerminatorCount = value;
+  }
+
+  /** Gets the template instance that instantiated this node, if any. */
+  @Experimental
+  TemplateInstance get templateInstance =>
+      _templateInstance != null ? _templateInstance :
+      (parent != null ? parent.templateInstance : null);
+
 
   static const int ATTRIBUTE_NODE = 2;
 
@@ -15685,7 +15012,7 @@
 
 @DocsEditable
 @DomName('NodeList')
-class NodeList implements JavaScriptIndexingBehavior, List<Node> native "NodeList,RadioNodeList" {
+class NodeList extends Object with ListMixin<Node>, ImmutableListMixin<Node> implements JavaScriptIndexingBehavior, List<Node> native "NodeList,RadioNodeList" {
 
   @DomName('NodeList.length')
   @DocsEditable
@@ -15699,196 +15026,11 @@
   // -- start List<Node> mixins.
   // Node is the element type.
 
-  // From Iterable<Node>:
 
-  Iterator<Node> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<Node>(this);
-  }
-
-  Node reduce(Node combine(Node value, Node element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, Node element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(Node element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(Node element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(Node element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<Node> where(bool f(Node element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(Node element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(Node element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(Node element)) => IterableMixinWorkaround.any(this, f);
-
-  List<Node> toList({ bool growable: true }) =>
-      new List<Node>.from(this, growable: growable);
-
-  Set<Node> toSet() => new Set<Node>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<Node> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<Node> takeWhile(bool test(Node value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<Node> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<Node> skipWhile(bool test(Node value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  Node firstWhere(bool test(Node value), { Node orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  Node lastWhere(bool test(Node value), {Node orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  Node singleWhere(bool test(Node value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  Node elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<Node>:
-
-  void add(Node value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<Node>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<Node> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(Node a, Node b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(Node element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(Node element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  Node get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  Node get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  Node get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, Node element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Node removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  Node removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(Node element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(Node element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<Node> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [Node fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<Node> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<Node> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <Node>[]);
-  }
-
-  Map<int, Node> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<Node> mixins.
 
   @JSName('item')
@@ -16423,7 +15565,7 @@
 @DomName('Path')
 class Path native "Path" {
 
-  @DomName('DOMPath.DOMPath')
+  @DomName('Path.DOMPath')
   @DocsEditable
   factory Path([path_OR_text]) {
     if (!?path_OR_text) {
@@ -16441,35 +15583,35 @@
   static Path _create_2(path_OR_text) => JS('Path', 'new Path(#)', path_OR_text);
   static Path _create_3(path_OR_text) => JS('Path', 'new Path(#)', path_OR_text);
 
-  @DomName('DOMPath.arc')
+  @DomName('Path.arc')
   @DocsEditable
   void arc(num x, num y, num radius, num startAngle, num endAngle, bool anticlockwise) native;
 
-  @DomName('DOMPath.arcTo')
+  @DomName('Path.arcTo')
   @DocsEditable
   void arcTo(num x1, num y1, num x2, num y2, num radius) native;
 
-  @DomName('DOMPath.bezierCurveTo')
+  @DomName('Path.bezierCurveTo')
   @DocsEditable
   void bezierCurveTo(num cp1x, num cp1y, num cp2x, num cp2y, num x, num y) native;
 
-  @DomName('DOMPath.closePath')
+  @DomName('Path.closePath')
   @DocsEditable
   void closePath() native;
 
-  @DomName('DOMPath.lineTo')
+  @DomName('Path.lineTo')
   @DocsEditable
   void lineTo(num x, num y) native;
 
-  @DomName('DOMPath.moveTo')
+  @DomName('Path.moveTo')
   @DocsEditable
   void moveTo(num x, num y) native;
 
-  @DomName('DOMPath.quadraticCurveTo')
+  @DomName('Path.quadraticCurveTo')
   @DocsEditable
   void quadraticCurveTo(num cpx, num cpy, num x, num y) native;
 
-  @DomName('DOMPath.rect')
+  @DomName('Path.rect')
   @DocsEditable
   void rect(num x, num y, num width, num height) native;
 }
@@ -16820,27 +15962,27 @@
 @DomName('Plugin')
 class Plugin native "Plugin" {
 
-  @DomName('DOMPlugin.description')
+  @DomName('Plugin.description')
   @DocsEditable
   final String description;
 
-  @DomName('DOMPlugin.filename')
+  @DomName('Plugin.filename')
   @DocsEditable
   final String filename;
 
-  @DomName('DOMPlugin.length')
+  @DomName('Plugin.length')
   @DocsEditable
   final int length;
 
-  @DomName('DOMPlugin.name')
+  @DomName('Plugin.name')
   @DocsEditable
   final String name;
 
-  @DomName('DOMPlugin.item')
+  @DomName('Plugin.item')
   @DocsEditable
   MimeType item(int index) native;
 
-  @DomName('DOMPlugin.namedItem')
+  @DomName('Plugin.namedItem')
   @DocsEditable
   MimeType namedItem(String name) native;
 }
@@ -16851,9 +15993,9 @@
 
 @DocsEditable
 @DomName('PluginArray')
-class PluginArray implements JavaScriptIndexingBehavior, List<Plugin> native "PluginArray" {
+class PluginArray extends Object with ListMixin<Plugin>, ImmutableListMixin<Plugin> implements JavaScriptIndexingBehavior, List<Plugin> native "PluginArray" {
 
-  @DomName('DOMPluginArray.length')
+  @DomName('PluginArray.length')
   @DocsEditable
   int get length => JS("int", "#.length", this);
 
@@ -16865,207 +16007,22 @@
   // -- start List<Plugin> mixins.
   // Plugin is the element type.
 
-  // From Iterable<Plugin>:
 
-  Iterator<Plugin> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<Plugin>(this);
-  }
-
-  Plugin reduce(Plugin combine(Plugin value, Plugin element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, Plugin element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(Plugin element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(Plugin element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(Plugin element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<Plugin> where(bool f(Plugin element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(Plugin element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(Plugin element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(Plugin element)) => IterableMixinWorkaround.any(this, f);
-
-  List<Plugin> toList({ bool growable: true }) =>
-      new List<Plugin>.from(this, growable: growable);
-
-  Set<Plugin> toSet() => new Set<Plugin>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<Plugin> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<Plugin> takeWhile(bool test(Plugin value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<Plugin> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<Plugin> skipWhile(bool test(Plugin value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  Plugin firstWhere(bool test(Plugin value), { Plugin orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  Plugin lastWhere(bool test(Plugin value), {Plugin orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  Plugin singleWhere(bool test(Plugin value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  Plugin elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<Plugin>:
-
-  void add(Plugin value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<Plugin> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<Plugin>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<Plugin> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(Plugin a, Plugin b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(Plugin element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(Plugin element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  Plugin get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  Plugin get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  Plugin get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, Plugin element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<Plugin> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<Plugin> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Plugin removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  Plugin removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(Plugin element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(Plugin element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<Plugin> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<Plugin> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [Plugin fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<Plugin> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<Plugin> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <Plugin>[]);
-  }
-
-  Map<int, Plugin> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<Plugin> mixins.
 
-  @DomName('DOMPluginArray.item')
+  @DomName('PluginArray.item')
   @DocsEditable
   Plugin item(int index) native;
 
-  @DomName('DOMPluginArray.namedItem')
+  @DomName('PluginArray.namedItem')
   @DocsEditable
   Plugin namedItem(String name) native;
 
-  @DomName('DOMPluginArray.refresh')
+  @DomName('PluginArray.refresh')
   @DocsEditable
   void refresh(bool reload) native;
 }
@@ -18250,65 +17207,65 @@
 @DomName('SecurityPolicy')
 class SecurityPolicy native "SecurityPolicy" {
 
-  @DomName('DOMSecurityPolicy.allowsEval')
+  @DomName('SecurityPolicy.allowsEval')
   @DocsEditable
   final bool allowsEval;
 
-  @DomName('DOMSecurityPolicy.allowsInlineScript')
+  @DomName('SecurityPolicy.allowsInlineScript')
   @DocsEditable
   final bool allowsInlineScript;
 
-  @DomName('DOMSecurityPolicy.allowsInlineStyle')
+  @DomName('SecurityPolicy.allowsInlineStyle')
   @DocsEditable
   final bool allowsInlineStyle;
 
-  @DomName('DOMSecurityPolicy.isActive')
+  @DomName('SecurityPolicy.isActive')
   @DocsEditable
   final bool isActive;
 
-  @DomName('DOMSecurityPolicy.reportURIs')
+  @DomName('SecurityPolicy.reportURIs')
   @DocsEditable
   @Returns('DomStringList')
   @Creates('DomStringList')
   final List<String> reportURIs;
 
-  @DomName('DOMSecurityPolicy.allowsConnectionTo')
+  @DomName('SecurityPolicy.allowsConnectionTo')
   @DocsEditable
   bool allowsConnectionTo(String url) native;
 
-  @DomName('DOMSecurityPolicy.allowsFontFrom')
+  @DomName('SecurityPolicy.allowsFontFrom')
   @DocsEditable
   bool allowsFontFrom(String url) native;
 
-  @DomName('DOMSecurityPolicy.allowsFormAction')
+  @DomName('SecurityPolicy.allowsFormAction')
   @DocsEditable
   bool allowsFormAction(String url) native;
 
-  @DomName('DOMSecurityPolicy.allowsFrameFrom')
+  @DomName('SecurityPolicy.allowsFrameFrom')
   @DocsEditable
   bool allowsFrameFrom(String url) native;
 
-  @DomName('DOMSecurityPolicy.allowsImageFrom')
+  @DomName('SecurityPolicy.allowsImageFrom')
   @DocsEditable
   bool allowsImageFrom(String url) native;
 
-  @DomName('DOMSecurityPolicy.allowsMediaFrom')
+  @DomName('SecurityPolicy.allowsMediaFrom')
   @DocsEditable
   bool allowsMediaFrom(String url) native;
 
-  @DomName('DOMSecurityPolicy.allowsObjectFrom')
+  @DomName('SecurityPolicy.allowsObjectFrom')
   @DocsEditable
   bool allowsObjectFrom(String url) native;
 
-  @DomName('DOMSecurityPolicy.allowsPluginType')
+  @DomName('SecurityPolicy.allowsPluginType')
   @DocsEditable
   bool allowsPluginType(String type) native;
 
-  @DomName('DOMSecurityPolicy.allowsScriptFrom')
+  @DomName('SecurityPolicy.allowsScriptFrom')
   @DocsEditable
   bool allowsScriptFrom(String url) native;
 
-  @DomName('DOMSecurityPolicy.allowsStyleFrom')
+  @DomName('SecurityPolicy.allowsStyleFrom')
   @DocsEditable
   bool allowsStyleFrom(String url) native;
 }
@@ -18472,107 +17429,107 @@
 @DomName('Selection')
 class Selection native "Selection" {
 
-  @DomName('DOMSelection.anchorNode')
+  @DomName('Selection.anchorNode')
   @DocsEditable
   final Node anchorNode;
 
-  @DomName('DOMSelection.anchorOffset')
+  @DomName('Selection.anchorOffset')
   @DocsEditable
   final int anchorOffset;
 
-  @DomName('DOMSelection.baseNode')
+  @DomName('Selection.baseNode')
   @DocsEditable
   final Node baseNode;
 
-  @DomName('DOMSelection.baseOffset')
+  @DomName('Selection.baseOffset')
   @DocsEditable
   final int baseOffset;
 
-  @DomName('DOMSelection.extentNode')
+  @DomName('Selection.extentNode')
   @DocsEditable
   final Node extentNode;
 
-  @DomName('DOMSelection.extentOffset')
+  @DomName('Selection.extentOffset')
   @DocsEditable
   final int extentOffset;
 
-  @DomName('DOMSelection.focusNode')
+  @DomName('Selection.focusNode')
   @DocsEditable
   final Node focusNode;
 
-  @DomName('DOMSelection.focusOffset')
+  @DomName('Selection.focusOffset')
   @DocsEditable
   final int focusOffset;
 
-  @DomName('DOMSelection.isCollapsed')
+  @DomName('Selection.isCollapsed')
   @DocsEditable
   final bool isCollapsed;
 
-  @DomName('DOMSelection.rangeCount')
+  @DomName('Selection.rangeCount')
   @DocsEditable
   final int rangeCount;
 
-  @DomName('DOMSelection.type')
+  @DomName('Selection.type')
   @DocsEditable
   final String type;
 
-  @DomName('DOMSelection.addRange')
+  @DomName('Selection.addRange')
   @DocsEditable
   void addRange(Range range) native;
 
-  @DomName('DOMSelection.collapse')
+  @DomName('Selection.collapse')
   @DocsEditable
   void collapse(Node node, int index) native;
 
-  @DomName('DOMSelection.collapseToEnd')
+  @DomName('Selection.collapseToEnd')
   @DocsEditable
   void collapseToEnd() native;
 
-  @DomName('DOMSelection.collapseToStart')
+  @DomName('Selection.collapseToStart')
   @DocsEditable
   void collapseToStart() native;
 
-  @DomName('DOMSelection.containsNode')
+  @DomName('Selection.containsNode')
   @DocsEditable
   bool containsNode(Node node, bool allowPartial) native;
 
-  @DomName('DOMSelection.deleteFromDocument')
+  @DomName('Selection.deleteFromDocument')
   @DocsEditable
   void deleteFromDocument() native;
 
-  @DomName('DOMSelection.empty')
+  @DomName('Selection.empty')
   @DocsEditable
   void empty() native;
 
-  @DomName('DOMSelection.extend')
+  @DomName('Selection.extend')
   @DocsEditable
   void extend(Node node, int offset) native;
 
-  @DomName('DOMSelection.getRangeAt')
+  @DomName('Selection.getRangeAt')
   @DocsEditable
   Range getRangeAt(int index) native;
 
-  @DomName('DOMSelection.modify')
+  @DomName('Selection.modify')
   @DocsEditable
   void modify(String alter, String direction, String granularity) native;
 
-  @DomName('DOMSelection.removeAllRanges')
+  @DomName('Selection.removeAllRanges')
   @DocsEditable
   void removeAllRanges() native;
 
-  @DomName('DOMSelection.selectAllChildren')
+  @DomName('Selection.selectAllChildren')
   @DocsEditable
   void selectAllChildren(Node node) native;
 
-  @DomName('DOMSelection.setBaseAndExtent')
+  @DomName('Selection.setBaseAndExtent')
   @DocsEditable
   void setBaseAndExtent(Node baseNode, int baseOffset, Node extentNode, int extentOffset) native;
 
-  @DomName('DOMSelection.setPosition')
+  @DomName('Selection.setPosition')
   @DocsEditable
   void setPosition(Node node, int offset) native;
 
-  @DomName('DOMSelection.toString')
+  @DomName('Selection.toString')
   @DocsEditable
   String toString() native;
 }
@@ -18691,7 +17648,7 @@
 
 @DocsEditable
 @DomName('SourceBufferList')
-class SourceBufferList extends EventTarget implements JavaScriptIndexingBehavior, List<SourceBuffer> native "SourceBufferList" {
+class SourceBufferList extends EventTarget with ListMixin<SourceBuffer>, ImmutableListMixin<SourceBuffer> implements JavaScriptIndexingBehavior, List<SourceBuffer> native "SourceBufferList" {
 
   @DomName('SourceBufferList.length')
   @DocsEditable
@@ -18705,196 +17662,11 @@
   // -- start List<SourceBuffer> mixins.
   // SourceBuffer is the element type.
 
-  // From Iterable<SourceBuffer>:
 
-  Iterator<SourceBuffer> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<SourceBuffer>(this);
-  }
-
-  SourceBuffer reduce(SourceBuffer combine(SourceBuffer value, SourceBuffer element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, SourceBuffer element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(SourceBuffer element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(SourceBuffer element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(SourceBuffer element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<SourceBuffer> where(bool f(SourceBuffer element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(SourceBuffer element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(SourceBuffer element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(SourceBuffer element)) => IterableMixinWorkaround.any(this, f);
-
-  List<SourceBuffer> toList({ bool growable: true }) =>
-      new List<SourceBuffer>.from(this, growable: growable);
-
-  Set<SourceBuffer> toSet() => new Set<SourceBuffer>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<SourceBuffer> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<SourceBuffer> takeWhile(bool test(SourceBuffer value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<SourceBuffer> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<SourceBuffer> skipWhile(bool test(SourceBuffer value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  SourceBuffer firstWhere(bool test(SourceBuffer value), { SourceBuffer orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  SourceBuffer lastWhere(bool test(SourceBuffer value), {SourceBuffer orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  SourceBuffer singleWhere(bool test(SourceBuffer value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  SourceBuffer elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<SourceBuffer>:
-
-  void add(SourceBuffer value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<SourceBuffer> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<SourceBuffer>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<SourceBuffer> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(SourceBuffer a, SourceBuffer b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(SourceBuffer element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(SourceBuffer element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  SourceBuffer get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  SourceBuffer get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  SourceBuffer get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, SourceBuffer element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<SourceBuffer> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<SourceBuffer> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  SourceBuffer removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  SourceBuffer removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(SourceBuffer element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(SourceBuffer element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<SourceBuffer> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<SourceBuffer> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [SourceBuffer fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<SourceBuffer> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<SourceBuffer> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <SourceBuffer>[]);
-  }
-
-  Map<int, SourceBuffer> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<SourceBuffer> mixins.
 
   @JSName('addEventListener')
@@ -18984,7 +17756,7 @@
 
 @DocsEditable
 @DomName('SpeechGrammarList')
-class SpeechGrammarList implements JavaScriptIndexingBehavior, List<SpeechGrammar> native "SpeechGrammarList" {
+class SpeechGrammarList extends Object with ListMixin<SpeechGrammar>, ImmutableListMixin<SpeechGrammar> implements JavaScriptIndexingBehavior, List<SpeechGrammar> native "SpeechGrammarList" {
 
   @DomName('SpeechGrammarList.SpeechGrammarList')
   @DocsEditable
@@ -19005,196 +17777,11 @@
   // -- start List<SpeechGrammar> mixins.
   // SpeechGrammar is the element type.
 
-  // From Iterable<SpeechGrammar>:
 
-  Iterator<SpeechGrammar> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<SpeechGrammar>(this);
-  }
-
-  SpeechGrammar reduce(SpeechGrammar combine(SpeechGrammar value, SpeechGrammar element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, SpeechGrammar element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(SpeechGrammar element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(SpeechGrammar element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(SpeechGrammar element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<SpeechGrammar> where(bool f(SpeechGrammar element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(SpeechGrammar element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(SpeechGrammar element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(SpeechGrammar element)) => IterableMixinWorkaround.any(this, f);
-
-  List<SpeechGrammar> toList({ bool growable: true }) =>
-      new List<SpeechGrammar>.from(this, growable: growable);
-
-  Set<SpeechGrammar> toSet() => new Set<SpeechGrammar>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<SpeechGrammar> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<SpeechGrammar> takeWhile(bool test(SpeechGrammar value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<SpeechGrammar> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<SpeechGrammar> skipWhile(bool test(SpeechGrammar value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  SpeechGrammar firstWhere(bool test(SpeechGrammar value), { SpeechGrammar orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  SpeechGrammar lastWhere(bool test(SpeechGrammar value), {SpeechGrammar orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  SpeechGrammar singleWhere(bool test(SpeechGrammar value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  SpeechGrammar elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<SpeechGrammar>:
-
-  void add(SpeechGrammar value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<SpeechGrammar> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<SpeechGrammar>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<SpeechGrammar> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(SpeechGrammar a, SpeechGrammar b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(SpeechGrammar element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(SpeechGrammar element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  SpeechGrammar get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  SpeechGrammar get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  SpeechGrammar get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, SpeechGrammar element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<SpeechGrammar> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<SpeechGrammar> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  SpeechGrammar removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  SpeechGrammar removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(SpeechGrammar element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(SpeechGrammar element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<SpeechGrammar> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<SpeechGrammar> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [SpeechGrammar fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<SpeechGrammar> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<SpeechGrammar> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <SpeechGrammar>[]);
-  }
-
-  Map<int, SpeechGrammar> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<SpeechGrammar> mixins.
 
   @DomName('SpeechGrammarList.addFromString')
@@ -20079,14 +18666,134 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+// WARNING: Do not edit - generated code.
 
-@DocsEditable
+
+@Experimental
 @DomName('HTMLTemplateElement')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@Experimental
 class TemplateElement extends Element native "HTMLTemplateElement" {
 
+  @DomName('HTMLTemplateElement.HTMLTemplateElement')
+  @DocsEditable
+  factory TemplateElement() => document.$dom_createElement("template");
+
+  /// Checks if this type is supported on the current platform.
+  static bool get supported => Element.isTagSupported('template');
+
+  @JSName('content')
   @DomName('HTMLTemplateElement.content')
   @DocsEditable
-  final DocumentFragment content;
+  final DocumentFragment $dom_content;
+
+
+  // For real TemplateElement use the actual DOM .content field instead of
+  // our polyfilled expando.
+  @Experimental
+  DocumentFragment get content => $dom_content;
+
+  static StreamController<DocumentFragment> _instanceCreated;
+
+  /**
+   * *Warning*: This is an implementation helper for Model-Driven Views and
+   * should not be used in your code.
+   *
+   * This event is fired whenever a template is instantiated via
+   * [createInstance].
+   */
+  // TODO(rafaelw): This is a hack, and is neccesary for the polyfill
+  // because custom elements are not upgraded during clone()
+  @Experimental
+  static Stream<DocumentFragment> get instanceCreated {
+    if (_instanceCreated == null) {
+      _instanceCreated = new StreamController<DocumentFragment>();
+    }
+    return _instanceCreated.stream;
+  }
+
+  /**
+   * Ensures proper API and content model for template elements.
+   *
+   * [instanceRef] can be used to set the [Element.ref] property of [template],
+   * and use the ref's content will be used as source when createInstance() is
+   * invoked.
+   *
+   * Returns true if this template was just decorated, or false if it was
+   * already decorated.
+   */
+  @Experimental
+  static bool decorate(Element template, [Element instanceRef]) {
+    // == true check because it starts as a null field.
+    if (template._templateIsDecorated == true) return false;
+
+    template._templateIsDecorated = true;
+
+    _injectStylesheet();
+
+    // Create content
+    if (template is! TemplateElement) {
+      var doc = _getTemplateContentsOwner(template.document);
+      template._templateContent = doc.createDocumentFragment();
+    }
+
+    if (instanceRef != null) {
+      template._templateInstanceRef = instanceRef;
+      return true; // content is empty.
+    }
+
+    if (template is TemplateElement) {
+      _bootstrapTemplatesRecursivelyFrom(template.content);
+    } else {
+      _liftNonNativeTemplateChildrenIntoContent(template);
+    }
+
+    return true;
+  }
+
+  /**
+   * This used to decorate recursively all templates from a given node.
+   *
+   * By default [decorate] will be called on templates lazily when certain
+   * properties such as [model] are accessed, but it can be run eagerly to
+   * decorate an entire tree recursively.
+   */
+  // TODO(rafaelw): Review whether this is the right public API.
+  @Experimental
+  static void bootstrap(Node content) {
+    _bootstrapTemplatesRecursivelyFrom(content);
+  }
+
+  static bool _initStyles;
+
+  static void _injectStylesheet() {
+    if (_initStyles == true) return;
+    _initStyles = true;
+
+    var style = new StyleElement();
+    style.text = r'''
+template,
+thead[template],
+tbody[template],
+tfoot[template],
+th[template],
+tr[template],
+td[template],
+caption[template],
+colgroup[template],
+col[template],
+option[template] {
+  display: none;
+}''';
+    document.head.append(style);
+  }
+
+  /**
+   * A mapping of names to Custom Syntax objects. See [CustomBindingSyntax] for
+   * more information.
+   */
+  @Experimental
+  static Map<String, CustomBindingSyntax> syntax = {};
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -20119,6 +18826,42 @@
   @DocsEditable
   Text splitText(int offset) native;
 
+
+  @Creates('Null')  // Set from Dart code; does not instantiate a native type.
+  StreamSubscription _textBinding;
+
+  @Experimental
+  void bind(String name, model, String path) {
+    if (name != 'text') {
+      super.bind(name, model, path);
+      return;
+    }
+
+    unbind('text');
+
+    _textBinding = new PathObserver(model, path).bindSync((value) {
+      text = value == null ? '' : '$value';
+    });
+  }
+
+  @Experimental
+  void unbind(String name) {
+    if (name != 'text') {
+      super.unbind(name);
+      return;
+    }
+
+    if (_textBinding == null) return;
+
+    _textBinding.cancel();
+    _textBinding = null;
+  }
+
+  @Experimental
+  void unbindAll() {
+    unbind('text');
+    super.unbindAll();
+  }
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -20458,7 +19201,7 @@
 
 @DocsEditable
 @DomName('TextTrackCueList')
-class TextTrackCueList implements List<TextTrackCue>, JavaScriptIndexingBehavior native "TextTrackCueList" {
+class TextTrackCueList extends Object with ListMixin<TextTrackCue>, ImmutableListMixin<TextTrackCue> implements List<TextTrackCue>, JavaScriptIndexingBehavior native "TextTrackCueList" {
 
   @DomName('TextTrackCueList.length')
   @DocsEditable
@@ -20472,196 +19215,11 @@
   // -- start List<TextTrackCue> mixins.
   // TextTrackCue is the element type.
 
-  // From Iterable<TextTrackCue>:
 
-  Iterator<TextTrackCue> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<TextTrackCue>(this);
-  }
-
-  TextTrackCue reduce(TextTrackCue combine(TextTrackCue value, TextTrackCue element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, TextTrackCue element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(TextTrackCue element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(TextTrackCue element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(TextTrackCue element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<TextTrackCue> where(bool f(TextTrackCue element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(TextTrackCue element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(TextTrackCue element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(TextTrackCue element)) => IterableMixinWorkaround.any(this, f);
-
-  List<TextTrackCue> toList({ bool growable: true }) =>
-      new List<TextTrackCue>.from(this, growable: growable);
-
-  Set<TextTrackCue> toSet() => new Set<TextTrackCue>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<TextTrackCue> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<TextTrackCue> takeWhile(bool test(TextTrackCue value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<TextTrackCue> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<TextTrackCue> skipWhile(bool test(TextTrackCue value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  TextTrackCue firstWhere(bool test(TextTrackCue value), { TextTrackCue orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  TextTrackCue lastWhere(bool test(TextTrackCue value), {TextTrackCue orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  TextTrackCue singleWhere(bool test(TextTrackCue value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  TextTrackCue elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<TextTrackCue>:
-
-  void add(TextTrackCue value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<TextTrackCue> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<TextTrackCue>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<TextTrackCue> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(TextTrackCue a, TextTrackCue b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(TextTrackCue element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(TextTrackCue element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  TextTrackCue get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  TextTrackCue get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  TextTrackCue get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, TextTrackCue element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<TextTrackCue> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<TextTrackCue> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  TextTrackCue removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  TextTrackCue removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(TextTrackCue element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(TextTrackCue element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<TextTrackCue> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<TextTrackCue> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [TextTrackCue fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<TextTrackCue> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<TextTrackCue> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <TextTrackCue>[]);
-  }
-
-  Map<int, TextTrackCue> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<TextTrackCue> mixins.
 
   @DomName('TextTrackCueList.getCueById')
@@ -20679,7 +19237,7 @@
 
 @DocsEditable
 @DomName('TextTrackList')
-class TextTrackList extends EventTarget implements JavaScriptIndexingBehavior, List<TextTrack> native "TextTrackList" {
+class TextTrackList extends EventTarget with ListMixin<TextTrack>, ImmutableListMixin<TextTrack> implements JavaScriptIndexingBehavior, List<TextTrack> native "TextTrackList" {
 
   @DomName('TextTrackList.addtrackEvent')
   @DocsEditable
@@ -20697,196 +19255,11 @@
   // -- start List<TextTrack> mixins.
   // TextTrack is the element type.
 
-  // From Iterable<TextTrack>:
 
-  Iterator<TextTrack> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<TextTrack>(this);
-  }
-
-  TextTrack reduce(TextTrack combine(TextTrack value, TextTrack element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, TextTrack element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(TextTrack element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(TextTrack element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(TextTrack element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<TextTrack> where(bool f(TextTrack element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(TextTrack element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(TextTrack element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(TextTrack element)) => IterableMixinWorkaround.any(this, f);
-
-  List<TextTrack> toList({ bool growable: true }) =>
-      new List<TextTrack>.from(this, growable: growable);
-
-  Set<TextTrack> toSet() => new Set<TextTrack>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<TextTrack> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<TextTrack> takeWhile(bool test(TextTrack value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<TextTrack> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<TextTrack> skipWhile(bool test(TextTrack value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  TextTrack firstWhere(bool test(TextTrack value), { TextTrack orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  TextTrack lastWhere(bool test(TextTrack value), {TextTrack orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  TextTrack singleWhere(bool test(TextTrack value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  TextTrack elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<TextTrack>:
-
-  void add(TextTrack value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<TextTrack> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<TextTrack>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<TextTrack> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(TextTrack a, TextTrack b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(TextTrack element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(TextTrack element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  TextTrack get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  TextTrack get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  TextTrack get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, TextTrack element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<TextTrack> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<TextTrack> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  TextTrack removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  TextTrack removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(TextTrack element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(TextTrack element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<TextTrack> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<TextTrack> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [TextTrack fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<TextTrack> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<TextTrack> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <TextTrack>[]);
-  }
-
-  Map<int, TextTrack> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<TextTrack> mixins.
 
   @JSName('addEventListener')
@@ -21127,7 +19500,7 @@
 
 
 @DomName('TouchList')
-class TouchList implements JavaScriptIndexingBehavior, List<Touch> native "TouchList" {
+class TouchList extends Object with ListMixin<Touch>, ImmutableListMixin<Touch> implements JavaScriptIndexingBehavior, List<Touch> native "TouchList" {
   /// NB: This constructor likely does not work as you might expect it to! This
   /// constructor will simply fail (returning null) if you are not on a device
   /// with touch enabled. See dartbug.com/8314.
@@ -21148,196 +19521,11 @@
   // -- start List<Touch> mixins.
   // Touch is the element type.
 
-  // From Iterable<Touch>:
 
-  Iterator<Touch> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<Touch>(this);
-  }
-
-  Touch reduce(Touch combine(Touch value, Touch element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, Touch element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(Touch element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(Touch element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(Touch element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<Touch> where(bool f(Touch element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(Touch element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(Touch element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(Touch element)) => IterableMixinWorkaround.any(this, f);
-
-  List<Touch> toList({ bool growable: true }) =>
-      new List<Touch>.from(this, growable: growable);
-
-  Set<Touch> toSet() => new Set<Touch>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<Touch> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<Touch> takeWhile(bool test(Touch value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<Touch> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<Touch> skipWhile(bool test(Touch value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  Touch firstWhere(bool test(Touch value), { Touch orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  Touch lastWhere(bool test(Touch value), {Touch orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  Touch singleWhere(bool test(Touch value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  Touch elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<Touch>:
-
-  void add(Touch value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<Touch> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<Touch>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<Touch> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(Touch a, Touch b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(Touch element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(Touch element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  Touch get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  Touch get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  Touch get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, Touch element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<Touch> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<Touch> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Touch removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  Touch removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(Touch element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(Touch element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<Touch> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<Touch> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [Touch fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<Touch> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<Touch> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <Touch>[]);
-  }
-
-  Map<int, Touch> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<Touch> mixins.
 
   @DomName('TouchList.item')
@@ -22427,73 +20615,73 @@
    */
   static bool get supportsPointConversions => _DomPoint.supported;
 
-  @DomName('DOMWindow.DOMContentLoadedEvent')
+  @DomName('Window.DOMContentLoadedEvent')
   @DocsEditable
   static const EventStreamProvider<Event> contentLoadedEvent = const EventStreamProvider<Event>('DOMContentLoaded');
 
-  @DomName('DOMWindow.devicemotionEvent')
+  @DomName('Window.devicemotionEvent')
   @DocsEditable
   static const EventStreamProvider<DeviceMotionEvent> deviceMotionEvent = const EventStreamProvider<DeviceMotionEvent>('devicemotion');
 
-  @DomName('DOMWindow.deviceorientationEvent')
+  @DomName('Window.deviceorientationEvent')
   @DocsEditable
   static const EventStreamProvider<DeviceOrientationEvent> deviceOrientationEvent = const EventStreamProvider<DeviceOrientationEvent>('deviceorientation');
 
-  @DomName('DOMWindow.hashchangeEvent')
+  @DomName('Window.hashchangeEvent')
   @DocsEditable
   static const EventStreamProvider<Event> hashChangeEvent = const EventStreamProvider<Event>('hashchange');
 
-  @DomName('DOMWindow.messageEvent')
+  @DomName('Window.messageEvent')
   @DocsEditable
   static const EventStreamProvider<MessageEvent> messageEvent = const EventStreamProvider<MessageEvent>('message');
 
-  @DomName('DOMWindow.offlineEvent')
+  @DomName('Window.offlineEvent')
   @DocsEditable
   static const EventStreamProvider<Event> offlineEvent = const EventStreamProvider<Event>('offline');
 
-  @DomName('DOMWindow.onlineEvent')
+  @DomName('Window.onlineEvent')
   @DocsEditable
   static const EventStreamProvider<Event> onlineEvent = const EventStreamProvider<Event>('online');
 
-  @DomName('DOMWindow.pagehideEvent')
+  @DomName('Window.pagehideEvent')
   @DocsEditable
   static const EventStreamProvider<Event> pageHideEvent = const EventStreamProvider<Event>('pagehide');
 
-  @DomName('DOMWindow.pageshowEvent')
+  @DomName('Window.pageshowEvent')
   @DocsEditable
   static const EventStreamProvider<Event> pageShowEvent = const EventStreamProvider<Event>('pageshow');
 
-  @DomName('DOMWindow.popstateEvent')
+  @DomName('Window.popstateEvent')
   @DocsEditable
   static const EventStreamProvider<PopStateEvent> popStateEvent = const EventStreamProvider<PopStateEvent>('popstate');
 
-  @DomName('DOMWindow.resizeEvent')
+  @DomName('Window.resizeEvent')
   @DocsEditable
   static const EventStreamProvider<Event> resizeEvent = const EventStreamProvider<Event>('resize');
 
-  @DomName('DOMWindow.storageEvent')
+  @DomName('Window.storageEvent')
   @DocsEditable
   static const EventStreamProvider<StorageEvent> storageEvent = const EventStreamProvider<StorageEvent>('storage');
 
-  @DomName('DOMWindow.unloadEvent')
+  @DomName('Window.unloadEvent')
   @DocsEditable
   static const EventStreamProvider<Event> unloadEvent = const EventStreamProvider<Event>('unload');
 
-  @DomName('DOMWindow.webkitAnimationEndEvent')
+  @DomName('Window.webkitAnimationEndEvent')
   @DocsEditable
   @SupportedBrowser(SupportedBrowser.CHROME)
   @SupportedBrowser(SupportedBrowser.SAFARI)
   @Experimental
   static const EventStreamProvider<AnimationEvent> animationEndEvent = const EventStreamProvider<AnimationEvent>('webkitAnimationEnd');
 
-  @DomName('DOMWindow.webkitAnimationIterationEvent')
+  @DomName('Window.webkitAnimationIterationEvent')
   @DocsEditable
   @SupportedBrowser(SupportedBrowser.CHROME)
   @SupportedBrowser(SupportedBrowser.SAFARI)
   @Experimental
   static const EventStreamProvider<AnimationEvent> animationIterationEvent = const EventStreamProvider<AnimationEvent>('webkitAnimationIteration');
 
-  @DomName('DOMWindow.webkitAnimationStartEvent')
+  @DomName('Window.webkitAnimationStartEvent')
   @DocsEditable
   @SupportedBrowser(SupportedBrowser.CHROME)
   @SupportedBrowser(SupportedBrowser.SAFARI)
@@ -22504,183 +20692,183 @@
 
   static const int TEMPORARY = 0;
 
-  @DomName('DOMWindow.applicationCache')
+  @DomName('Window.applicationCache')
   @DocsEditable
   final ApplicationCache applicationCache;
 
-  @DomName('DOMWindow.closed')
+  @DomName('Window.closed')
   @DocsEditable
   final bool closed;
 
-  @DomName('DOMWindow.crypto')
+  @DomName('Window.crypto')
   @DocsEditable
   final Crypto crypto;
 
-  @DomName('DOMWindow.defaultStatus')
+  @DomName('Window.defaultStatus')
   @DocsEditable
   String defaultStatus;
 
-  @DomName('DOMWindow.defaultstatus')
+  @DomName('Window.defaultstatus')
   @DocsEditable
   String defaultstatus;
 
-  @DomName('DOMWindow.devicePixelRatio')
+  @DomName('Window.devicePixelRatio')
   @DocsEditable
   final num devicePixelRatio;
 
-  @DomName('DOMWindow.event')
+  @DomName('Window.event')
   @DocsEditable
   final Event event;
 
-  @DomName('DOMWindow.history')
+  @DomName('Window.history')
   @DocsEditable
   final History history;
 
-  @DomName('DOMWindow.innerHeight')
+  @DomName('Window.innerHeight')
   @DocsEditable
   final int innerHeight;
 
-  @DomName('DOMWindow.innerWidth')
+  @DomName('Window.innerWidth')
   @DocsEditable
   final int innerWidth;
 
-  @DomName('DOMWindow.localStorage')
+  @DomName('Window.localStorage')
   @DocsEditable
   final Storage localStorage;
 
-  @DomName('DOMWindow.locationbar')
+  @DomName('Window.locationbar')
   @DocsEditable
   final BarInfo locationbar;
 
-  @DomName('DOMWindow.menubar')
+  @DomName('Window.menubar')
   @DocsEditable
   final BarInfo menubar;
 
-  @DomName('DOMWindow.name')
+  @DomName('Window.name')
   @DocsEditable
   String name;
 
-  @DomName('DOMWindow.navigator')
+  @DomName('Window.navigator')
   @DocsEditable
   final Navigator navigator;
 
-  @DomName('DOMWindow.offscreenBuffering')
+  @DomName('Window.offscreenBuffering')
   @DocsEditable
   final bool offscreenBuffering;
 
   WindowBase get opener => _convertNativeToDart_Window(this._get_opener);
   @JSName('opener')
-  @DomName('DOMWindow.opener')
+  @DomName('Window.opener')
   @DocsEditable
   @Creates('Window|=Object')
   @Returns('Window|=Object')
   final dynamic _get_opener;
 
-  @DomName('DOMWindow.outerHeight')
+  @DomName('Window.outerHeight')
   @DocsEditable
   final int outerHeight;
 
-  @DomName('DOMWindow.outerWidth')
+  @DomName('Window.outerWidth')
   @DocsEditable
   final int outerWidth;
 
-  @DomName('DOMWindow.pageXOffset')
+  @DomName('Window.pageXOffset')
   @DocsEditable
   final int pageXOffset;
 
-  @DomName('DOMWindow.pageYOffset')
+  @DomName('Window.pageYOffset')
   @DocsEditable
   final int pageYOffset;
 
   WindowBase get parent => _convertNativeToDart_Window(this._get_parent);
   @JSName('parent')
-  @DomName('DOMWindow.parent')
+  @DomName('Window.parent')
   @DocsEditable
   @Creates('Window|=Object')
   @Returns('Window|=Object')
   final dynamic _get_parent;
 
-  @DomName('DOMWindow.performance')
+  @DomName('Window.performance')
   @DocsEditable
   @SupportedBrowser(SupportedBrowser.CHROME)
   @SupportedBrowser(SupportedBrowser.FIREFOX)
   @SupportedBrowser(SupportedBrowser.IE)
   final Performance performance;
 
-  @DomName('DOMWindow.personalbar')
+  @DomName('Window.personalbar')
   @DocsEditable
   final BarInfo personalbar;
 
-  @DomName('DOMWindow.screen')
+  @DomName('Window.screen')
   @DocsEditable
   final Screen screen;
 
-  @DomName('DOMWindow.screenLeft')
+  @DomName('Window.screenLeft')
   @DocsEditable
   final int screenLeft;
 
-  @DomName('DOMWindow.screenTop')
+  @DomName('Window.screenTop')
   @DocsEditable
   final int screenTop;
 
-  @DomName('DOMWindow.screenX')
+  @DomName('Window.screenX')
   @DocsEditable
   final int screenX;
 
-  @DomName('DOMWindow.screenY')
+  @DomName('Window.screenY')
   @DocsEditable
   final int screenY;
 
-  @DomName('DOMWindow.scrollX')
+  @DomName('Window.scrollX')
   @DocsEditable
   final int scrollX;
 
-  @DomName('DOMWindow.scrollY')
+  @DomName('Window.scrollY')
   @DocsEditable
   final int scrollY;
 
-  @DomName('DOMWindow.scrollbars')
+  @DomName('Window.scrollbars')
   @DocsEditable
   final BarInfo scrollbars;
 
   WindowBase get self => _convertNativeToDart_Window(this._get_self);
   @JSName('self')
-  @DomName('DOMWindow.self')
+  @DomName('Window.self')
   @DocsEditable
   @Creates('Window|=Object')
   @Returns('Window|=Object')
   final dynamic _get_self;
 
-  @DomName('DOMWindow.sessionStorage')
+  @DomName('Window.sessionStorage')
   @DocsEditable
   final Storage sessionStorage;
 
-  @DomName('DOMWindow.status')
+  @DomName('Window.status')
   @DocsEditable
   String status;
 
-  @DomName('DOMWindow.statusbar')
+  @DomName('Window.statusbar')
   @DocsEditable
   final BarInfo statusbar;
 
-  @DomName('DOMWindow.styleMedia')
+  @DomName('Window.styleMedia')
   @DocsEditable
   final StyleMedia styleMedia;
 
-  @DomName('DOMWindow.toolbar')
+  @DomName('Window.toolbar')
   @DocsEditable
   final BarInfo toolbar;
 
   WindowBase get top => _convertNativeToDart_Window(this._get_top);
   @JSName('top')
-  @DomName('DOMWindow.top')
+  @DomName('Window.top')
   @DocsEditable
   @Creates('Window|=Object')
   @Returns('Window|=Object')
   final dynamic _get_top;
 
   @JSName('webkitNotifications')
-  @DomName('DOMWindow.webkitNotifications')
+  @DomName('Window.webkitNotifications')
   @DocsEditable
   @SupportedBrowser(SupportedBrowser.CHROME)
   @SupportedBrowser(SupportedBrowser.SAFARI)
@@ -22688,7 +20876,7 @@
   final NotificationCenter notifications;
 
   @JSName('webkitStorageInfo')
-  @DomName('DOMWindow.webkitStorageInfo')
+  @DomName('Window.webkitStorageInfo')
   @DocsEditable
   @SupportedBrowser(SupportedBrowser.CHROME)
   @SupportedBrowser(SupportedBrowser.SAFARI)
@@ -22697,88 +20885,88 @@
 
   WindowBase get window => _convertNativeToDart_Window(this._get_window);
   @JSName('window')
-  @DomName('DOMWindow.window')
+  @DomName('Window.window')
   @DocsEditable
   @Creates('Window|=Object')
   @Returns('Window|=Object')
   final dynamic _get_window;
 
   @JSName('addEventListener')
-  @DomName('DOMWindow.addEventListener')
+  @DomName('Window.addEventListener')
   @DocsEditable
   void $dom_addEventListener(String type, EventListener listener, [bool useCapture]) native;
 
-  @DomName('DOMWindow.alert')
+  @DomName('Window.alert')
   @DocsEditable
   void alert(String message) native;
 
-  @DomName('DOMWindow.atob')
+  @DomName('Window.atob')
   @DocsEditable
   String atob(String string) native;
 
-  @DomName('DOMWindow.btoa')
+  @DomName('Window.btoa')
   @DocsEditable
   String btoa(String string) native;
 
-  @DomName('DOMWindow.captureEvents')
+  @DomName('Window.captureEvents')
   @DocsEditable
   void captureEvents() native;
 
   @JSName('clearInterval')
-  @DomName('DOMWindow.clearInterval')
+  @DomName('Window.clearInterval')
   @DocsEditable
   void _clearInterval(int handle) native;
 
   @JSName('clearTimeout')
-  @DomName('DOMWindow.clearTimeout')
+  @DomName('Window.clearTimeout')
   @DocsEditable
   void _clearTimeout(int handle) native;
 
-  @DomName('DOMWindow.close')
+  @DomName('Window.close')
   @DocsEditable
   void close() native;
 
-  @DomName('DOMWindow.confirm')
+  @DomName('Window.confirm')
   @DocsEditable
   bool confirm(String message) native;
 
-  @DomName('DOMWindow.dispatchEvent')
+  @DomName('Window.dispatchEvent')
   @DocsEditable
   bool dispatchEvent(Event evt) native;
 
-  @DomName('DOMWindow.find')
+  @DomName('Window.find')
   @DocsEditable
   bool find(String string, bool caseSensitive, bool backwards, bool wrap, bool wholeWord, bool searchInFrames, bool showDialog) native;
 
   @JSName('getComputedStyle')
-  @DomName('DOMWindow.getComputedStyle')
+  @DomName('Window.getComputedStyle')
   @DocsEditable
   CssStyleDeclaration $dom_getComputedStyle(Element element, String pseudoElement) native;
 
   @JSName('getMatchedCSSRules')
-  @DomName('DOMWindow.getMatchedCSSRules')
+  @DomName('Window.getMatchedCSSRules')
   @DocsEditable
   @Returns('_CssRuleList')
   @Creates('_CssRuleList')
   List<CssRule> getMatchedCssRules(Element element, String pseudoElement) native;
 
-  @DomName('DOMWindow.getSelection')
+  @DomName('Window.getSelection')
   @DocsEditable
   Selection getSelection() native;
 
-  @DomName('DOMWindow.matchMedia')
+  @DomName('Window.matchMedia')
   @DocsEditable
   MediaQueryList matchMedia(String query) native;
 
-  @DomName('DOMWindow.moveBy')
+  @DomName('Window.moveBy')
   @DocsEditable
   void moveBy(num x, num y) native;
 
-  @DomName('DOMWindow.moveTo')
+  @DomName('Window.moveTo')
   @DocsEditable
   void moveTo(num x, num y) native;
 
-  @DomName('DOMWindow.openDatabase')
+  @DomName('Window.openDatabase')
   @DocsEditable
   @SupportedBrowser(SupportedBrowser.CHROME)
   @SupportedBrowser(SupportedBrowser.SAFARI)
@@ -22786,7 +20974,7 @@
   @Creates('SqlDatabase')
   SqlDatabase openDatabase(String name, String version, String displayName, int estimatedSize, [DatabaseCallback creationCallback]) native;
 
-  @DomName('DOMWindow.postMessage')
+  @DomName('Window.postMessage')
   @DocsEditable
   void postMessage(/*SerializedScriptValue*/ message, String targetOrigin, [List messagePorts]) {
     if (?messagePorts) {
@@ -22799,71 +20987,71 @@
     return;
   }
   @JSName('postMessage')
-  @DomName('DOMWindow.postMessage')
+  @DomName('Window.postMessage')
   @DocsEditable
   void _postMessage_1(message, targetOrigin, List messagePorts) native;
   @JSName('postMessage')
-  @DomName('DOMWindow.postMessage')
+  @DomName('Window.postMessage')
   @DocsEditable
   void _postMessage_2(message, targetOrigin) native;
 
-  @DomName('DOMWindow.print')
+  @DomName('Window.print')
   @DocsEditable
   void print() native;
 
-  @DomName('DOMWindow.releaseEvents')
+  @DomName('Window.releaseEvents')
   @DocsEditable
   void releaseEvents() native;
 
   @JSName('removeEventListener')
-  @DomName('DOMWindow.removeEventListener')
+  @DomName('Window.removeEventListener')
   @DocsEditable
   void $dom_removeEventListener(String type, EventListener listener, [bool useCapture]) native;
 
-  @DomName('DOMWindow.resizeBy')
+  @DomName('Window.resizeBy')
   @DocsEditable
   void resizeBy(num x, num y) native;
 
-  @DomName('DOMWindow.resizeTo')
+  @DomName('Window.resizeTo')
   @DocsEditable
   void resizeTo(num width, num height) native;
 
-  @DomName('DOMWindow.scroll')
+  @DomName('Window.scroll')
   @DocsEditable
   void scroll(int x, int y) native;
 
-  @DomName('DOMWindow.scrollBy')
+  @DomName('Window.scrollBy')
   @DocsEditable
   void scrollBy(int x, int y) native;
 
-  @DomName('DOMWindow.scrollTo')
+  @DomName('Window.scrollTo')
   @DocsEditable
   void scrollTo(int x, int y) native;
 
   @JSName('setInterval')
-  @DomName('DOMWindow.setInterval')
+  @DomName('Window.setInterval')
   @DocsEditable
   int _setInterval(Object handler, int timeout) native;
 
   @JSName('setTimeout')
-  @DomName('DOMWindow.setTimeout')
+  @DomName('Window.setTimeout')
   @DocsEditable
   int _setTimeout(Object handler, int timeout) native;
 
-  @DomName('DOMWindow.showModalDialog')
+  @DomName('Window.showModalDialog')
   @DocsEditable
   Object showModalDialog(String url, [Object dialogArgs, String featureArgs]) native;
 
-  @DomName('DOMWindow.stop')
+  @DomName('Window.stop')
   @DocsEditable
   void stop() native;
 
-  @DomName('DOMWindow.toString')
+  @DomName('Window.toString')
   @DocsEditable
   String toString() native;
 
   @JSName('webkitConvertPointFromNodeToPage')
-  @DomName('DOMWindow.webkitConvertPointFromNodeToPage')
+  @DomName('Window.webkitConvertPointFromNodeToPage')
   @DocsEditable
   @SupportedBrowser(SupportedBrowser.CHROME)
   @SupportedBrowser(SupportedBrowser.SAFARI)
@@ -22871,7 +21059,7 @@
   _DomPoint _convertPointFromNodeToPage(Node node, _DomPoint p) native;
 
   @JSName('webkitConvertPointFromPageToNode')
-  @DomName('DOMWindow.webkitConvertPointFromPageToNode')
+  @DomName('Window.webkitConvertPointFromPageToNode')
   @DocsEditable
   @SupportedBrowser(SupportedBrowser.CHROME)
   @SupportedBrowser(SupportedBrowser.SAFARI)
@@ -22879,14 +21067,14 @@
   _DomPoint _convertPointFromPageToNode(Node node, _DomPoint p) native;
 
   @JSName('webkitRequestFileSystem')
-  @DomName('DOMWindow.webkitRequestFileSystem')
+  @DomName('Window.webkitRequestFileSystem')
   @DocsEditable
   @SupportedBrowser(SupportedBrowser.CHROME)
   @Experimental
   void __requestFileSystem(int type, int size, _FileSystemCallback successCallback, [_ErrorCallback errorCallback]) native;
 
   @JSName('webkitRequestFileSystem')
-  @DomName('DOMWindow.webkitRequestFileSystem')
+  @DomName('Window.webkitRequestFileSystem')
   @DocsEditable
   @SupportedBrowser(SupportedBrowser.CHROME)
   @Experimental
@@ -22899,14 +21087,14 @@
   }
 
   @JSName('webkitResolveLocalFileSystemURL')
-  @DomName('DOMWindow.webkitResolveLocalFileSystemURL')
+  @DomName('Window.webkitResolveLocalFileSystemURL')
   @DocsEditable
   @SupportedBrowser(SupportedBrowser.CHROME)
   @Experimental
   void _resolveLocalFileSystemUrl(String url, _EntryCallback successCallback, [_ErrorCallback errorCallback]) native;
 
   @JSName('webkitResolveLocalFileSystemURL')
-  @DomName('DOMWindow.webkitResolveLocalFileSystemURL')
+  @DomName('Window.webkitResolveLocalFileSystemURL')
   @DocsEditable
   @SupportedBrowser(SupportedBrowser.CHROME)
   @Experimental
@@ -22918,215 +21106,215 @@
     return completer.future;
   }
 
-  @DomName('DOMWindow.onDOMContentLoaded')
+  @DomName('Window.onDOMContentLoaded')
   @DocsEditable
   Stream<Event> get onContentLoaded => contentLoadedEvent.forTarget(this);
 
-  @DomName('DOMWindow.onabort')
+  @DomName('Window.onabort')
   @DocsEditable
   Stream<Event> get onAbort => Element.abortEvent.forTarget(this);
 
-  @DomName('DOMWindow.onblur')
+  @DomName('Window.onblur')
   @DocsEditable
   Stream<Event> get onBlur => Element.blurEvent.forTarget(this);
 
-  @DomName('DOMWindow.onchange')
+  @DomName('Window.onchange')
   @DocsEditable
   Stream<Event> get onChange => Element.changeEvent.forTarget(this);
 
-  @DomName('DOMWindow.onclick')
+  @DomName('Window.onclick')
   @DocsEditable
   Stream<MouseEvent> get onClick => Element.clickEvent.forTarget(this);
 
-  @DomName('DOMWindow.oncontextmenu')
+  @DomName('Window.oncontextmenu')
   @DocsEditable
   Stream<MouseEvent> get onContextMenu => Element.contextMenuEvent.forTarget(this);
 
-  @DomName('DOMWindow.ondblclick')
+  @DomName('Window.ondblclick')
   @DocsEditable
   Stream<Event> get onDoubleClick => Element.doubleClickEvent.forTarget(this);
 
-  @DomName('DOMWindow.ondevicemotion')
+  @DomName('Window.ondevicemotion')
   @DocsEditable
   Stream<DeviceMotionEvent> get onDeviceMotion => deviceMotionEvent.forTarget(this);
 
-  @DomName('DOMWindow.ondeviceorientation')
+  @DomName('Window.ondeviceorientation')
   @DocsEditable
   Stream<DeviceOrientationEvent> get onDeviceOrientation => deviceOrientationEvent.forTarget(this);
 
-  @DomName('DOMWindow.ondrag')
+  @DomName('Window.ondrag')
   @DocsEditable
   Stream<MouseEvent> get onDrag => Element.dragEvent.forTarget(this);
 
-  @DomName('DOMWindow.ondragend')
+  @DomName('Window.ondragend')
   @DocsEditable
   Stream<MouseEvent> get onDragEnd => Element.dragEndEvent.forTarget(this);
 
-  @DomName('DOMWindow.ondragenter')
+  @DomName('Window.ondragenter')
   @DocsEditable
   Stream<MouseEvent> get onDragEnter => Element.dragEnterEvent.forTarget(this);
 
-  @DomName('DOMWindow.ondragleave')
+  @DomName('Window.ondragleave')
   @DocsEditable
   Stream<MouseEvent> get onDragLeave => Element.dragLeaveEvent.forTarget(this);
 
-  @DomName('DOMWindow.ondragover')
+  @DomName('Window.ondragover')
   @DocsEditable
   Stream<MouseEvent> get onDragOver => Element.dragOverEvent.forTarget(this);
 
-  @DomName('DOMWindow.ondragstart')
+  @DomName('Window.ondragstart')
   @DocsEditable
   Stream<MouseEvent> get onDragStart => Element.dragStartEvent.forTarget(this);
 
-  @DomName('DOMWindow.ondrop')
+  @DomName('Window.ondrop')
   @DocsEditable
   Stream<MouseEvent> get onDrop => Element.dropEvent.forTarget(this);
 
-  @DomName('DOMWindow.onerror')
+  @DomName('Window.onerror')
   @DocsEditable
   Stream<Event> get onError => Element.errorEvent.forTarget(this);
 
-  @DomName('DOMWindow.onfocus')
+  @DomName('Window.onfocus')
   @DocsEditable
   Stream<Event> get onFocus => Element.focusEvent.forTarget(this);
 
-  @DomName('DOMWindow.onhashchange')
+  @DomName('Window.onhashchange')
   @DocsEditable
   Stream<Event> get onHashChange => hashChangeEvent.forTarget(this);
 
-  @DomName('DOMWindow.oninput')
+  @DomName('Window.oninput')
   @DocsEditable
   Stream<Event> get onInput => Element.inputEvent.forTarget(this);
 
-  @DomName('DOMWindow.oninvalid')
+  @DomName('Window.oninvalid')
   @DocsEditable
   Stream<Event> get onInvalid => Element.invalidEvent.forTarget(this);
 
-  @DomName('DOMWindow.onkeydown')
+  @DomName('Window.onkeydown')
   @DocsEditable
   Stream<KeyboardEvent> get onKeyDown => Element.keyDownEvent.forTarget(this);
 
-  @DomName('DOMWindow.onkeypress')
+  @DomName('Window.onkeypress')
   @DocsEditable
   Stream<KeyboardEvent> get onKeyPress => Element.keyPressEvent.forTarget(this);
 
-  @DomName('DOMWindow.onkeyup')
+  @DomName('Window.onkeyup')
   @DocsEditable
   Stream<KeyboardEvent> get onKeyUp => Element.keyUpEvent.forTarget(this);
 
-  @DomName('DOMWindow.onload')
+  @DomName('Window.onload')
   @DocsEditable
   Stream<Event> get onLoad => Element.loadEvent.forTarget(this);
 
-  @DomName('DOMWindow.onmessage')
+  @DomName('Window.onmessage')
   @DocsEditable
   Stream<MessageEvent> get onMessage => messageEvent.forTarget(this);
 
-  @DomName('DOMWindow.onmousedown')
+  @DomName('Window.onmousedown')
   @DocsEditable
   Stream<MouseEvent> get onMouseDown => Element.mouseDownEvent.forTarget(this);
 
-  @DomName('DOMWindow.onmousemove')
+  @DomName('Window.onmousemove')
   @DocsEditable
   Stream<MouseEvent> get onMouseMove => Element.mouseMoveEvent.forTarget(this);
 
-  @DomName('DOMWindow.onmouseout')
+  @DomName('Window.onmouseout')
   @DocsEditable
   Stream<MouseEvent> get onMouseOut => Element.mouseOutEvent.forTarget(this);
 
-  @DomName('DOMWindow.onmouseover')
+  @DomName('Window.onmouseover')
   @DocsEditable
   Stream<MouseEvent> get onMouseOver => Element.mouseOverEvent.forTarget(this);
 
-  @DomName('DOMWindow.onmouseup')
+  @DomName('Window.onmouseup')
   @DocsEditable
   Stream<MouseEvent> get onMouseUp => Element.mouseUpEvent.forTarget(this);
 
-  @DomName('DOMWindow.onmousewheel')
+  @DomName('Window.onmousewheel')
   @DocsEditable
   Stream<WheelEvent> get onMouseWheel => Element.mouseWheelEvent.forTarget(this);
 
-  @DomName('DOMWindow.onoffline')
+  @DomName('Window.onoffline')
   @DocsEditable
   Stream<Event> get onOffline => offlineEvent.forTarget(this);
 
-  @DomName('DOMWindow.ononline')
+  @DomName('Window.ononline')
   @DocsEditable
   Stream<Event> get onOnline => onlineEvent.forTarget(this);
 
-  @DomName('DOMWindow.onpagehide')
+  @DomName('Window.onpagehide')
   @DocsEditable
   Stream<Event> get onPageHide => pageHideEvent.forTarget(this);
 
-  @DomName('DOMWindow.onpageshow')
+  @DomName('Window.onpageshow')
   @DocsEditable
   Stream<Event> get onPageShow => pageShowEvent.forTarget(this);
 
-  @DomName('DOMWindow.onpopstate')
+  @DomName('Window.onpopstate')
   @DocsEditable
   Stream<PopStateEvent> get onPopState => popStateEvent.forTarget(this);
 
-  @DomName('DOMWindow.onreset')
+  @DomName('Window.onreset')
   @DocsEditable
   Stream<Event> get onReset => Element.resetEvent.forTarget(this);
 
-  @DomName('DOMWindow.onresize')
+  @DomName('Window.onresize')
   @DocsEditable
   Stream<Event> get onResize => resizeEvent.forTarget(this);
 
-  @DomName('DOMWindow.onscroll')
+  @DomName('Window.onscroll')
   @DocsEditable
   Stream<Event> get onScroll => Element.scrollEvent.forTarget(this);
 
-  @DomName('DOMWindow.onsearch')
+  @DomName('Window.onsearch')
   @DocsEditable
   Stream<Event> get onSearch => Element.searchEvent.forTarget(this);
 
-  @DomName('DOMWindow.onselect')
+  @DomName('Window.onselect')
   @DocsEditable
   Stream<Event> get onSelect => Element.selectEvent.forTarget(this);
 
-  @DomName('DOMWindow.onstorage')
+  @DomName('Window.onstorage')
   @DocsEditable
   Stream<StorageEvent> get onStorage => storageEvent.forTarget(this);
 
-  @DomName('DOMWindow.onsubmit')
+  @DomName('Window.onsubmit')
   @DocsEditable
   Stream<Event> get onSubmit => Element.submitEvent.forTarget(this);
 
-  @DomName('DOMWindow.ontouchcancel')
+  @DomName('Window.ontouchcancel')
   @DocsEditable
   Stream<TouchEvent> get onTouchCancel => Element.touchCancelEvent.forTarget(this);
 
-  @DomName('DOMWindow.ontouchend')
+  @DomName('Window.ontouchend')
   @DocsEditable
   Stream<TouchEvent> get onTouchEnd => Element.touchEndEvent.forTarget(this);
 
-  @DomName('DOMWindow.ontouchmove')
+  @DomName('Window.ontouchmove')
   @DocsEditable
   Stream<TouchEvent> get onTouchMove => Element.touchMoveEvent.forTarget(this);
 
-  @DomName('DOMWindow.ontouchstart')
+  @DomName('Window.ontouchstart')
   @DocsEditable
   Stream<TouchEvent> get onTouchStart => Element.touchStartEvent.forTarget(this);
 
-  @DomName('DOMWindow.onunload')
+  @DomName('Window.onunload')
   @DocsEditable
   Stream<Event> get onUnload => unloadEvent.forTarget(this);
 
-  @DomName('DOMWindow.onwebkitAnimationEnd')
+  @DomName('Window.onwebkitAnimationEnd')
   @DocsEditable
   Stream<AnimationEvent> get onAnimationEnd => animationEndEvent.forTarget(this);
 
-  @DomName('DOMWindow.onwebkitAnimationIteration')
+  @DomName('Window.onwebkitAnimationIteration')
   @DocsEditable
   Stream<AnimationEvent> get onAnimationIteration => animationIterationEvent.forTarget(this);
 
-  @DomName('DOMWindow.onwebkitAnimationStart')
+  @DomName('Window.onwebkitAnimationStart')
   @DocsEditable
   Stream<AnimationEvent> get onAnimationStart => animationStartEvent.forTarget(this);
 
-  @DomName('DOMWindow.onwebkitTransitionEnd')
+  @DomName('Window.onwebkitTransitionEnd')
   @DocsEditable
   Stream<TransitionEvent> get onTransitionEnd => Element.transitionEndEvent.forTarget(this);
 
@@ -23607,7 +21795,7 @@
 
 @DocsEditable
 @DomName('ClientRectList')
-class _ClientRectList implements JavaScriptIndexingBehavior, List<Rect> native "ClientRectList" {
+class _ClientRectList extends Object with ListMixin<Rect>, ImmutableListMixin<Rect> implements JavaScriptIndexingBehavior, List<Rect> native "ClientRectList" {
 
   @DomName('ClientRectList.length')
   @DocsEditable
@@ -23621,196 +21809,11 @@
   // -- start List<Rect> mixins.
   // Rect is the element type.
 
-  // From Iterable<Rect>:
 
-  Iterator<Rect> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<Rect>(this);
-  }
-
-  Rect reduce(Rect combine(Rect value, Rect element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, Rect element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(Rect element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(Rect element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(Rect element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<Rect> where(bool f(Rect element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(Rect element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(Rect element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(Rect element)) => IterableMixinWorkaround.any(this, f);
-
-  List<Rect> toList({ bool growable: true }) =>
-      new List<Rect>.from(this, growable: growable);
-
-  Set<Rect> toSet() => new Set<Rect>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<Rect> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<Rect> takeWhile(bool test(Rect value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<Rect> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<Rect> skipWhile(bool test(Rect value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  Rect firstWhere(bool test(Rect value), { Rect orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  Rect lastWhere(bool test(Rect value), {Rect orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  Rect singleWhere(bool test(Rect value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  Rect elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<Rect>:
-
-  void add(Rect value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<Rect> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<Rect>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<Rect> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(Rect a, Rect b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(Rect element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(Rect element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  Rect get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  Rect get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  Rect get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, Rect element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<Rect> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<Rect> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Rect removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  Rect removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(Rect element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(Rect element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<Rect> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<Rect> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [Rect fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<Rect> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<Rect> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <Rect>[]);
-  }
-
-  Map<int, Rect> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<Rect> mixins.
 
   @DomName('ClientRectList.item')
@@ -23833,7 +21836,7 @@
 
 @DocsEditable
 @DomName('CSSRuleList')
-class _CssRuleList implements JavaScriptIndexingBehavior, List<CssRule> native "CSSRuleList" {
+class _CssRuleList extends Object with ListMixin<CssRule>, ImmutableListMixin<CssRule> implements JavaScriptIndexingBehavior, List<CssRule> native "CSSRuleList" {
 
   @DomName('CSSRuleList.length')
   @DocsEditable
@@ -23847,196 +21850,11 @@
   // -- start List<CssRule> mixins.
   // CssRule is the element type.
 
-  // From Iterable<CssRule>:
 
-  Iterator<CssRule> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<CssRule>(this);
-  }
-
-  CssRule reduce(CssRule combine(CssRule value, CssRule element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, CssRule element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(CssRule element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(CssRule element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(CssRule element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<CssRule> where(bool f(CssRule element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(CssRule element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(CssRule element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(CssRule element)) => IterableMixinWorkaround.any(this, f);
-
-  List<CssRule> toList({ bool growable: true }) =>
-      new List<CssRule>.from(this, growable: growable);
-
-  Set<CssRule> toSet() => new Set<CssRule>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<CssRule> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<CssRule> takeWhile(bool test(CssRule value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<CssRule> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<CssRule> skipWhile(bool test(CssRule value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  CssRule firstWhere(bool test(CssRule value), { CssRule orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  CssRule lastWhere(bool test(CssRule value), {CssRule orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  CssRule singleWhere(bool test(CssRule value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  CssRule elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<CssRule>:
-
-  void add(CssRule value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<CssRule> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<CssRule>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<CssRule> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(CssRule a, CssRule b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(CssRule element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(CssRule element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  CssRule get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  CssRule get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  CssRule get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, CssRule element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<CssRule> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<CssRule> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  CssRule removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  CssRule removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(CssRule element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(CssRule element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<CssRule> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<CssRule> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [CssRule fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<CssRule> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<CssRule> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <CssRule>[]);
-  }
-
-  Map<int, CssRule> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<CssRule> mixins.
 
   @DomName('CSSRuleList.item')
@@ -24050,7 +21868,7 @@
 
 @DocsEditable
 @DomName('CSSValueList')
-class _CssValueList extends _CSSValue implements JavaScriptIndexingBehavior, List<_CSSValue> native "CSSValueList" {
+class _CssValueList extends _CSSValue with ListMixin<_CSSValue>, ImmutableListMixin<_CSSValue> implements JavaScriptIndexingBehavior, List<_CSSValue> native "CSSValueList" {
 
   @DomName('CSSValueList.length')
   @DocsEditable
@@ -24064,196 +21882,11 @@
   // -- start List<_CSSValue> mixins.
   // _CSSValue is the element type.
 
-  // From Iterable<_CSSValue>:
 
-  Iterator<_CSSValue> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<_CSSValue>(this);
-  }
-
-  _CSSValue reduce(_CSSValue combine(_CSSValue value, _CSSValue element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, _CSSValue element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(_CSSValue element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(_CSSValue element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(_CSSValue element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<_CSSValue> where(bool f(_CSSValue element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(_CSSValue element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(_CSSValue element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(_CSSValue element)) => IterableMixinWorkaround.any(this, f);
-
-  List<_CSSValue> toList({ bool growable: true }) =>
-      new List<_CSSValue>.from(this, growable: growable);
-
-  Set<_CSSValue> toSet() => new Set<_CSSValue>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<_CSSValue> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<_CSSValue> takeWhile(bool test(_CSSValue value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<_CSSValue> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<_CSSValue> skipWhile(bool test(_CSSValue value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  _CSSValue firstWhere(bool test(_CSSValue value), { _CSSValue orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  _CSSValue lastWhere(bool test(_CSSValue value), {_CSSValue orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  _CSSValue singleWhere(bool test(_CSSValue value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  _CSSValue elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<_CSSValue>:
-
-  void add(_CSSValue value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<_CSSValue> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<_CSSValue>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<_CSSValue> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(_CSSValue a, _CSSValue b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(_CSSValue element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(_CSSValue element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  _CSSValue get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  _CSSValue get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  _CSSValue get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, _CSSValue element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<_CSSValue> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<_CSSValue> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  _CSSValue removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  _CSSValue removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(_CSSValue element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(_CSSValue element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<_CSSValue> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<_CSSValue> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [_CSSValue fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<_CSSValue> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<_CSSValue> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <_CSSValue>[]);
-  }
-
-  Map<int, _CSSValue> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<_CSSValue> mixins.
 
   @DomName('CSSValueList.item')
@@ -24320,12 +21953,9 @@
 @SupportedBrowser(SupportedBrowser.CHROME)
 @SupportedBrowser(SupportedBrowser.SAFARI)
 @Experimental
-@SupportedBrowser(SupportedBrowser.CHROME)
-@SupportedBrowser(SupportedBrowser.SAFARI)
-@Experimental
 class _DomPoint native "WebKitPoint" {
 
-  @DomName('DOMPoint.DOMPoint')
+  @DomName('WebKitPoint.DOMPoint')
   @DocsEditable
   factory _DomPoint(num x, num y) {
     return _DomPoint._create_1(x, y);
@@ -24335,11 +21965,11 @@
   /// Checks if this type is supported on the current platform.
   static bool get supported => JS('bool', '!!(window.WebKitPoint)');
 
-  @DomName('DOMPoint.x')
+  @DomName('WebKitPoint.x')
   @DocsEditable
   num x;
 
-  @DomName('DOMPoint.y')
+  @DomName('WebKitPoint.y')
   @DocsEditable
   num y;
 }
@@ -24359,7 +21989,7 @@
 
 @DocsEditable
 @DomName('EntryArray')
-class _EntryArray implements JavaScriptIndexingBehavior, List<Entry> native "EntryArray" {
+class _EntryArray extends Object with ListMixin<Entry>, ImmutableListMixin<Entry> implements JavaScriptIndexingBehavior, List<Entry> native "EntryArray" {
 
   @DomName('EntryArray.length')
   @DocsEditable
@@ -24373,196 +22003,11 @@
   // -- start List<Entry> mixins.
   // Entry is the element type.
 
-  // From Iterable<Entry>:
 
-  Iterator<Entry> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<Entry>(this);
-  }
-
-  Entry reduce(Entry combine(Entry value, Entry element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, Entry element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(Entry element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(Entry element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(Entry element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<Entry> where(bool f(Entry element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(Entry element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(Entry element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(Entry element)) => IterableMixinWorkaround.any(this, f);
-
-  List<Entry> toList({ bool growable: true }) =>
-      new List<Entry>.from(this, growable: growable);
-
-  Set<Entry> toSet() => new Set<Entry>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<Entry> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<Entry> takeWhile(bool test(Entry value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<Entry> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<Entry> skipWhile(bool test(Entry value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  Entry firstWhere(bool test(Entry value), { Entry orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  Entry lastWhere(bool test(Entry value), {Entry orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  Entry singleWhere(bool test(Entry value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  Entry elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<Entry>:
-
-  void add(Entry value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<Entry> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<Entry>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<Entry> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(Entry a, Entry b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(Entry element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(Entry element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  Entry get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  Entry get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  Entry get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, Entry element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<Entry> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<Entry> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Entry removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  Entry removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(Entry element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(Entry element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<Entry> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<Entry> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [Entry fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<Entry> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<Entry> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <Entry>[]);
-  }
-
-  Map<int, Entry> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<Entry> mixins.
 
   @DomName('EntryArray.item')
@@ -24576,7 +22021,7 @@
 
 @DocsEditable
 @DomName('EntryArraySync')
-class _EntryArraySync implements JavaScriptIndexingBehavior, List<_EntrySync> native "EntryArraySync" {
+class _EntryArraySync extends Object with ListMixin<_EntrySync>, ImmutableListMixin<_EntrySync> implements JavaScriptIndexingBehavior, List<_EntrySync> native "EntryArraySync" {
 
   @DomName('EntryArraySync.length')
   @DocsEditable
@@ -24590,196 +22035,11 @@
   // -- start List<_EntrySync> mixins.
   // _EntrySync is the element type.
 
-  // From Iterable<_EntrySync>:
 
-  Iterator<_EntrySync> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<_EntrySync>(this);
-  }
-
-  _EntrySync reduce(_EntrySync combine(_EntrySync value, _EntrySync element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, _EntrySync element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(_EntrySync element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(_EntrySync element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(_EntrySync element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<_EntrySync> where(bool f(_EntrySync element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(_EntrySync element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(_EntrySync element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(_EntrySync element)) => IterableMixinWorkaround.any(this, f);
-
-  List<_EntrySync> toList({ bool growable: true }) =>
-      new List<_EntrySync>.from(this, growable: growable);
-
-  Set<_EntrySync> toSet() => new Set<_EntrySync>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<_EntrySync> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<_EntrySync> takeWhile(bool test(_EntrySync value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<_EntrySync> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<_EntrySync> skipWhile(bool test(_EntrySync value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  _EntrySync firstWhere(bool test(_EntrySync value), { _EntrySync orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  _EntrySync lastWhere(bool test(_EntrySync value), {_EntrySync orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  _EntrySync singleWhere(bool test(_EntrySync value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  _EntrySync elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<_EntrySync>:
-
-  void add(_EntrySync value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<_EntrySync> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<_EntrySync>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<_EntrySync> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(_EntrySync a, _EntrySync b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(_EntrySync element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(_EntrySync element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  _EntrySync get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  _EntrySync get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  _EntrySync get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, _EntrySync element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<_EntrySync> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<_EntrySync> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  _EntrySync removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  _EntrySync removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(_EntrySync element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(_EntrySync element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<_EntrySync> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<_EntrySync> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [_EntrySync fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<_EntrySync> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<_EntrySync> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <_EntrySync>[]);
-  }
-
-  Map<int, _EntrySync> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<_EntrySync> mixins.
 
   @DomName('EntryArraySync.item')
@@ -24836,7 +22096,7 @@
 
 @DocsEditable
 @DomName('GamepadList')
-class _GamepadList implements JavaScriptIndexingBehavior, List<Gamepad> native "GamepadList" {
+class _GamepadList extends Object with ListMixin<Gamepad>, ImmutableListMixin<Gamepad> implements JavaScriptIndexingBehavior, List<Gamepad> native "GamepadList" {
 
   @DomName('GamepadList.length')
   @DocsEditable
@@ -24850,196 +22110,11 @@
   // -- start List<Gamepad> mixins.
   // Gamepad is the element type.
 
-  // From Iterable<Gamepad>:
 
-  Iterator<Gamepad> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<Gamepad>(this);
-  }
-
-  Gamepad reduce(Gamepad combine(Gamepad value, Gamepad element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, Gamepad element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(Gamepad element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(Gamepad element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(Gamepad element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<Gamepad> where(bool f(Gamepad element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(Gamepad element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(Gamepad element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(Gamepad element)) => IterableMixinWorkaround.any(this, f);
-
-  List<Gamepad> toList({ bool growable: true }) =>
-      new List<Gamepad>.from(this, growable: growable);
-
-  Set<Gamepad> toSet() => new Set<Gamepad>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<Gamepad> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<Gamepad> takeWhile(bool test(Gamepad value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<Gamepad> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<Gamepad> skipWhile(bool test(Gamepad value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  Gamepad firstWhere(bool test(Gamepad value), { Gamepad orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  Gamepad lastWhere(bool test(Gamepad value), {Gamepad orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  Gamepad singleWhere(bool test(Gamepad value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  Gamepad elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<Gamepad>:
-
-  void add(Gamepad value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<Gamepad> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<Gamepad>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<Gamepad> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(Gamepad a, Gamepad b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(Gamepad element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(Gamepad element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  Gamepad get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  Gamepad get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  Gamepad get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, Gamepad element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<Gamepad> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<Gamepad> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Gamepad removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  Gamepad removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(Gamepad element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(Gamepad element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<Gamepad> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<Gamepad> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [Gamepad fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<Gamepad> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<Gamepad> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <Gamepad>[]);
-  }
-
-  Map<int, Gamepad> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<Gamepad> mixins.
 
   @DomName('GamepadList.item')
@@ -25116,7 +22191,7 @@
 
 @DocsEditable
 @DomName('NamedNodeMap')
-class _NamedNodeMap implements JavaScriptIndexingBehavior, List<Node> native "NamedNodeMap" {
+class _NamedNodeMap extends Object with ListMixin<Node>, ImmutableListMixin<Node> implements JavaScriptIndexingBehavior, List<Node> native "NamedNodeMap" {
 
   @DomName('NamedNodeMap.length')
   @DocsEditable
@@ -25130,196 +22205,11 @@
   // -- start List<Node> mixins.
   // Node is the element type.
 
-  // From Iterable<Node>:
 
-  Iterator<Node> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<Node>(this);
-  }
-
-  Node reduce(Node combine(Node value, Node element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, Node element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(Node element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(Node element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(Node element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<Node> where(bool f(Node element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(Node element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(Node element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(Node element)) => IterableMixinWorkaround.any(this, f);
-
-  List<Node> toList({ bool growable: true }) =>
-      new List<Node>.from(this, growable: growable);
-
-  Set<Node> toSet() => new Set<Node>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<Node> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<Node> takeWhile(bool test(Node value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<Node> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<Node> skipWhile(bool test(Node value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  Node firstWhere(bool test(Node value), { Node orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  Node lastWhere(bool test(Node value), {Node orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  Node singleWhere(bool test(Node value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  Node elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<Node>:
-
-  void add(Node value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<Node>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<Node> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(Node a, Node b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(Node element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(Node element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  Node get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  Node get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  Node get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, Node element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Node removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  Node removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(Node element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(Node element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<Node> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [Node fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<Node> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<Node> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <Node>[]);
-  }
-
-  Map<int, Node> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<Node> mixins.
 
   @DomName('NamedNodeMap.getNamedItem')
@@ -25422,7 +22312,7 @@
 
 @DocsEditable
 @DomName('SpeechInputResultList')
-class _SpeechInputResultList implements JavaScriptIndexingBehavior, List<SpeechInputResult> native "SpeechInputResultList" {
+class _SpeechInputResultList extends Object with ListMixin<SpeechInputResult>, ImmutableListMixin<SpeechInputResult> implements JavaScriptIndexingBehavior, List<SpeechInputResult> native "SpeechInputResultList" {
 
   @DomName('SpeechInputResultList.length')
   @DocsEditable
@@ -25436,196 +22326,11 @@
   // -- start List<SpeechInputResult> mixins.
   // SpeechInputResult is the element type.
 
-  // From Iterable<SpeechInputResult>:
 
-  Iterator<SpeechInputResult> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<SpeechInputResult>(this);
-  }
-
-  SpeechInputResult reduce(SpeechInputResult combine(SpeechInputResult value, SpeechInputResult element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, SpeechInputResult element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(SpeechInputResult element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(SpeechInputResult element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(SpeechInputResult element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<SpeechInputResult> where(bool f(SpeechInputResult element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(SpeechInputResult element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(SpeechInputResult element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(SpeechInputResult element)) => IterableMixinWorkaround.any(this, f);
-
-  List<SpeechInputResult> toList({ bool growable: true }) =>
-      new List<SpeechInputResult>.from(this, growable: growable);
-
-  Set<SpeechInputResult> toSet() => new Set<SpeechInputResult>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<SpeechInputResult> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<SpeechInputResult> takeWhile(bool test(SpeechInputResult value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<SpeechInputResult> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<SpeechInputResult> skipWhile(bool test(SpeechInputResult value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  SpeechInputResult firstWhere(bool test(SpeechInputResult value), { SpeechInputResult orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  SpeechInputResult lastWhere(bool test(SpeechInputResult value), {SpeechInputResult orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  SpeechInputResult singleWhere(bool test(SpeechInputResult value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  SpeechInputResult elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<SpeechInputResult>:
-
-  void add(SpeechInputResult value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<SpeechInputResult> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<SpeechInputResult>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<SpeechInputResult> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(SpeechInputResult a, SpeechInputResult b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(SpeechInputResult element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(SpeechInputResult element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  SpeechInputResult get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  SpeechInputResult get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  SpeechInputResult get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, SpeechInputResult element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<SpeechInputResult> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<SpeechInputResult> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  SpeechInputResult removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  SpeechInputResult removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(SpeechInputResult element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(SpeechInputResult element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<SpeechInputResult> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<SpeechInputResult> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [SpeechInputResult fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<SpeechInputResult> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<SpeechInputResult> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <SpeechInputResult>[]);
-  }
-
-  Map<int, SpeechInputResult> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<SpeechInputResult> mixins.
 
   @DomName('SpeechInputResultList.item')
@@ -25639,7 +22344,7 @@
 
 @DocsEditable
 @DomName('SpeechRecognitionResultList')
-class _SpeechRecognitionResultList implements JavaScriptIndexingBehavior, List<SpeechRecognitionResult> native "SpeechRecognitionResultList" {
+class _SpeechRecognitionResultList extends Object with ListMixin<SpeechRecognitionResult>, ImmutableListMixin<SpeechRecognitionResult> implements JavaScriptIndexingBehavior, List<SpeechRecognitionResult> native "SpeechRecognitionResultList" {
 
   @DomName('SpeechRecognitionResultList.length')
   @DocsEditable
@@ -25653,196 +22358,11 @@
   // -- start List<SpeechRecognitionResult> mixins.
   // SpeechRecognitionResult is the element type.
 
-  // From Iterable<SpeechRecognitionResult>:
 
-  Iterator<SpeechRecognitionResult> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<SpeechRecognitionResult>(this);
-  }
-
-  SpeechRecognitionResult reduce(SpeechRecognitionResult combine(SpeechRecognitionResult value, SpeechRecognitionResult element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, SpeechRecognitionResult element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(SpeechRecognitionResult element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(SpeechRecognitionResult element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(SpeechRecognitionResult element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<SpeechRecognitionResult> where(bool f(SpeechRecognitionResult element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(SpeechRecognitionResult element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(SpeechRecognitionResult element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(SpeechRecognitionResult element)) => IterableMixinWorkaround.any(this, f);
-
-  List<SpeechRecognitionResult> toList({ bool growable: true }) =>
-      new List<SpeechRecognitionResult>.from(this, growable: growable);
-
-  Set<SpeechRecognitionResult> toSet() => new Set<SpeechRecognitionResult>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<SpeechRecognitionResult> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<SpeechRecognitionResult> takeWhile(bool test(SpeechRecognitionResult value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<SpeechRecognitionResult> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<SpeechRecognitionResult> skipWhile(bool test(SpeechRecognitionResult value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  SpeechRecognitionResult firstWhere(bool test(SpeechRecognitionResult value), { SpeechRecognitionResult orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  SpeechRecognitionResult lastWhere(bool test(SpeechRecognitionResult value), {SpeechRecognitionResult orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  SpeechRecognitionResult singleWhere(bool test(SpeechRecognitionResult value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  SpeechRecognitionResult elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<SpeechRecognitionResult>:
-
-  void add(SpeechRecognitionResult value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<SpeechRecognitionResult> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<SpeechRecognitionResult>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<SpeechRecognitionResult> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(SpeechRecognitionResult a, SpeechRecognitionResult b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(SpeechRecognitionResult element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(SpeechRecognitionResult element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  SpeechRecognitionResult get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  SpeechRecognitionResult get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  SpeechRecognitionResult get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, SpeechRecognitionResult element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<SpeechRecognitionResult> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<SpeechRecognitionResult> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  SpeechRecognitionResult removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  SpeechRecognitionResult removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(SpeechRecognitionResult element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(SpeechRecognitionResult element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<SpeechRecognitionResult> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<SpeechRecognitionResult> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [SpeechRecognitionResult fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<SpeechRecognitionResult> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<SpeechRecognitionResult> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <SpeechRecognitionResult>[]);
-  }
-
-  Map<int, SpeechRecognitionResult> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<SpeechRecognitionResult> mixins.
 
   @DomName('SpeechRecognitionResultList.item')
@@ -25856,7 +22376,7 @@
 
 @DocsEditable
 @DomName('StyleSheetList')
-class _StyleSheetList implements JavaScriptIndexingBehavior, List<StyleSheet> native "StyleSheetList" {
+class _StyleSheetList extends Object with ListMixin<StyleSheet>, ImmutableListMixin<StyleSheet> implements JavaScriptIndexingBehavior, List<StyleSheet> native "StyleSheetList" {
 
   @DomName('StyleSheetList.length')
   @DocsEditable
@@ -25870,196 +22390,11 @@
   // -- start List<StyleSheet> mixins.
   // StyleSheet is the element type.
 
-  // From Iterable<StyleSheet>:
 
-  Iterator<StyleSheet> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<StyleSheet>(this);
-  }
-
-  StyleSheet reduce(StyleSheet combine(StyleSheet value, StyleSheet element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, StyleSheet element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(StyleSheet element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(StyleSheet element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(StyleSheet element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<StyleSheet> where(bool f(StyleSheet element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(StyleSheet element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(StyleSheet element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(StyleSheet element)) => IterableMixinWorkaround.any(this, f);
-
-  List<StyleSheet> toList({ bool growable: true }) =>
-      new List<StyleSheet>.from(this, growable: growable);
-
-  Set<StyleSheet> toSet() => new Set<StyleSheet>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<StyleSheet> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<StyleSheet> takeWhile(bool test(StyleSheet value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<StyleSheet> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<StyleSheet> skipWhile(bool test(StyleSheet value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  StyleSheet firstWhere(bool test(StyleSheet value), { StyleSheet orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  StyleSheet lastWhere(bool test(StyleSheet value), {StyleSheet orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  StyleSheet singleWhere(bool test(StyleSheet value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  StyleSheet elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<StyleSheet>:
-
-  void add(StyleSheet value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<StyleSheet> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<StyleSheet>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<StyleSheet> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(StyleSheet a, StyleSheet b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(StyleSheet element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(StyleSheet element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  StyleSheet get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  StyleSheet get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  StyleSheet get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, StyleSheet element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<StyleSheet> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<StyleSheet> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  StyleSheet removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  StyleSheet removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(StyleSheet element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(StyleSheet element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<StyleSheet> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<StyleSheet> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [StyleSheet fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<StyleSheet> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<StyleSheet> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <StyleSheet>[]);
-  }
-
-  Map<int, StyleSheet> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<StyleSheet> mixins.
 
   @DomName('StyleSheetList.item')
@@ -26856,6 +23191,82 @@
 // BSD-style license that can be found in the LICENSE file.
 
 
+abstract class ImmutableListMixin<E> implements List<E> {
+  // From Iterable<$E>:
+  Iterator<E> get iterator {
+    // Note: NodeLists are not fixed size. And most probably length shouldn't
+    // be cached in both iterator _and_ forEach method. For now caching it
+    // for consistency.
+    return new FixedSizeListIterator<E>(this);
+  }
+
+  // From Collection<E>:
+  void add(E value) {
+    throw new UnsupportedError("Cannot add to immutable List.");
+  }
+
+  void addAll(Iterable<E> iterable) {
+    throw new UnsupportedError("Cannot add to immutable List.");
+  }
+
+  // From List<E>:
+  void sort([int compare(E a, E b)]) {
+    throw new UnsupportedError("Cannot sort immutable List.");
+  }
+
+  void insert(int index, E element) {
+    throw new UnsupportedError("Cannot add to immutable List.");
+  }
+
+  void insertAll(int index, Iterable<E> iterable) {
+    throw new UnsupportedError("Cannot add to immutable List.");
+  }
+
+  void setAll(int index, Iterable<E> iterable) {
+    throw new UnsupportedError("Cannot modify an immutable List.");
+  }
+
+  E removeAt(int pos) {
+    throw new UnsupportedError("Cannot remove from immutable List.");
+  }
+
+  E removeLast() {
+    throw new UnsupportedError("Cannot remove from immutable List.");
+  }
+
+  void remove(Object object) {
+    throw new UnsupportedError("Cannot remove from immutable List.");
+  }
+
+  void removeWhere(bool test(E element)) {
+    throw new UnsupportedError("Cannot remove from immutable List.");
+  }
+
+  void retainWhere(bool test(E element)) {
+    throw new UnsupportedError("Cannot remove from immutable List.");
+  }
+
+  void setRange(int start, int end, Iterable<E> iterable, [int skipCount]) {
+    throw new UnsupportedError("Cannot setRange on immutable List.");
+  }
+
+  void removeRange(int start, int end) {
+    throw new UnsupportedError("Cannot removeRange on immutable List.");
+  }
+
+  void replaceRange(int start, int end, Iterable<E> iterable) {
+    throw new UnsupportedError("Cannot modify an immutable List.");
+  }
+
+  void fillRange(int start, int end, [E fillValue]) {
+    throw new UnsupportedError("Cannot modify an immutable List.");
+  }
+}
+// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+
 /**
  * Internal class that does the actual calculations to determine keyCode and
  * charCode for keydown, keypress, and keyup events for all browsers.
@@ -28026,100 +24437,286 @@
 // BSD-style license that can be found in the LICENSE file.
 
 
-class _ModelTreeObserver {
-  static bool _initialized = false;
+// This code is inspired by ChangeSummary:
+// https://github.com/rafaelw/ChangeSummary/blob/master/change_summary.js
+// ...which underlies MDV. Since we don't need the functionality of
+// ChangeSummary, we just implement what we need for data bindings.
+// This allows our implementation to be much simpler.
+
+// TODO(jmesserly): should we make these types stronger, and require
+// Observable objects? Currently, it is fine to say something like:
+//     var path = new PathObserver(123, '');
+//     print(path.value); // "123"
+//
+// Furthermore this degenerate case is allowed:
+//     var path = new PathObserver(123, 'foo.bar.baz.qux');
+//     print(path.value); // "null"
+//
+// Here we see that any invalid (i.e. not Observable) value will break the
+// path chain without producing an error or exception.
+//
+// Now the real question: should we do this? For the former case, the behavior
+// is correct but we could chose to handle it in the dart:html bindings layer.
+// For the latter case, it might be better to throw an error so users can find
+// the problem.
+
+
+/**
+ * A data-bound path starting from a view-model or model object, for example
+ * `foo.bar.baz`.
+ *
+ * When the [values] stream is being listened to, this will observe changes to
+ * the object and any intermediate object along the path, and send [values]
+ * accordingly. When all listeners are unregistered it will stop observing
+ * the objects.
+ *
+ * This class is used to implement [Node.bind] and similar functionality.
+ */
+// TODO(jmesserly): find a better home for this type.
+@Experimental
+class PathObserver {
+  /** The object being observed. */
+  final object;
+
+  /** The path string. */
+  final String path;
+
+  /** True if the path is valid, otherwise false. */
+  final bool _isValid;
+
+  // TODO(jmesserly): same issue here as ObservableMixin: is there an easier
+  // way to get a broadcast stream?
+  StreamController _values;
+  Stream _valueStream;
+
+  _PropertyObserver _observer, _lastObserver;
+
+  Object _lastValue;
+  bool _scheduled = false;
 
   /**
-   * Start an observer watching the document for tree changes to automatically
-   * propagate model changes.
-   *
-   * Currently this does not support propagation through Shadow DOMs.
+   * Observes [path] on [object] for changes. This returns an object that can be
+   * used to get the changes and get/set the value at this path.
+   * See [PathObserver.values] and [PathObserver.value].
    */
-  static void initialize() {
-    if (!_initialized) {
-      _initialized = true;
+  PathObserver(this.object, String path)
+    : path = path, _isValid = _isPathValid(path) {
 
-      if (MutationObserver.supported) {
-        var observer = new MutationObserver(_processTreeChange);
-        observer.observe(document, childList: true, subtree: true);
-      } else {
-        document.on['DOMNodeInserted'].listen(_handleNodeInserted);
-        document.on['DOMNodeRemoved'].listen(_handleNodeRemoved);
+    // TODO(jmesserly): if the path is empty, or the object is! Observable, we
+    // can optimize the PathObserver to be more lightweight.
+
+    _values = new StreamController(onListen: _observe, onCancel: _unobserve);
+
+    if (_isValid) {
+      var segments = [];
+      for (var segment in path.trim().split('.')) {
+        if (segment == '') continue;
+        var index = int.parse(segment, onError: (_) {});
+        segments.add(index != null ? index : new Symbol(segment));
+      }
+
+      // Create the property observer linked list.
+      // Note that the structure of a path can't change after it is initially
+      // constructed, even though the objects along the path can change.
+      for (int i = segments.length - 1; i >= 0; i--) {
+        _observer = new _PropertyObserver(this, segments[i], _observer);
+        if (_lastObserver == null) _lastObserver = _observer;
       }
     }
   }
 
-  static void _processTreeChange(List<MutationRecord> mutations,
-      MutationObserver observer) {
-    for (var record in mutations) {
-      for (var node in record.addedNodes) {
-        // When nodes enter the document we need to make sure that all of the
-        // models are properly propagated through the entire sub-tree.
-        propagateModel(node, _calculatedModel(node), true);
-      }
-      for (var node in record.removedNodes) {
-        propagateModel(node, _calculatedModel(node), false);
-      }
-    }
-  }
-
-  static void _handleNodeInserted(MutationEvent e) {
-    var node = e.target;
-    window.setImmediate(() {
-      propagateModel(node, _calculatedModel(node), true);
-    });
-  }
-
-  static void _handleNodeRemoved(MutationEvent e) {
-    var node = e.target;
-    window.setImmediate(() {
-      propagateModel(node, _calculatedModel(node), false);
-    });
-  }
-
+  // TODO(jmesserly): we could try adding the first value to the stream, but
+  // that delivers the first record async.
   /**
-   * Figures out what the model should be for a node, avoiding any cached
-   * model values.
+   * Listens to the stream, and invokes the [callback] immediately with the
+   * current [value]. This is useful for bindings, which want to be up-to-date
+   * immediately.
    */
-  static _calculatedModel(node) {
-    if (node._hasLocalModel == true) {
-      return node._model;
-    } else if (node.parentNode != null) {
-      return node.parentNode._model;
-    }
-    return null;
+  StreamSubscription bindSync(void callback(value)) {
+    var result = values.listen(callback);
+    callback(value);
+    return result;
   }
 
+  // TODO(jmesserly): should this be a change record with the old value?
+  // TODO(jmesserly): should this be a broadcast stream? We only need
+  // single-subscription in the bindings system, so single sub saves overhead.
   /**
-   * Pushes model changes down through the tree.
-   *
-   * Set fullTree to true if the state of the tree is unknown and model changes
-   * should be propagated through the entire tree.
+   * Gets the stream of values that were observed at this path.
+   * This returns a single-subscription stream.
    */
-  static void propagateModel(Node node, model, bool fullTree) {
-    // Calling into user code with the != call could generate exceptions.
-    // Catch and report them a global exceptions.
-    try {
-      if (node._hasLocalModel != true && node._model != model &&
-          node._modelChangedStreams != null &&
-          !node._modelChangedStreams.isEmpty) {
-        node._model = model;
-        node._modelChangedStreams.toList()
-          .forEach((controller) => controller.add(node));
-      }
-    } catch (e, s) {
-      new Future.error(e, s);
+  Stream get values => _values.stream;
+
+  /** Force synchronous delivery of [values]. */
+  void _deliverValues() {
+    _scheduled = false;
+
+    var newValue = value;
+    if (!identical(_lastValue, newValue)) {
+      _values.add(newValue);
+      _lastValue = newValue;
     }
-    for (var child = node.$dom_firstChild; child != null;
-        child = child.nextNode) {
-      if (child._hasLocalModel != true) {
-        propagateModel(child, model, fullTree);
-      } else if (fullTree) {
-        propagateModel(child, child._model, true);
+  }
+
+  void _observe() {
+    if (_observer != null) {
+      _lastValue = value;
+      _observer.observe();
+    }
+  }
+
+  void _unobserve() {
+    if (_observer != null) _observer.unobserve();
+  }
+
+  void _notifyChange() {
+    if (_scheduled) return;
+    _scheduled = true;
+
+    // TODO(jmesserly): should we have a guarenteed order with respect to other
+    // paths? If so, we could implement this fairly easily by sorting instances
+    // of this class by birth order before delivery.
+    queueChangeRecords(_deliverValues);
+  }
+
+  /** Gets the last reported value at this path. */
+  get value {
+    if (!_isValid) return null;
+    if (_observer == null) return object;
+    _observer.ensureValue(object);
+    return _lastObserver.value;
+  }
+
+  /** Sets the value at this path. */
+  void set value(Object value) {
+    // TODO(jmesserly): throw if property cannot be set?
+    // MDV seems tolerant of these error.
+    if (_observer == null || !_isValid) return;
+    _observer.ensureValue(object);
+    var last = _lastObserver;
+    if (_setObjectProperty(last._object, last._property, value)) {
+      // Technically, this would get updated asynchronously via a change record.
+      // However, it is nice if calling the getter will yield the same value
+      // that was just set. So we use this opportunity to update our cache.
+      last.value = value;
+    }
+  }
+}
+
+// TODO(jmesserly): these should go away in favor of mirrors!
+_getObjectProperty(object, property) {
+  if (object is List && property is int) {
+    if (property >= 0 && property < object.length) {
+      return object[property];
+    } else {
+      return null;
+    }
+  }
+
+  // TODO(jmesserly): what about length?
+  if (object is Map) return object[property];
+
+  if (object is Observable) return object.getValueWorkaround(property);
+
+  return null;
+}
+
+bool _setObjectProperty(object, property, value) {
+  if (object is List && property is int) {
+    object[property] = value;
+  } else if (object is Map) {
+    object[property] = value;
+  } else if (object is Observable) {
+    (object as Observable).setValueWorkaround(property, value);
+  } else {
+    return false;
+  }
+  return true;
+}
+
+
+class _PropertyObserver {
+  final PathObserver _path;
+  final _property;
+  final _PropertyObserver _next;
+
+  // TODO(jmesserly): would be nice not to store both of these.
+  Object _object;
+  Object _value;
+  StreamSubscription _sub;
+
+  _PropertyObserver(this._path, this._property, this._next);
+
+  get value => _value;
+
+  void set value(Object newValue) {
+    _value = newValue;
+    if (_next != null) {
+      if (_sub != null) _next.unobserve();
+      _next.ensureValue(_value);
+      if (_sub != null) _next.observe();
+    }
+  }
+
+  void ensureValue(object) {
+    // If we're observing, values should be up to date already.
+    if (_sub != null) return;
+
+    _object = object;
+    value = _getObjectProperty(object, _property);
+  }
+
+  void observe() {
+    if (_object is Observable) {
+      assert(_sub == null);
+      _sub = (_object as Observable).changes.listen(_onChange);
+    }
+    if (_next != null) _next.observe();
+  }
+
+  void unobserve() {
+    if (_sub == null) return;
+
+    _sub.cancel();
+    _sub = null;
+    if (_next != null) _next.unobserve();
+  }
+
+  void _onChange(List<ChangeRecord> changes) {
+    for (var change in changes) {
+      // TODO(jmesserly): what to do about "new Symbol" here?
+      // Ideally this would only preserve names if the user has opted in to
+      // them being preserved.
+      // TODO(jmesserly): should we drop observable maps with String keys?
+      // If so then we only need one check here.
+      if (change.changes(_property)) {
+        value = _getObjectProperty(_object, _property);
+        _path._notifyChange();
+        return;
       }
     }
   }
 }
+
+// From: https://github.com/rafaelw/ChangeSummary/blob/master/change_summary.js
+
+const _pathIndentPart = r'[$a-z0-9_]+[$a-z0-9_\d]*';
+final _pathRegExp = new RegExp('^'
+    '(?:#?' + _pathIndentPart + ')?'
+    '(?:'
+      '(?:\\.' + _pathIndentPart + ')'
+    ')*'
+    r'$', caseSensitive: false);
+
+final _spacesRegExp = new RegExp(r'\s');
+
+bool _isPathValid(String s) {
+  s = s.replaceAll(_spacesRegExp, '');
+
+  if (s == '') return true;
+  if (s[0] == '.') return false;
+  return _pathRegExp.hasMatch(s);
+}
 // Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
@@ -28342,6 +24939,781 @@
   Point get bottomRight => new Point(this.left + this.width,
       this.top + this.height);
 }
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+
+// This code is a port of Model-Driven-Views:
+// https://github.com/toolkitchen/mdv
+// The code mostly comes from src/template_element.js
+
+typedef void _ChangeHandler(value);
+
+/**
+ * Model-Driven Views (MDV)'s native features enables a wide-range of use cases,
+ * but (by design) don't attempt to implement a wide array of specialized
+ * behaviors.
+ *
+ * Enabling these features in MDV is a matter of implementing and registering an
+ * MDV Custom Syntax. A Custom Syntax is an object which contains one or more
+ * delegation functions which implement specialized behavior. This object is
+ * registered with MDV via [TemplateElement.syntax]:
+ *
+ *
+ * HTML:
+ *     <template bind syntax="MySyntax">
+ *       {{ What!Ever('crazy')->thing^^^I+Want(data) }}
+ *     </template>
+ *
+ * Dart:
+ *     class MySyntax extends CustomBindingSyntax {
+ *       getBinding(model, path, name, node) {
+ *         // The magic happens here!
+ *       }
+ *     }
+ *
+ *     ...
+ *
+ *     TemplateElement.syntax['MySyntax'] = new MySyntax();
+ *
+ * See <https://github.com/toolkitchen/mdv/blob/master/docs/syntax.md> for more
+ * information about Custom Syntax.
+ */
+// TODO(jmesserly): if this is just one method, a function type would make it
+// more Dart-friendly.
+@Experimental
+abstract class CustomBindingSyntax {
+  // TODO(jmesserly): I had to remove type annotations from "name" and "node"
+  // Normally they are String and Node respectively. But sometimes it will pass
+  // (int name, CompoundBinding node). That seems very confusing; we may want
+  // to change this API.
+  getBinding(model, String path, name, node);
+}
+
+/** The callback used in the [CompoundBinding.combinator] field. */
+@Experimental
+typedef Object CompoundBindingCombinator(Map objects);
+
+/** Information about the instantiated template. */
+@Experimental
+class TemplateInstance {
+  // TODO(rafaelw): firstNode & lastNode should be read-synchronous
+  // in cases where script has modified the template instance boundary.
+
+  /** The first node of this template instantiation. */
+  final Node firstNode;
+
+  /**
+   * The last node of this template instantiation.
+   * This could be identical to [firstNode] if the template only expanded to a
+   * single node.
+   */
+  final Node lastNode;
+
+  /** The model used to instantiate the template. */
+  final model;
+
+  TemplateInstance(this.firstNode, this.lastNode, this.model);
+}
+
+/**
+ * Model-Driven Views contains a helper object which is useful for the
+ * implementation of a Custom Syntax.
+ *
+ *     var binding = new CompoundBinding((values) {
+ *       var combinedValue;
+ *       // compute combinedValue based on the current values which are provided
+ *       return combinedValue;
+ *     });
+ *     binding.bind('name1', obj1, path1);
+ *     binding.bind('name2', obj2, path2);
+ *     //...
+ *     binding.bind('nameN', objN, pathN);
+ *
+ * CompoundBinding is an object which knows how to listen to multiple path
+ * values (registered via [bind]) and invoke its [combinator] when one or more
+ * of the values have changed and set its [value] property to the return value
+ * of the function. When any value has changed, all current values are provided
+ * to the [combinator] in the single `values` argument.
+ *
+ * See [CustomBindingSyntax] for more information.
+ */
+// TODO(jmesserly): what is the public API surface here? I just guessed;
+// most of it seemed non-public.
+@Experimental
+class CompoundBinding extends ObservableBase {
+  CompoundBindingCombinator _combinator;
+
+  // TODO(jmesserly): ideally these would be String keys, but sometimes we
+  // use integers.
+  Map<dynamic, StreamSubscription> _bindings = new Map();
+  Map _values = new Map();
+  bool _scheduled = false;
+  bool _disposed = false;
+  Object _value;
+
+  CompoundBinding([CompoundBindingCombinator combinator]) {
+    // TODO(jmesserly): this is a tweak to the original code, it seemed to me
+    // that passing the combinator to the constructor should be equivalent to
+    // setting it via the property.
+    // I also added a null check to the combinator setter.
+    this.combinator = combinator;
+  }
+
+  CompoundBindingCombinator get combinator => _combinator;
+
+  set combinator(CompoundBindingCombinator combinator) {
+    _combinator = combinator;
+    if (combinator != null) _scheduleResolve();
+  }
+
+  static const _VALUE = const Symbol('value');
+
+  get value => _value;
+
+  void set value(newValue) {
+    _value = notifyPropertyChange(_VALUE, _value, newValue);
+  }
+
+  // TODO(jmesserly): remove these workarounds when dart2js supports mirrors!
+  getValueWorkaround(key) {
+    if (key == _VALUE) return value;
+    return null;
+  }
+  setValueWorkaround(key, val) {
+    if (key == _VALUE) value = val;
+  }
+
+  void bind(name, model, String path) {
+    unbind(name);
+
+    _bindings[name] = new PathObserver(model, path).bindSync((value) {
+      _values[name] = value;
+      _scheduleResolve();
+    });
+  }
+
+  void unbind(name, {bool suppressResolve: false}) {
+    var binding = _bindings.remove(name);
+    if (binding == null) return;
+
+    binding.cancel();
+    _values.remove(name);
+    if (!suppressResolve) _scheduleResolve();
+  }
+
+  // TODO(rafaelw): Is this the right processing model?
+  // TODO(rafaelw): Consider having a seperate ChangeSummary for
+  // CompoundBindings so to excess dirtyChecks.
+  void _scheduleResolve() {
+    if (_scheduled) return;
+    _scheduled = true;
+    queueChangeRecords(resolve);
+  }
+
+  void resolve() {
+    if (_disposed) return;
+    _scheduled = false;
+
+    if (_combinator == null) {
+      throw new StateError(
+          'CompoundBinding attempted to resolve without a combinator');
+    }
+
+    value = _combinator(_values);
+  }
+
+  void dispose() {
+    for (var binding in _bindings.values) {
+      binding.cancel();
+    }
+    _bindings.clear();
+    _values.clear();
+
+    _disposed = true;
+    value = null;
+  }
+}
+
+Stream<Event> _getStreamForInputType(InputElement element) {
+  switch (element.type) {
+    case 'checkbox':
+      return element.onClick;
+    case 'radio':
+    case 'select-multiple':
+    case 'select-one':
+      return element.onChange;
+    default:
+      return element.onInput;
+  }
+}
+
+abstract class _InputBinding {
+  final InputElement element;
+  PathObserver binding;
+  StreamSubscription _pathSub;
+  StreamSubscription _eventSub;
+
+  _InputBinding(this.element, model, String path) {
+    binding = new PathObserver(model, path);
+    _pathSub = binding.bindSync(valueChanged);
+    _eventSub = _getStreamForInputType(element).listen(updateBinding);
+  }
+
+  void valueChanged(newValue);
+
+  void updateBinding(e);
+
+  void unbind() {
+    binding = null;
+    _pathSub.cancel();
+    _eventSub.cancel();
+  }
+}
+
+class _ValueBinding extends _InputBinding {
+  _ValueBinding(element, model, path) : super(element, model, path);
+
+  void valueChanged(value) {
+    element.value = value == null ? '' : '$value';
+  }
+
+  void updateBinding(e) {
+    binding.value = element.value;
+  }
+}
+
+// TODO(jmesserly): not sure what kind of boolean conversion rules to
+// apply for template data-binding. HTML attributes are true if they're present.
+// However Dart only treats "true" as true. Since this is HTML we'll use
+// something closer to the HTML rules: null (missing) and false are false,
+// everything else is true. See: https://github.com/toolkitchen/mdv/issues/59
+bool _templateBooleanConversion(value) => null != value && false != value;
+
+class _CheckedBinding extends _InputBinding {
+  _CheckedBinding(element, model, path) : super(element, model, path);
+
+  void valueChanged(value) {
+    element.checked = _templateBooleanConversion(value);
+  }
+
+  void updateBinding(e) {
+    binding.value = element.checked;
+
+    // Only the radio button that is getting checked gets an event. We
+    // therefore find all the associated radio buttons and update their
+    // CheckedBinding manually.
+    if (element is InputElement && element.type == 'radio') {
+      for (var r in _getAssociatedRadioButtons(element)) {
+        var checkedBinding = r._checkedBinding;
+        if (checkedBinding != null) {
+          // Set the value directly to avoid an infinite call stack.
+          checkedBinding.binding.value = false;
+        }
+      }
+    }
+  }
+}
+
+// TODO(jmesserly): polyfill document.contains API instead of doing it here
+bool _isNodeInDocument(Node node) {
+  // On non-IE this works:
+  // return node.document.contains(node);
+  var document = node.document;
+  if (node == document || node.parentNode == document) return true;
+  return document.documentElement.contains(node);
+}
+
+// |element| is assumed to be an HTMLInputElement with |type| == 'radio'.
+// Returns an array containing all radio buttons other than |element| that
+// have the same |name|, either in the form that |element| belongs to or,
+// if no form, in the document tree to which |element| belongs.
+//
+// This implementation is based upon the HTML spec definition of a
+// "radio button group":
+//   http://www.whatwg.org/specs/web-apps/current-work/multipage/number-state.html#radio-button-group
+//
+Iterable _getAssociatedRadioButtons(element) {
+  if (!_isNodeInDocument(element)) return [];
+  if (element.form != null) {
+    return element.form.nodes.where((el) {
+      return el != element &&
+          el is InputElement &&
+          el.type == 'radio' &&
+          el.name == element.name;
+    });
+  } else {
+    var radios = element.document.queryAll(
+        'input[type="radio"][name="${element.name}"]');
+    return radios.where((el) => el != element && el.form == null);
+  }
+}
+
+Node _createDeepCloneAndDecorateTemplates(Node node, String syntax) {
+  var clone = node.clone(false); // Shallow clone.
+  if (clone is Element && clone.isTemplate) {
+    TemplateElement.decorate(clone, node);
+    if (syntax != null) {
+      clone.attributes.putIfAbsent('syntax', () => syntax);
+    }
+  }
+
+  for (var c = node.$dom_firstChild; c != null; c = c.nextNode) {
+    clone.append(_createDeepCloneAndDecorateTemplates(c, syntax));
+  }
+  return clone;
+}
+
+// http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html#dfn-template-contents-owner
+Document _getTemplateContentsOwner(Document doc) {
+  if (doc.window == null) {
+    return doc;
+  }
+  var d = doc._templateContentsOwner;
+  if (d == null) {
+    // TODO(arv): This should either be a Document or HTMLDocument depending
+    // on doc.
+    d = doc.implementation.createHtmlDocument('');
+    while (d.$dom_lastChild != null) {
+      d.$dom_lastChild.remove();
+    }
+    doc._templateContentsOwner = d;
+  }
+  return d;
+}
+
+Element _cloneAndSeperateAttributeTemplate(Element templateElement) {
+  var clone = templateElement.clone(false);
+  var attributes = templateElement.attributes;
+  for (var name in attributes.keys.toList()) {
+    switch (name) {
+      case 'template':
+      case 'repeat':
+      case 'bind':
+      case 'ref':
+        clone.attributes.remove(name);
+        break;
+      default:
+        attributes.remove(name);
+        break;
+    }
+  }
+
+  return clone;
+}
+
+void _liftNonNativeTemplateChildrenIntoContent(Element templateElement) {
+  var content = templateElement.content;
+
+  if (!templateElement._isAttributeTemplate) {
+    var child;
+    while ((child = templateElement.$dom_firstChild) != null) {
+      content.append(child);
+    }
+    return;
+  }
+
+  // For attribute templates we copy the whole thing into the content and
+  // we move the non template attributes into the content.
+  //
+  //   <tr foo template>
+  //
+  // becomes
+  //
+  //   <tr template>
+  //   + #document-fragment
+  //     + <tr foo>
+  //
+  var newRoot = _cloneAndSeperateAttributeTemplate(templateElement);
+  var child;
+  while ((child = templateElement.$dom_firstChild) != null) {
+    newRoot.append(child);
+  }
+  content.append(newRoot);
+}
+
+void _bootstrapTemplatesRecursivelyFrom(Node node) {
+  void bootstrap(template) {
+    if (!TemplateElement.decorate(template)) {
+      _bootstrapTemplatesRecursivelyFrom(template.content);
+    }
+  }
+
+  // Need to do this first as the contents may get lifted if |node| is
+  // template.
+  // TODO(jmesserly): node is DocumentFragment or Element
+  var templateDescendents = (node as dynamic).queryAll(_allTemplatesSelectors);
+  if (node is Element && node.isTemplate) bootstrap(node);
+
+  templateDescendents.forEach(bootstrap);
+}
+
+final String _allTemplatesSelectors = 'template, option[template], ' +
+    Element._TABLE_TAGS.keys.map((k) => "$k[template]").join(", ");
+
+void _addBindings(Node node, model, [CustomBindingSyntax syntax]) {
+  if (node is Element) {
+    _addAttributeBindings(node, model, syntax);
+  } else if (node is Text) {
+    _parseAndBind(node, node.text, 'text', model, syntax);
+  }
+
+  for (var c = node.$dom_firstChild; c != null; c = c.nextNode) {
+    _addBindings(c, model, syntax);
+  }
+}
+
+
+void _addAttributeBindings(Element element, model, syntax) {
+  element.attributes.forEach((name, value) {
+    if (value == '' && (name == 'bind' || name == 'repeat')) {
+      value = '{{}}';
+    }
+    _parseAndBind(element, value, name, model, syntax);
+  });
+}
+
+void _parseAndBind(Node node, String text, String name, model,
+    CustomBindingSyntax syntax) {
+
+  var tokens = _parseMustacheTokens(text);
+  if (tokens.length == 0 || (tokens.length == 1 && tokens[0].isText)) {
+    return;
+  }
+
+  if (tokens.length == 1 && tokens[0].isBinding) {
+    _bindOrDelegate(node, name, model, tokens[0].value, syntax);
+    return;
+  }
+
+  var replacementBinding = new CompoundBinding();
+  for (var i = 0; i < tokens.length; i++) {
+    var token = tokens[i];
+    if (token.isBinding) {
+      _bindOrDelegate(replacementBinding, i, model, token.value, syntax);
+    }
+  }
+
+  replacementBinding.combinator = (values) {
+    var newValue = new StringBuffer();
+
+    for (var i = 0; i < tokens.length; i++) {
+      var token = tokens[i];
+      if (token.isText) {
+        newValue.write(token.value);
+      } else {
+        var value = values[i];
+        if (value != null) {
+          newValue.write(value);
+        }
+      }
+    }
+
+    return newValue.toString();
+  };
+
+  node.bind(name, replacementBinding, 'value');
+}
+
+void _bindOrDelegate(node, name, model, String path,
+    CustomBindingSyntax syntax) {
+
+  if (syntax != null) {
+    var delegateBinding = syntax.getBinding(model, path, name, node);
+    if (delegateBinding != null) {
+      model = delegateBinding;
+      path = 'value';
+    }
+  }
+
+  node.bind(name, model, path);
+}
+
+class _BindingToken {
+  final String value;
+  final bool isBinding;
+
+  _BindingToken(this.value, {this.isBinding: false});
+
+  bool get isText => !isBinding;
+}
+
+List<_BindingToken> _parseMustacheTokens(String s) {
+  var result = [];
+  var length = s.length;
+  var index = 0, lastIndex = 0;
+  while (lastIndex < length) {
+    index = s.indexOf('{{', lastIndex);
+    if (index < 0) {
+      result.add(new _BindingToken(s.substring(lastIndex)));
+      break;
+    } else {
+      // There is a non-empty text run before the next path token.
+      if (index > 0 && lastIndex < index) {
+        result.add(new _BindingToken(s.substring(lastIndex, index)));
+      }
+      lastIndex = index + 2;
+      index = s.indexOf('}}', lastIndex);
+      if (index < 0) {
+        var text = s.substring(lastIndex - 2);
+        if (result.length > 0 && result.last.isText) {
+          result.last.value += text;
+        } else {
+          result.add(new _BindingToken(text));
+        }
+        break;
+      }
+
+      var value = s.substring(lastIndex, index).trim();
+      result.add(new _BindingToken(value, isBinding: true));
+      lastIndex = index + 2;
+    }
+  }
+  return result;
+}
+
+void _addTemplateInstanceRecord(fragment, model) {
+  if (fragment.$dom_firstChild == null) {
+    return;
+  }
+
+  var instanceRecord = new TemplateInstance(
+      fragment.$dom_firstChild, fragment.$dom_lastChild, model);
+
+  var node = instanceRecord.firstNode;
+  while (node != null) {
+    node._templateInstance = instanceRecord;
+    node = node.nextNode;
+  }
+}
+
+void _removeAllBindingsRecursively(Node node) {
+  node.unbindAll();
+  for (var c = node.$dom_firstChild; c != null; c = c.nextNode) {
+    _removeAllBindingsRecursively(c);
+  }
+}
+
+void _removeTemplateChild(Node parent, Node child) {
+  child._templateInstance = null;
+  if (child is Element && child.isTemplate) {
+    // Make sure we stop observing when we remove an element.
+    var templateIterator = child._templateIterator;
+    if (templateIterator != null) {
+      templateIterator.abandon();
+      child._templateIterator = null;
+    }
+  }
+  child.remove();
+  _removeAllBindingsRecursively(child);
+}
+
+class _InstanceCursor {
+  final Element _template;
+  Node _terminator;
+  Node _previousTerminator;
+  int _previousIndex = -1;
+  int _index = 0;
+
+  _InstanceCursor(this._template, [index]) {
+    _terminator = _template;
+    if (index != null) {
+      while (index-- > 0) {
+        next();
+      }
+    }
+  }
+
+  void next() {
+    _previousTerminator = _terminator;
+    _previousIndex = _index;
+    _index++;
+
+    while (_index > _terminator._instanceTerminatorCount) {
+      _index -= _terminator._instanceTerminatorCount;
+      _terminator = _terminator.nextNode;
+      if (_terminator is Element && _terminator.tagName == 'TEMPLATE') {
+        _index += _instanceCount(_terminator);
+      }
+    }
+  }
+
+  void abandon() {
+    assert(_instanceCount(_template) > 0);
+    assert(_terminator._instanceTerminatorCount > 0);
+    assert(_index > 0);
+
+    _terminator._instanceTerminatorCount--;
+    _index--;
+  }
+
+  void insert(fragment) {
+    assert(_template.parentNode != null);
+
+    _previousTerminator = _terminator;
+    _previousIndex = _index;
+    _index++;
+
+    _terminator = fragment.$dom_lastChild;
+    if (_terminator == null) _terminator = _previousTerminator;
+    _template.parentNode.insertBefore(fragment, _previousTerminator.nextNode);
+
+    _terminator._instanceTerminatorCount++;
+    if (_terminator != _previousTerminator) {
+      while (_previousTerminator._instanceTerminatorCount >
+              _previousIndex) {
+        _previousTerminator._instanceTerminatorCount--;
+        _terminator._instanceTerminatorCount++;
+      }
+    }
+  }
+
+  void remove() {
+    assert(_previousIndex != -1);
+    assert(_previousTerminator != null &&
+           (_previousIndex > 0 || _previousTerminator == _template));
+    assert(_terminator != null && _index > 0);
+    assert(_template.parentNode != null);
+    assert(_instanceCount(_template) > 0);
+
+    if (_previousTerminator == _terminator) {
+      assert(_index == _previousIndex + 1);
+      _terminator._instanceTerminatorCount--;
+      _terminator = _template;
+      _previousTerminator = null;
+      _previousIndex = -1;
+      return;
+    }
+
+    _terminator._instanceTerminatorCount--;
+
+    var parent = _template.parentNode;
+    while (_previousTerminator.nextNode != _terminator) {
+      _removeTemplateChild(parent, _previousTerminator.nextNode);
+    }
+    _removeTemplateChild(parent, _terminator);
+
+    _terminator = _previousTerminator;
+    _index = _previousIndex;
+    _previousTerminator = null;
+    _previousIndex = -1;  // 0?
+  }
+}
+
+
+class _TemplateIterator {
+  final Element _templateElement;
+  int instanceCount = 0;
+  List iteratedValue;
+  bool observing = false;
+  final CompoundBinding inputs;
+
+  StreamSubscription _sub;
+  StreamSubscription _valueBinding;
+
+  _TemplateIterator(this._templateElement)
+    : inputs = new CompoundBinding(resolveInputs) {
+
+    _valueBinding = new PathObserver(inputs, 'value').bindSync(valueChanged);
+  }
+
+  static Object resolveInputs(Map values) {
+    if (values.containsKey('if') && !_templateBooleanConversion(values['if'])) {
+      return null;
+    }
+
+    if (values.containsKey('repeat')) {
+      return values['repeat'];
+    }
+
+    if (values.containsKey('bind')) {
+      return [values['bind']];
+    }
+
+    return null;
+  }
+
+  void valueChanged(value) {
+    clear();
+    if (value is! List) return;
+
+    iteratedValue = value;
+
+    if (value is Observable) {
+      _sub = value.changes.listen(_handleChanges);
+    }
+
+    int len = iteratedValue.length;
+    if (len > 0) {
+      _handleChanges([new ListChangeRecord(0, addedCount: len)]);
+    }
+  }
+
+  // TODO(jmesserly): port MDV v3.
+  getInstanceModel(model, syntax) => model;
+  getInstanceFragment(syntax) => _templateElement.createInstance();
+
+  void _handleChanges(List<ListChangeRecord> splices) {
+    var syntax = TemplateElement.syntax[_templateElement.attributes['syntax']];
+
+    for (var splice in splices) {
+      if (splice is! ListChangeRecord) continue;
+
+      for (int i = 0; i < splice.removedCount; i++) {
+        var cursor = new _InstanceCursor(_templateElement, splice.index + 1);
+        cursor.remove();
+        instanceCount--;
+      }
+
+      for (var addIndex = splice.index;
+          addIndex < splice.index + splice.addedCount;
+          addIndex++) {
+
+        var model = getInstanceModel(iteratedValue[addIndex], syntax);
+        var fragment = getInstanceFragment(syntax);
+
+        _addBindings(fragment, model, syntax);
+        _addTemplateInstanceRecord(fragment, model);
+
+        var cursor = new _InstanceCursor(_templateElement, addIndex);
+        cursor.insert(fragment);
+        instanceCount++;
+      }
+    }
+  }
+
+  void unobserve() {
+    if (_sub == null) return;
+    _sub.cancel();
+    _sub = null;
+  }
+
+  void clear() {
+    unobserve();
+
+    iteratedValue = null;
+    if (instanceCount == 0) return;
+
+    for (var i = 0; i < instanceCount; i++) {
+      var cursor = new _InstanceCursor(_templateElement, 1);
+      cursor.remove();
+    }
+
+    instanceCount = 0;
+  }
+
+  void abandon() {
+    unobserve();
+    _valueBinding.cancel();
+    inputs.dispose();
+  }
+}
+
+int _instanceCount(Element element) {
+  var templateIterator = element._templateIterator;
+  return templateIterator != null ? templateIterator.instanceCount : 0;
+}
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
@@ -29125,6 +26497,7 @@
 }
 
 WindowBase _convertNativeToDart_Window(win) {
+  if (win == null) return null;
   return _DOMWindowCrossFrame._createSafe(win);
 }
 
diff --git a/sdk/lib/html/dartium/html_dartium.dart b/sdk/lib/html/dartium/html_dartium.dart
index 3b32ec2..e549cb6 100644
--- a/sdk/lib/html/dartium/html_dartium.dart
+++ b/sdk/lib/html/dartium/html_dartium.dart
@@ -3,13 +3,14 @@
 
 import 'dart:async';
 import 'dart:collection';
-import 'dart:_collection-dev';
+import 'dart:_collection-dev' hide Symbol;
 import 'dart:html_common';
 import 'dart:indexed_db';
 import 'dart:isolate';
 import 'dart:json' as json;
 import 'dart:math';
 import 'dart:nativewrappers';
+import 'dart:mdv_observe_impl';
 import 'dart:typed_data';
 import 'dart:web_gl' as gl;
 import 'dart:web_sql';
@@ -268,11 +269,11 @@
 class AnimationEvent extends Event {
   AnimationEvent.internal() : super.internal();
 
-  @DomName('AnimationEvent.animationName')
+  @DomName('WebKitAnimationEvent.animationName')
   @DocsEditable
   String get animationName native "AnimationEvent_animationName_Getter";
 
-  @DomName('AnimationEvent.elapsedTime')
+  @DomName('WebKitAnimationEvent.elapsedTime')
   @DocsEditable
   num get elapsedTime native "AnimationEvent_elapsedTime_Getter";
 
@@ -2541,10 +2542,6 @@
   @DocsEditable
   CssRule get parentRule native "CSSStyleDeclaration_parentRule_Getter";
 
-  @DomName('CSSStyleDeclaration._getPropertyValue')
-  @DocsEditable
-  String _getPropertyValue(String propertyName) native "CSSStyleDeclaration__getPropertyValue_Callback";
-
   @DomName('CSSStyleDeclaration.getPropertyPriority')
   @DocsEditable
   String getPropertyPriority(String propertyName) native "CSSStyleDeclaration_getPropertyPriority_Callback";
@@ -2553,6 +2550,10 @@
   @DocsEditable
   String getPropertyShorthand(String propertyName) native "CSSStyleDeclaration_getPropertyShorthand_Callback";
 
+  @DomName('CSSStyleDeclaration.getPropertyValue')
+  @DocsEditable
+  String _getPropertyValue(String propertyName) native "CSSStyleDeclaration_getPropertyValue_Callback";
+
   @DomName('CSSStyleDeclaration.isPropertyImplicit')
   @DocsEditable
   bool isPropertyImplicit(String propertyName) native "CSSStyleDeclaration_isPropertyImplicit_Callback";
@@ -2567,7 +2568,7 @@
 
   @DomName('CSSStyleDeclaration.setProperty')
   @DocsEditable
-  void setProperty(String propertyName, String value, [String priority]) native "CSSStyleDeclaration_setProperty_Callback";
+  void setProperty(String propertyName, String value, String priority) native "CSSStyleDeclaration_setProperty_Callback";
 
 
   String getPropertyValue(String propertyName) {
@@ -7043,7 +7044,7 @@
     e.innerHtml = value;
 
     // Copy list first since we don't want liveness during iteration.
-    List nodes = new List.from(e.nodes);
+    List nodes = new List.from(e.nodes, growable: false);
     this.nodes.addAll(nodes);
   }
 
@@ -7137,15 +7138,15 @@
 
   DomException.internal();
 
-  @DomName('DOMCoreException.message')
+  @DomName('DOMException.message')
   @DocsEditable
   String get message native "DOMCoreException_message_Getter";
 
-  @DomName('DOMCoreException.name')
+  @DomName('DOMException.name')
   @DocsEditable
   String get name native "DOMCoreException_name_Getter";
 
-  @DomName('DOMCoreException.toString')
+  @DomName('DOMException.toString')
   @DocsEditable
   String toString() native "DOMCoreException_toString_Callback";
 
@@ -7239,7 +7240,7 @@
 
 @DocsEditable
 @DomName('DOMStringList')
-class DomStringList extends NativeFieldWrapperClass1 implements List<String> {
+class DomStringList extends NativeFieldWrapperClass1 with ListMixin<String>, ImmutableListMixin<String> implements List<String> {
   DomStringList.internal();
 
   @DomName('DOMStringList.length')
@@ -7254,196 +7255,11 @@
   // -- start List<String> mixins.
   // String is the element type.
 
-  // From Iterable<String>:
 
-  Iterator<String> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<String>(this);
-  }
-
-  String reduce(String combine(String value, String element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, String element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  // contains() defined by IDL.
-
-  void forEach(void f(String element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(String element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<String> where(bool f(String element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(String element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(String element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(String element)) => IterableMixinWorkaround.any(this, f);
-
-  List<String> toList({ bool growable: true }) =>
-      new List<String>.from(this, growable: growable);
-
-  Set<String> toSet() => new Set<String>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<String> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<String> takeWhile(bool test(String value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<String> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<String> skipWhile(bool test(String value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  String firstWhere(bool test(String value), { String orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  String lastWhere(bool test(String value), {String orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  String singleWhere(bool test(String value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  String elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<String>:
-
-  void add(String value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<String> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<String>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<String> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(String a, String b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(String element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(String element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  String get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  String get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  String get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, String element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<String> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<String> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  String removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  String removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(String element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(String element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<String> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<String> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [String fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<String> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<String> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <String>[]);
-  }
-
-  Map<int, String> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<String> mixins.
 
   @DomName('DOMStringList.contains')
@@ -7646,7 +7462,7 @@
   }
 }
 
-/** 
+/**
  * An immutable list containing HTML elements. This list contains some
  * additional methods for ease of CSS manipulation on a group of elements.
  */
@@ -7981,6 +7797,251 @@
   }
 
 
+  @Creates('Null')
+  Map<String, StreamSubscription> _attributeBindings;
+
+  // TODO(jmesserly): I'm concerned about adding these to every element.
+  // Conceptually all of these belong on TemplateElement. They are here to
+  // support browsers that don't have <template> yet.
+  // However even in the polyfill they're restricted to certain tags
+  // (see [isTemplate]). So we can probably convert it to a (public) mixin, and
+  // only mix it in to the elements that need it.
+  var _model;
+
+  _TemplateIterator _templateIterator;
+
+  Element _templateInstanceRef;
+
+  // Note: only used if `this is! TemplateElement`
+  DocumentFragment _templateContent;
+
+  bool _templateIsDecorated;
+
+  // TODO(jmesserly): should path be optional, and default to empty path?
+  // It is used that way in at least one path in JS TemplateElement tests
+  // (see "BindImperative" test in original JS code).
+  @Experimental
+  void bind(String name, model, String path) {
+    _bindElement(this, name, model, path);
+  }
+
+  // TODO(jmesserly): this is static to work around http://dartbug.com/10166
+  // Similar issue for unbind/unbindAll below.
+  static void _bindElement(Element self, String name, model, String path) {
+    if (self._bindTemplate(name, model, path)) return;
+
+    if (self._attributeBindings == null) {
+      self._attributeBindings = new Map<String, StreamSubscription>();
+    }
+
+    self.attributes.remove(name);
+
+    var changed;
+    if (name.endsWith('?')) {
+      name = name.substring(0, name.length - 1);
+
+      changed = (value) {
+        if (_templateBooleanConversion(value)) {
+          self.attributes[name] = '';
+        } else {
+          self.attributes.remove(name);
+        }
+      };
+    } else {
+      changed = (value) {
+        // TODO(jmesserly): escape value if needed to protect against XSS.
+        // See https://github.com/toolkitchen/mdv/issues/58
+        self.attributes[name] = value == null ? '' : '$value';
+      };
+    }
+
+    self.unbind(name);
+
+    self._attributeBindings[name] =
+        new PathObserver(model, path).bindSync(changed);
+  }
+
+  @Experimental
+  void unbind(String name) {
+    _unbindElement(this, name);
+  }
+
+  static _unbindElement(Element self, String name) {
+    if (self._unbindTemplate(name)) return;
+    if (self._attributeBindings != null) {
+      var binding = self._attributeBindings.remove(name);
+      if (binding != null) binding.cancel();
+    }
+  }
+
+  @Experimental
+  void unbindAll() {
+    _unbindAllElement(this);
+  }
+
+  static void _unbindAllElement(Element self) {
+    self._unbindAllTemplate();
+
+    if (self._attributeBindings != null) {
+      for (var binding in self._attributeBindings.values) {
+        binding.cancel();
+      }
+      self._attributeBindings = null;
+    }
+  }
+
+  // TODO(jmesserly): unlike the JS polyfill, we can't mixin
+  // HTMLTemplateElement at runtime into things that are semantically template
+  // elements. So instead we implement it here with a runtime check.
+  // If the bind succeeds, we return true, otherwise we return false and let
+  // the normal Element.bind logic kick in.
+  bool _bindTemplate(String name, model, String path) {
+    if (isTemplate) {
+      switch (name) {
+        case 'bind':
+        case 'repeat':
+        case 'if':
+          _ensureTemplate();
+          if (_templateIterator == null) {
+            _templateIterator = new _TemplateIterator(this);
+          }
+          _templateIterator.inputs.bind(name, model, path);
+          return true;
+      }
+    }
+    return false;
+  }
+
+  bool _unbindTemplate(String name) {
+    if (isTemplate) {
+      switch (name) {
+        case 'bind':
+        case 'repeat':
+        case 'if':
+          _ensureTemplate();
+          if (_templateIterator != null) {
+            _templateIterator.inputs.unbind(name);
+          }
+          return true;
+      }
+    }
+    return false;
+  }
+
+  void _unbindAllTemplate() {
+    if (isTemplate) {
+      unbind('bind');
+      unbind('repeat');
+      unbind('if');
+    }
+  }
+
+  /**
+   * Gets the template this node refers to.
+   * This is only supported if [isTemplate] is true.
+   */
+  @Experimental
+  Element get ref {
+    _ensureTemplate();
+
+    Element ref = null;
+    var refId = attributes['ref'];
+    if (refId != null) {
+      ref = document.getElementById(refId);
+    }
+
+    return ref != null ? ref : _templateInstanceRef;
+  }
+
+  /**
+   * Gets the content of this template.
+   * This is only supported if [isTemplate] is true.
+   */
+  @Experimental
+  DocumentFragment get content {
+    _ensureTemplate();
+    return _templateContent;
+  }
+
+  /**
+   * Creates an instance of the template.
+   * This is only supported if [isTemplate] is true.
+   */
+  @Experimental
+  DocumentFragment createInstance() {
+    _ensureTemplate();
+
+    var template = ref;
+    if (template == null) template = this;
+
+    var instance = _createDeepCloneAndDecorateTemplates(template.content,
+        attributes['syntax']);
+
+    if (TemplateElement._instanceCreated != null) {
+      TemplateElement._instanceCreated.add(instance);
+    }
+    return instance;
+  }
+
+  /**
+   * The data model which is inherited through the tree.
+   * This is only supported if [isTemplate] is true.
+   *
+   * Setting this will destructive propagate the value to all descendant nodes,
+   * and reinstantiate all of the nodes expanded by this template.
+   *
+   * Currently this does not support propagation through Shadow DOMs.
+   */
+  @Experimental
+  get model => _model;
+
+  @Experimental
+  void set model(value) {
+    _ensureTemplate();
+
+    _model = value;
+    _addBindings(this, model);
+  }
+
+  // TODO(jmesserly): const set would be better
+  static const _TABLE_TAGS = const {
+    'caption': null,
+    'col': null,
+    'colgroup': null,
+    'tbody': null,
+    'td': null,
+    'tfoot': null,
+    'th': null,
+    'thead': null,
+    'tr': null,
+  };
+
+  bool get _isAttributeTemplate => attributes.containsKey('template') &&
+      (localName == 'option' || _TABLE_TAGS.containsKey(localName));
+
+  /**
+   * Returns true if this node is a template.
+   *
+   * A node is a template if [tagName] is TEMPLATE, or the node has the
+   * 'template' attribute and this tag supports attribute form for backwards
+   * compatibility with existing HTML parsers. The nodes that can use attribute
+   * form are table elments (THEAD, TBODY, TFOOT, TH, TR, TD, CAPTION, COLGROUP
+   * and COL) and OPTION.
+   */
+  // TODO(jmesserly): this is not a public MDV API, but it seems like a useful
+  // place to document which tags our polyfill considers to be templates.
+  // Otherwise I'd be repeating it in several other places.
+  // See if we can replace this with a TemplateMixin.
+  @Experimental
+  bool get isTemplate => tagName == 'TEMPLATE' || _isAttributeTemplate;
+
+  void _ensureTemplate() {
+    if (!isTemplate) {
+      throw new UnsupportedError('$this is not a template.');
+    }
+    TemplateElement.decorate(this);
+  }
+
   Element.internal() : super.internal();
 
   @DomName('Element.abortEvent')
@@ -8715,6 +8776,7 @@
 
 }
 
+
 final _START_TAG_REGEXP = new RegExp('<(\\w+)');
 class _ElementFactoryProvider {
   static const _CUSTOM_PARENT_TAG_MAP = const {
@@ -8732,19 +8794,6 @@
     'track' : 'audio',
   };
 
-  // TODO(jmesserly): const set would be better
-  static const _TABLE_TAGS = const {
-    'caption': null,
-    'col': null,
-    'colgroup': null,
-    'tbody': null,
-    'td': null,
-    'tfoot': null,
-    'th': null,
-    'thead': null,
-    'tr': null,
-  };
-
   @DomName('Document.createElement')
   static Element createElement_html(String html) {
     // TODO(jacobr): this method can be made more robust and performant.
@@ -8758,7 +8807,7 @@
     final match = _START_TAG_REGEXP.firstMatch(html);
     if (match != null) {
       tag = match.group(1).toLowerCase();
-      if (Device.isIE && _TABLE_TAGS.containsKey(tag)) {
+      if (Device.isIE && Element._TABLE_TAGS.containsKey(tag)) {
         return _createTableForIE(html, tag);
       }
       parentTag = _CUSTOM_PARENT_TAG_MAP[tag];
@@ -9711,7 +9760,7 @@
 
 @DocsEditable
 @DomName('FileList')
-class FileList extends NativeFieldWrapperClass1 implements List<File> {
+class FileList extends NativeFieldWrapperClass1 with ListMixin<File>, ImmutableListMixin<File> implements List<File> {
   FileList.internal();
 
   @DomName('FileList.length')
@@ -9726,196 +9775,11 @@
   // -- start List<File> mixins.
   // File is the element type.
 
-  // From Iterable<File>:
 
-  Iterator<File> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<File>(this);
-  }
-
-  File reduce(File combine(File value, File element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, File element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(File element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(File element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(File element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<File> where(bool f(File element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(File element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(File element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(File element)) => IterableMixinWorkaround.any(this, f);
-
-  List<File> toList({ bool growable: true }) =>
-      new List<File>.from(this, growable: growable);
-
-  Set<File> toSet() => new Set<File>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<File> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<File> takeWhile(bool test(File value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<File> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<File> skipWhile(bool test(File value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  File firstWhere(bool test(File value), { File orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  File lastWhere(bool test(File value), {File orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  File singleWhere(bool test(File value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  File elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<File>:
-
-  void add(File value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<File> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<File>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<File> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(File a, File b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(File element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(File element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  File get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  File get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  File get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, File element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<File> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<File> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  File removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  File removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(File element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(File element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<File> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<File> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [File fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<File> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<File> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <File>[]);
-  }
-
-  Map<int, File> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<File> mixins.
 
   @DomName('FileList.item')
@@ -10302,7 +10166,7 @@
   /// Checks if this type is supported on the current platform.
   static bool get supported => true;
 
-  @DomName('DOMFormData.append')
+  @DomName('FormData.append')
   @DocsEditable
   void append(String name, value, [String filename]) native "DOMFormData_append_Callback";
 
@@ -10742,7 +10606,7 @@
 
 @DocsEditable
 @DomName('HTMLAllCollection')
-class HtmlAllCollection extends NativeFieldWrapperClass1 implements List<Node> {
+class HtmlAllCollection extends NativeFieldWrapperClass1 with ListMixin<Node>, ImmutableListMixin<Node> implements List<Node> {
   HtmlAllCollection.internal();
 
   @DomName('HTMLAllCollection.length')
@@ -10757,196 +10621,11 @@
   // -- start List<Node> mixins.
   // Node is the element type.
 
-  // From Iterable<Node>:
 
-  Iterator<Node> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<Node>(this);
-  }
-
-  Node reduce(Node combine(Node value, Node element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, Node element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(Node element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(Node element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(Node element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<Node> where(bool f(Node element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(Node element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(Node element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(Node element)) => IterableMixinWorkaround.any(this, f);
-
-  List<Node> toList({ bool growable: true }) =>
-      new List<Node>.from(this, growable: growable);
-
-  Set<Node> toSet() => new Set<Node>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<Node> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<Node> takeWhile(bool test(Node value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<Node> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<Node> skipWhile(bool test(Node value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  Node firstWhere(bool test(Node value), { Node orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  Node lastWhere(bool test(Node value), {Node orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  Node singleWhere(bool test(Node value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  Node elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<Node>:
-
-  void add(Node value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<Node>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<Node> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(Node a, Node b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(Node element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(Node element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  Node get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  Node get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  Node get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, Node element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Node removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  Node removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(Node element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(Node element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<Node> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [Node fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<Node> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<Node> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <Node>[]);
-  }
-
-  Map<int, Node> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<Node> mixins.
 
   @DomName('HTMLAllCollection.item')
@@ -10971,7 +10650,7 @@
 
 @DocsEditable
 @DomName('HTMLCollection')
-class HtmlCollection extends NativeFieldWrapperClass1 implements List<Node> {
+class HtmlCollection extends NativeFieldWrapperClass1 with ListMixin<Node>, ImmutableListMixin<Node> implements List<Node> {
   HtmlCollection.internal();
 
   @DomName('HTMLCollection.length')
@@ -10986,196 +10665,11 @@
   // -- start List<Node> mixins.
   // Node is the element type.
 
-  // From Iterable<Node>:
 
-  Iterator<Node> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<Node>(this);
-  }
-
-  Node reduce(Node combine(Node value, Node element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, Node element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(Node element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(Node element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(Node element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<Node> where(bool f(Node element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(Node element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(Node element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(Node element)) => IterableMixinWorkaround.any(this, f);
-
-  List<Node> toList({ bool growable: true }) =>
-      new List<Node>.from(this, growable: growable);
-
-  Set<Node> toSet() => new Set<Node>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<Node> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<Node> takeWhile(bool test(Node value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<Node> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<Node> skipWhile(bool test(Node value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  Node firstWhere(bool test(Node value), { Node orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  Node lastWhere(bool test(Node value), {Node orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  Node singleWhere(bool test(Node value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  Node elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<Node>:
-
-  void add(Node value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<Node>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<Node> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(Node a, Node b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(Node element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(Node element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  Node get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  Node get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  Node get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, Node element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Node removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  Node removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(Node element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(Node element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<Node> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [Node fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<Node> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<Node> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <Node>[]);
-  }
-
-  Map<int, Node> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<Node> mixins.
 
   @DomName('HTMLCollection.item')
@@ -11349,6 +10843,10 @@
   @SupportedBrowser(SupportedBrowser.SAFARI)
   @Experimental
   String get visibilityState => $dom_webkitVisibilityState;
+
+
+  // Note: used to polyfill <template>
+  Document _templateContentsOwner;
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -12289,6 +11787,60 @@
     }
     return e;
   }
+
+  _ValueBinding _valueBinding;
+
+  _CheckedBinding _checkedBinding;
+
+  @Experimental
+  void bind(String name, model, String path) {
+    switch (name) {
+      case 'value':
+        unbind('value');
+        attributes.remove('value');
+        _valueBinding = new _ValueBinding(this, model, path);
+        break;
+      case 'checked':
+        unbind('checked');
+        attributes.remove('checked');
+        _checkedBinding = new _CheckedBinding(this, model, path);
+        break;
+      default:
+        // TODO(jmesserly): this should be "super" (http://dartbug.com/10166).
+        // Similar issue for unbind/unbindAll below.
+        Element._bindElement(this, name, model, path);
+        break;
+    }
+  }
+
+  @Experimental
+  void unbind(String name) {
+    switch (name) {
+      case 'value':
+        if (_valueBinding != null) {
+          _valueBinding.unbind();
+          _valueBinding = null;
+        }
+        break;
+      case 'checked':
+        if (_checkedBinding != null) {
+          _checkedBinding.unbind();
+          _checkedBinding = null;
+        }
+        break;
+      default:
+        Element._unbindElement(this, name);
+        break;
+    }
+  }
+
+  @Experimental
+  void unbindAll() {
+    unbind('value');
+    unbind('checked');
+    Element._unbindAllElement(this);
+  }
+
   InputElement.internal() : super.internal();
 
   @DomName('HTMLInputElement.webkitSpeechChangeEvent')
@@ -12749,8 +12301,8 @@
 
 
 // Interfaces representing the InputElement APIs which are supported
-// for the various types of InputElement.
-// From http://dev.w3.org/html5/spec/the-input-element.html#the-input-element.
+// for the various types of InputElement. From:
+// http://www.w3.org/html/wg/drafts/html/master/forms.html#the-input-element.
 
 /**
  * Exposes the functionality common between all InputElement types.
@@ -12796,7 +12348,7 @@
 /**
  * Hidden input which is not intended to be seen or edited by the user.
  */
-abstract class HiddenInputElement implements Element {
+abstract class HiddenInputElement implements InputElementBase {
   factory HiddenInputElement() => new InputElement(type: 'hidden');
 }
 
@@ -14268,7 +13820,7 @@
   @DocsEditable
   void play() native "HTMLMediaElement_play_Callback";
 
-  void addKey(String keySystem, List<int> key, [List<int> initData, String sessionId]) {
+  void addKey(String keySystem, Uint8List key, [Uint8List initData, String sessionId]) {
     if (?initData) {
       _webkitAddKey_1(keySystem, key, initData, sessionId);
       return;
@@ -14288,7 +13840,7 @@
   @Experimental
   void cancelKeyRequest(String keySystem, String sessionId) native "HTMLMediaElement_webkitCancelKeyRequest_Callback";
 
-  void generateKeyRequest(String keySystem, [List<int> initData]) {
+  void generateKeyRequest(String keySystem, [Uint8List initData]) {
     if (?initData) {
       _webkitGenerateKeyRequest_1(keySystem, initData);
       return;
@@ -14480,7 +14032,7 @@
 
   @DomName('MediaKeyEvent.initData')
   @DocsEditable
-  List<int> get initData native "MediaKeyEvent_initData_Getter";
+  Uint8List get initData native "MediaKeyEvent_initData_Getter";
 
   @DomName('MediaKeyEvent.keySystem')
   @DocsEditable
@@ -14488,7 +14040,7 @@
 
   @DomName('MediaKeyEvent.message')
   @DocsEditable
-  List<int> get message native "MediaKeyEvent_message_Getter";
+  Uint8List get message native "MediaKeyEvent_message_Getter";
 
   @DomName('MediaKeyEvent.sessionId')
   @DocsEditable
@@ -15207,19 +14759,19 @@
 class MimeType extends NativeFieldWrapperClass1 {
   MimeType.internal();
 
-  @DomName('DOMMimeType.description')
+  @DomName('MimeType.description')
   @DocsEditable
   String get description native "DOMMimeType_description_Getter";
 
-  @DomName('DOMMimeType.enabledPlugin')
+  @DomName('MimeType.enabledPlugin')
   @DocsEditable
   Plugin get enabledPlugin native "DOMMimeType_enabledPlugin_Getter";
 
-  @DomName('DOMMimeType.suffixes')
+  @DomName('MimeType.suffixes')
   @DocsEditable
   String get suffixes native "DOMMimeType_suffixes_Getter";
 
-  @DomName('DOMMimeType.type')
+  @DomName('MimeType.type')
   @DocsEditable
   String get type native "DOMMimeType_type_Getter";
 
@@ -15233,10 +14785,10 @@
 
 @DocsEditable
 @DomName('MimeTypeArray')
-class MimeTypeArray extends NativeFieldWrapperClass1 implements List<MimeType> {
+class MimeTypeArray extends NativeFieldWrapperClass1 with ListMixin<MimeType>, ImmutableListMixin<MimeType> implements List<MimeType> {
   MimeTypeArray.internal();
 
-  @DomName('DOMMimeTypeArray.length')
+  @DomName('MimeTypeArray.length')
   @DocsEditable
   int get length native "DOMMimeTypeArray_length_Getter";
 
@@ -15248,203 +14800,18 @@
   // -- start List<MimeType> mixins.
   // MimeType is the element type.
 
-  // From Iterable<MimeType>:
 
-  Iterator<MimeType> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<MimeType>(this);
-  }
-
-  MimeType reduce(MimeType combine(MimeType value, MimeType element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, MimeType element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(MimeType element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(MimeType element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(MimeType element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<MimeType> where(bool f(MimeType element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(MimeType element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(MimeType element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(MimeType element)) => IterableMixinWorkaround.any(this, f);
-
-  List<MimeType> toList({ bool growable: true }) =>
-      new List<MimeType>.from(this, growable: growable);
-
-  Set<MimeType> toSet() => new Set<MimeType>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<MimeType> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<MimeType> takeWhile(bool test(MimeType value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<MimeType> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<MimeType> skipWhile(bool test(MimeType value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  MimeType firstWhere(bool test(MimeType value), { MimeType orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  MimeType lastWhere(bool test(MimeType value), {MimeType orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  MimeType singleWhere(bool test(MimeType value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  MimeType elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<MimeType>:
-
-  void add(MimeType value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<MimeType> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<MimeType>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<MimeType> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(MimeType a, MimeType b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(MimeType element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(MimeType element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  MimeType get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  MimeType get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  MimeType get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, MimeType element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<MimeType> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<MimeType> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  MimeType removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  MimeType removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(MimeType element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(MimeType element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<MimeType> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<MimeType> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [MimeType fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<MimeType> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<MimeType> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <MimeType>[]);
-  }
-
-  Map<int, MimeType> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<MimeType> mixins.
 
-  @DomName('DOMMimeTypeArray.item')
+  @DomName('MimeTypeArray.item')
   @DocsEditable
   MimeType item(int index) native "DOMMimeTypeArray_item_Callback";
 
-  @DomName('DOMMimeTypeArray.namedItem')
+  @DomName('MimeTypeArray.namedItem')
   @DocsEditable
   MimeType namedItem(String name) native "DOMMimeTypeArray_namedItem_Callback";
 
@@ -15701,14 +15068,14 @@
   @DocsEditable
   static MutationObserver _create(callback) native "MutationObserver_constructorCallback";
 
-  @DomName('MutationObserver._observe')
-  @DocsEditable
-  void _observe(Node target, Map options) native "MutationObserver__observe_Callback";
-
   @DomName('MutationObserver.disconnect')
   @DocsEditable
   void disconnect() native "MutationObserver_disconnect_Callback";
 
+  @DomName('MutationObserver.observe')
+  @DocsEditable
+  void _observe(Node target, Map options) native "MutationObserver_observe_Callback";
+
   @DomName('MutationObserver.takeRecords')
   @DocsEditable
   List<MutationRecord> takeRecords() native "MutationObserver_takeRecords_Callback";
@@ -15833,39 +15200,39 @@
 class NamedFlow extends EventTarget {
   NamedFlow.internal() : super.internal();
 
-  @DomName('NamedFlow.firstEmptyRegionIndex')
+  @DomName('WebKitNamedFlow.firstEmptyRegionIndex')
   @DocsEditable
   int get firstEmptyRegionIndex native "NamedFlow_firstEmptyRegionIndex_Getter";
 
-  @DomName('NamedFlow.name')
+  @DomName('WebKitNamedFlow.name')
   @DocsEditable
   String get name native "NamedFlow_name_Getter";
 
-  @DomName('NamedFlow.overset')
+  @DomName('WebKitNamedFlow.overset')
   @DocsEditable
   bool get overset native "NamedFlow_overset_Getter";
 
-  @DomName('NamedFlow.addEventListener')
+  @DomName('WebKitNamedFlow.addEventListener')
   @DocsEditable
   void $dom_addEventListener(String type, EventListener listener, [bool useCapture]) native "NamedFlow_addEventListener_Callback";
 
-  @DomName('NamedFlow.dispatchEvent')
+  @DomName('WebKitNamedFlow.dispatchEvent')
   @DocsEditable
   bool dispatchEvent(Event event) native "NamedFlow_dispatchEvent_Callback";
 
-  @DomName('NamedFlow.getContent')
+  @DomName('WebKitNamedFlow.getContent')
   @DocsEditable
   List<Node> getContent() native "NamedFlow_getContent_Callback";
 
-  @DomName('NamedFlow.getRegions')
+  @DomName('WebKitNamedFlow.getRegions')
   @DocsEditable
   List<Node> getRegions() native "NamedFlow_getRegions_Callback";
 
-  @DomName('NamedFlow.getRegionsByContent')
+  @DomName('WebKitNamedFlow.getRegionsByContent')
   @DocsEditable
   List<Node> getRegionsByContent(Node contentNode) native "NamedFlow_getRegionsByContent_Callback";
 
-  @DomName('NamedFlow.removeEventListener')
+  @DomName('WebKitNamedFlow.removeEventListener')
   @DocsEditable
   void $dom_removeEventListener(String type, EventListener listener, [bool useCapture]) native "NamedFlow_removeEventListener_Callback";
 
@@ -15885,15 +15252,15 @@
 class NamedFlowCollection extends NativeFieldWrapperClass1 {
   NamedFlowCollection.internal();
 
-  @DomName('DOMNamedFlowCollection.length')
+  @DomName('WebKitNamedFlowCollection.length')
   @DocsEditable
   int get length native "DOMNamedFlowCollection_length_Getter";
 
-  @DomName('DOMNamedFlowCollection.item')
+  @DomName('WebKitNamedFlowCollection.item')
   @DocsEditable
   NamedFlow item(int index) native "DOMNamedFlowCollection_item_Callback";
 
-  @DomName('DOMNamedFlowCollection.namedItem')
+  @DomName('WebKitNamedFlowCollection.namedItem')
   @DocsEditable
   NamedFlow namedItem(String name) native "DOMNamedFlowCollection_namedItem_Callback";
 
@@ -16204,7 +15571,7 @@
     // time.
     Node child = _this.$dom_firstChild;
     while (child != null) {
-      Node nextChild = child.nextSibling;
+      Node nextChild = child.nextNode;
       if (test(child) == removeMatching) {
         _this.$dom_removeChild(child);
       }
@@ -16329,94 +15696,50 @@
     }
   }
 
-  // Note that this may either be the locally set model or a cached value
-  // of the inherited model. This is cached to minimize model change
-  // notifications.
-  var _model;
-  bool _hasLocalModel;
-  Set<StreamController<Node>> _modelChangedStreams;
-
-  /**
-   * The data model which is inherited through the tree.
-   *
-   * Setting this will propagate the value to all descendant nodes. If the
-   * model is not set on this node then it will be inherited from ancestor
-   * nodes.
-   *
-   * Currently this does not support propagation through Shadow DOMs.
-   *
-   * [clearModel] must be used to remove the model property from this node
-   * and have the model inherit from ancestor nodes.
-   */
-  @Experimental
-  get model {
-    // If we have a change handler then we've cached the model locally.
-    if (_modelChangedStreams != null && !_modelChangedStreams.isEmpty) {
-      return _model;
-    }
-    // Otherwise start looking up the tree.
-    for (var node = this; node != null; node = node.parentNode) {
-      if (node._hasLocalModel == true) {
-        return node._model;
-      }
-    }
-    return null;
-  }
-
-  @Experimental
-  void set model(value) {
-    var changed = model != value;
-    _model = value;
-    _hasLocalModel = true;
-    _ModelTreeObserver.initialize();
-
-    if (changed) {
-      if (_modelChangedStreams != null && !_modelChangedStreams.isEmpty) {
-        _modelChangedStreams.toList().forEach((stream) => stream.add(this));
-      }
-      // Propagate new model to all descendants.
-      _ModelTreeObserver.propagateModel(this, value, false);
-    }
-  }
-
-  /**
-   * Clears the locally set model and makes this model be inherited from parent
-   * nodes.
-   */
-  @Experimental
-  void clearModel() {
-    if (_hasLocalModel == true) {
-      _hasLocalModel = false;
-
-      // Propagate new model to all descendants.
-      if (parentNode != null) {
-        _ModelTreeObserver.propagateModel(this, parentNode.model, false);
-      } else {
-        _ModelTreeObserver.propagateModel(this, null, false);
-      }
-    }
-  }
-
-  /**
-   * Get a stream of models, whenever the model changes.
-   */
-  Stream<Node> get onModelChanged {
-    if (_modelChangedStreams == null) {
-      _modelChangedStreams = new Set<StreamController<Node>>();
-    }
-    var controller;
-    controller = new StreamController(
-        onListen: () { _modelChangedStreams.add(controller); },
-        onCancel: () { _modelChangedStreams.remove(controller); });
-    return controller.stream;
-  }
-
   /**
    * Print out a String representation of this Node.
    */
   String toString() => localName == null ?
       (nodeValue == null ? super.toString() : nodeValue) : localName;
 
+  /**
+   * Binds the attribute [name] to the [path] of the [model].
+   * Path is a String of accessors such as `foo.bar.baz`.
+   */
+  @Experimental
+  void bind(String name, model, String path) {
+    // TODO(jmesserly): should we throw instead?
+    window.console.error('Unhandled binding to Node: '
+        '$this $name $model $path');
+  }
+
+  /** Unbinds the attribute [name]. */
+  @Experimental
+  void unbind(String name) {}
+
+  /** Unbinds all bound attributes. */
+  @Experimental
+  void unbindAll() {}
+
+  TemplateInstance _templateInstance;
+
+  // TODO(arv): Consider storing all "NodeRareData" on a single object?
+  int __instanceTerminatorCount;
+  int get _instanceTerminatorCount {
+    if (__instanceTerminatorCount == null) return 0;
+    return __instanceTerminatorCount;
+  }
+  set _instanceTerminatorCount(int value) {
+    if (value == 0) value = null;
+    __instanceTerminatorCount = value;
+  }
+
+  /** Gets the template instance that instantiated this node, if any. */
+  @Experimental
+  TemplateInstance get templateInstance =>
+      _templateInstance != null ? _templateInstance :
+      (parent != null ? parent.templateInstance : null);
+
   Node.internal() : super.internal();
 
   static const int ATTRIBUTE_NODE = 2;
@@ -16635,7 +15958,7 @@
 
 @DocsEditable
 @DomName('NodeList')
-class NodeList extends NativeFieldWrapperClass1 implements List<Node> {
+class NodeList extends NativeFieldWrapperClass1 with ListMixin<Node>, ImmutableListMixin<Node> implements List<Node> {
   NodeList.internal();
 
   @DomName('NodeList.length')
@@ -16650,196 +15973,11 @@
   // -- start List<Node> mixins.
   // Node is the element type.
 
-  // From Iterable<Node>:
 
-  Iterator<Node> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<Node>(this);
-  }
-
-  Node reduce(Node combine(Node value, Node element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, Node element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(Node element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(Node element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(Node element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<Node> where(bool f(Node element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(Node element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(Node element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(Node element)) => IterableMixinWorkaround.any(this, f);
-
-  List<Node> toList({ bool growable: true }) =>
-      new List<Node>.from(this, growable: growable);
-
-  Set<Node> toSet() => new Set<Node>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<Node> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<Node> takeWhile(bool test(Node value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<Node> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<Node> skipWhile(bool test(Node value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  Node firstWhere(bool test(Node value), { Node orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  Node lastWhere(bool test(Node value), {Node orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  Node singleWhere(bool test(Node value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  Node elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<Node>:
-
-  void add(Node value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<Node>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<Node> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(Node a, Node b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(Node element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(Node element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  Node get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  Node get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  Node get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, Node element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Node removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  Node removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(Node element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(Node element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<Node> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [Node fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<Node> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<Node> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <Node>[]);
-  }
-
-  Map<int, Node> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<Node> mixins.
 
   @DomName('NodeList.item')
@@ -17493,7 +16631,7 @@
 class Path extends NativeFieldWrapperClass1 {
   Path.internal();
 
-  @DomName('DOMPath.DOMPath')
+  @DomName('Path.DOMPath')
   @DocsEditable
   factory Path([path_OR_text]) {
     if (!?path_OR_text) {
@@ -17517,35 +16655,35 @@
   @DocsEditable
   static Path _create_3(path_OR_text) native "DOMPath__create_3constructorCallback";
 
-  @DomName('DOMPath.arc')
+  @DomName('Path.arc')
   @DocsEditable
   void arc(num x, num y, num radius, num startAngle, num endAngle, bool anticlockwise) native "DOMPath_arc_Callback";
 
-  @DomName('DOMPath.arcTo')
+  @DomName('Path.arcTo')
   @DocsEditable
   void arcTo(num x1, num y1, num x2, num y2, num radius) native "DOMPath_arcTo_Callback";
 
-  @DomName('DOMPath.bezierCurveTo')
+  @DomName('Path.bezierCurveTo')
   @DocsEditable
   void bezierCurveTo(num cp1x, num cp1y, num cp2x, num cp2y, num x, num y) native "DOMPath_bezierCurveTo_Callback";
 
-  @DomName('DOMPath.closePath')
+  @DomName('Path.closePath')
   @DocsEditable
   void closePath() native "DOMPath_closePath_Callback";
 
-  @DomName('DOMPath.lineTo')
+  @DomName('Path.lineTo')
   @DocsEditable
   void lineTo(num x, num y) native "DOMPath_lineTo_Callback";
 
-  @DomName('DOMPath.moveTo')
+  @DomName('Path.moveTo')
   @DocsEditable
   void moveTo(num x, num y) native "DOMPath_moveTo_Callback";
 
-  @DomName('DOMPath.quadraticCurveTo')
+  @DomName('Path.quadraticCurveTo')
   @DocsEditable
   void quadraticCurveTo(num cpx, num cpy, num x, num y) native "DOMPath_quadraticCurveTo_Callback";
 
-  @DomName('DOMPath.rect')
+  @DomName('Path.rect')
   @DocsEditable
   void rect(num x, num y, num width, num height) native "DOMPath_rect_Callback";
 
@@ -17923,27 +17061,27 @@
 class Plugin extends NativeFieldWrapperClass1 {
   Plugin.internal();
 
-  @DomName('DOMPlugin.description')
+  @DomName('Plugin.description')
   @DocsEditable
   String get description native "DOMPlugin_description_Getter";
 
-  @DomName('DOMPlugin.filename')
+  @DomName('Plugin.filename')
   @DocsEditable
   String get filename native "DOMPlugin_filename_Getter";
 
-  @DomName('DOMPlugin.length')
+  @DomName('Plugin.length')
   @DocsEditable
   int get length native "DOMPlugin_length_Getter";
 
-  @DomName('DOMPlugin.name')
+  @DomName('Plugin.name')
   @DocsEditable
   String get name native "DOMPlugin_name_Getter";
 
-  @DomName('DOMPlugin.item')
+  @DomName('Plugin.item')
   @DocsEditable
   MimeType item(int index) native "DOMPlugin_item_Callback";
 
-  @DomName('DOMPlugin.namedItem')
+  @DomName('Plugin.namedItem')
   @DocsEditable
   MimeType namedItem(String name) native "DOMPlugin_namedItem_Callback";
 
@@ -17957,10 +17095,10 @@
 
 @DocsEditable
 @DomName('PluginArray')
-class PluginArray extends NativeFieldWrapperClass1 implements List<Plugin> {
+class PluginArray extends NativeFieldWrapperClass1 with ListMixin<Plugin>, ImmutableListMixin<Plugin> implements List<Plugin> {
   PluginArray.internal();
 
-  @DomName('DOMPluginArray.length')
+  @DomName('PluginArray.length')
   @DocsEditable
   int get length native "DOMPluginArray_length_Getter";
 
@@ -17972,207 +17110,22 @@
   // -- start List<Plugin> mixins.
   // Plugin is the element type.
 
-  // From Iterable<Plugin>:
 
-  Iterator<Plugin> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<Plugin>(this);
-  }
-
-  Plugin reduce(Plugin combine(Plugin value, Plugin element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, Plugin element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(Plugin element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(Plugin element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(Plugin element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<Plugin> where(bool f(Plugin element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(Plugin element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(Plugin element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(Plugin element)) => IterableMixinWorkaround.any(this, f);
-
-  List<Plugin> toList({ bool growable: true }) =>
-      new List<Plugin>.from(this, growable: growable);
-
-  Set<Plugin> toSet() => new Set<Plugin>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<Plugin> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<Plugin> takeWhile(bool test(Plugin value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<Plugin> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<Plugin> skipWhile(bool test(Plugin value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  Plugin firstWhere(bool test(Plugin value), { Plugin orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  Plugin lastWhere(bool test(Plugin value), {Plugin orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  Plugin singleWhere(bool test(Plugin value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  Plugin elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<Plugin>:
-
-  void add(Plugin value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<Plugin> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<Plugin>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<Plugin> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(Plugin a, Plugin b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(Plugin element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(Plugin element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  Plugin get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  Plugin get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  Plugin get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, Plugin element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<Plugin> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<Plugin> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Plugin removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  Plugin removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(Plugin element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(Plugin element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<Plugin> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<Plugin> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [Plugin fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<Plugin> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<Plugin> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <Plugin>[]);
-  }
-
-  Map<int, Plugin> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<Plugin> mixins.
 
-  @DomName('DOMPluginArray.item')
+  @DomName('PluginArray.item')
   @DocsEditable
   Plugin item(int index) native "DOMPluginArray_item_Callback";
 
-  @DomName('DOMPluginArray.namedItem')
+  @DomName('PluginArray.namedItem')
   @DocsEditable
   Plugin namedItem(String name) native "DOMPluginArray_namedItem_Callback";
 
-  @DomName('DOMPluginArray.refresh')
+  @DomName('PluginArray.refresh')
   @DocsEditable
   void refresh(bool reload) native "DOMPluginArray_refresh_Callback";
 
@@ -19427,63 +18380,63 @@
 class SecurityPolicy extends NativeFieldWrapperClass1 {
   SecurityPolicy.internal();
 
-  @DomName('DOMSecurityPolicy.allowsEval')
+  @DomName('SecurityPolicy.allowsEval')
   @DocsEditable
   bool get allowsEval native "DOMSecurityPolicy_allowsEval_Getter";
 
-  @DomName('DOMSecurityPolicy.allowsInlineScript')
+  @DomName('SecurityPolicy.allowsInlineScript')
   @DocsEditable
   bool get allowsInlineScript native "DOMSecurityPolicy_allowsInlineScript_Getter";
 
-  @DomName('DOMSecurityPolicy.allowsInlineStyle')
+  @DomName('SecurityPolicy.allowsInlineStyle')
   @DocsEditable
   bool get allowsInlineStyle native "DOMSecurityPolicy_allowsInlineStyle_Getter";
 
-  @DomName('DOMSecurityPolicy.isActive')
+  @DomName('SecurityPolicy.isActive')
   @DocsEditable
   bool get isActive native "DOMSecurityPolicy_isActive_Getter";
 
-  @DomName('DOMSecurityPolicy.reportURIs')
+  @DomName('SecurityPolicy.reportURIs')
   @DocsEditable
   List<String> get reportURIs native "DOMSecurityPolicy_reportURIs_Getter";
 
-  @DomName('DOMSecurityPolicy.allowsConnectionTo')
+  @DomName('SecurityPolicy.allowsConnectionTo')
   @DocsEditable
   bool allowsConnectionTo(String url) native "DOMSecurityPolicy_allowsConnectionTo_Callback";
 
-  @DomName('DOMSecurityPolicy.allowsFontFrom')
+  @DomName('SecurityPolicy.allowsFontFrom')
   @DocsEditable
   bool allowsFontFrom(String url) native "DOMSecurityPolicy_allowsFontFrom_Callback";
 
-  @DomName('DOMSecurityPolicy.allowsFormAction')
+  @DomName('SecurityPolicy.allowsFormAction')
   @DocsEditable
   bool allowsFormAction(String url) native "DOMSecurityPolicy_allowsFormAction_Callback";
 
-  @DomName('DOMSecurityPolicy.allowsFrameFrom')
+  @DomName('SecurityPolicy.allowsFrameFrom')
   @DocsEditable
   bool allowsFrameFrom(String url) native "DOMSecurityPolicy_allowsFrameFrom_Callback";
 
-  @DomName('DOMSecurityPolicy.allowsImageFrom')
+  @DomName('SecurityPolicy.allowsImageFrom')
   @DocsEditable
   bool allowsImageFrom(String url) native "DOMSecurityPolicy_allowsImageFrom_Callback";
 
-  @DomName('DOMSecurityPolicy.allowsMediaFrom')
+  @DomName('SecurityPolicy.allowsMediaFrom')
   @DocsEditable
   bool allowsMediaFrom(String url) native "DOMSecurityPolicy_allowsMediaFrom_Callback";
 
-  @DomName('DOMSecurityPolicy.allowsObjectFrom')
+  @DomName('SecurityPolicy.allowsObjectFrom')
   @DocsEditable
   bool allowsObjectFrom(String url) native "DOMSecurityPolicy_allowsObjectFrom_Callback";
 
-  @DomName('DOMSecurityPolicy.allowsPluginType')
+  @DomName('SecurityPolicy.allowsPluginType')
   @DocsEditable
   bool allowsPluginType(String type) native "DOMSecurityPolicy_allowsPluginType_Callback";
 
-  @DomName('DOMSecurityPolicy.allowsScriptFrom')
+  @DomName('SecurityPolicy.allowsScriptFrom')
   @DocsEditable
   bool allowsScriptFrom(String url) native "DOMSecurityPolicy_allowsScriptFrom_Callback";
 
-  @DomName('DOMSecurityPolicy.allowsStyleFrom')
+  @DomName('SecurityPolicy.allowsStyleFrom')
   @DocsEditable
   bool allowsStyleFrom(String url) native "DOMSecurityPolicy_allowsStyleFrom_Callback";
 
@@ -19688,107 +18641,107 @@
 class Selection extends NativeFieldWrapperClass1 {
   Selection.internal();
 
-  @DomName('DOMSelection.anchorNode')
+  @DomName('Selection.anchorNode')
   @DocsEditable
   Node get anchorNode native "DOMSelection_anchorNode_Getter";
 
-  @DomName('DOMSelection.anchorOffset')
+  @DomName('Selection.anchorOffset')
   @DocsEditable
   int get anchorOffset native "DOMSelection_anchorOffset_Getter";
 
-  @DomName('DOMSelection.baseNode')
+  @DomName('Selection.baseNode')
   @DocsEditable
   Node get baseNode native "DOMSelection_baseNode_Getter";
 
-  @DomName('DOMSelection.baseOffset')
+  @DomName('Selection.baseOffset')
   @DocsEditable
   int get baseOffset native "DOMSelection_baseOffset_Getter";
 
-  @DomName('DOMSelection.extentNode')
+  @DomName('Selection.extentNode')
   @DocsEditable
   Node get extentNode native "DOMSelection_extentNode_Getter";
 
-  @DomName('DOMSelection.extentOffset')
+  @DomName('Selection.extentOffset')
   @DocsEditable
   int get extentOffset native "DOMSelection_extentOffset_Getter";
 
-  @DomName('DOMSelection.focusNode')
+  @DomName('Selection.focusNode')
   @DocsEditable
   Node get focusNode native "DOMSelection_focusNode_Getter";
 
-  @DomName('DOMSelection.focusOffset')
+  @DomName('Selection.focusOffset')
   @DocsEditable
   int get focusOffset native "DOMSelection_focusOffset_Getter";
 
-  @DomName('DOMSelection.isCollapsed')
+  @DomName('Selection.isCollapsed')
   @DocsEditable
   bool get isCollapsed native "DOMSelection_isCollapsed_Getter";
 
-  @DomName('DOMSelection.rangeCount')
+  @DomName('Selection.rangeCount')
   @DocsEditable
   int get rangeCount native "DOMSelection_rangeCount_Getter";
 
-  @DomName('DOMSelection.type')
+  @DomName('Selection.type')
   @DocsEditable
   String get type native "DOMSelection_type_Getter";
 
-  @DomName('DOMSelection.addRange')
+  @DomName('Selection.addRange')
   @DocsEditable
   void addRange(Range range) native "DOMSelection_addRange_Callback";
 
-  @DomName('DOMSelection.collapse')
+  @DomName('Selection.collapse')
   @DocsEditable
   void collapse(Node node, int index) native "DOMSelection_collapse_Callback";
 
-  @DomName('DOMSelection.collapseToEnd')
+  @DomName('Selection.collapseToEnd')
   @DocsEditable
   void collapseToEnd() native "DOMSelection_collapseToEnd_Callback";
 
-  @DomName('DOMSelection.collapseToStart')
+  @DomName('Selection.collapseToStart')
   @DocsEditable
   void collapseToStart() native "DOMSelection_collapseToStart_Callback";
 
-  @DomName('DOMSelection.containsNode')
+  @DomName('Selection.containsNode')
   @DocsEditable
   bool containsNode(Node node, bool allowPartial) native "DOMSelection_containsNode_Callback";
 
-  @DomName('DOMSelection.deleteFromDocument')
+  @DomName('Selection.deleteFromDocument')
   @DocsEditable
   void deleteFromDocument() native "DOMSelection_deleteFromDocument_Callback";
 
-  @DomName('DOMSelection.empty')
+  @DomName('Selection.empty')
   @DocsEditable
   void empty() native "DOMSelection_empty_Callback";
 
-  @DomName('DOMSelection.extend')
+  @DomName('Selection.extend')
   @DocsEditable
   void extend(Node node, int offset) native "DOMSelection_extend_Callback";
 
-  @DomName('DOMSelection.getRangeAt')
+  @DomName('Selection.getRangeAt')
   @DocsEditable
   Range getRangeAt(int index) native "DOMSelection_getRangeAt_Callback";
 
-  @DomName('DOMSelection.modify')
+  @DomName('Selection.modify')
   @DocsEditable
   void modify(String alter, String direction, String granularity) native "DOMSelection_modify_Callback";
 
-  @DomName('DOMSelection.removeAllRanges')
+  @DomName('Selection.removeAllRanges')
   @DocsEditable
   void removeAllRanges() native "DOMSelection_removeAllRanges_Callback";
 
-  @DomName('DOMSelection.selectAllChildren')
+  @DomName('Selection.selectAllChildren')
   @DocsEditable
   void selectAllChildren(Node node) native "DOMSelection_selectAllChildren_Callback";
 
-  @DomName('DOMSelection.setBaseAndExtent')
+  @DomName('Selection.setBaseAndExtent')
   @DocsEditable
   void setBaseAndExtent(Node baseNode, int baseOffset, Node extentNode, int extentOffset) native "DOMSelection_setBaseAndExtent_Callback";
 
-  @DomName('DOMSelection.setPosition')
+  @DomName('Selection.setPosition')
   @DocsEditable
   void setPosition(Node node, int offset) native "DOMSelection_setPosition_Callback";
 
-  @DomName('DOMSelection.toString')
+  @DomName('Selection.toString')
   @DocsEditable
   String toString() native "DOMSelection_toString_Callback";
 
@@ -19920,7 +18873,7 @@
 
   @DomName('SourceBuffer.append')
   @DocsEditable
-  void append(List<int> data) native "SourceBuffer_append_Callback";
+  void append(Uint8List data) native "SourceBuffer_append_Callback";
 
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
@@ -19932,7 +18885,7 @@
 
 @DocsEditable
 @DomName('SourceBufferList')
-class SourceBufferList extends EventTarget implements List<SourceBuffer> {
+class SourceBufferList extends EventTarget with ListMixin<SourceBuffer>, ImmutableListMixin<SourceBuffer> implements List<SourceBuffer> {
   SourceBufferList.internal() : super.internal();
 
   @DomName('SourceBufferList.length')
@@ -19947,196 +18900,11 @@
   // -- start List<SourceBuffer> mixins.
   // SourceBuffer is the element type.
 
-  // From Iterable<SourceBuffer>:
 
-  Iterator<SourceBuffer> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<SourceBuffer>(this);
-  }
-
-  SourceBuffer reduce(SourceBuffer combine(SourceBuffer value, SourceBuffer element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, SourceBuffer element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(SourceBuffer element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(SourceBuffer element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(SourceBuffer element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<SourceBuffer> where(bool f(SourceBuffer element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(SourceBuffer element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(SourceBuffer element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(SourceBuffer element)) => IterableMixinWorkaround.any(this, f);
-
-  List<SourceBuffer> toList({ bool growable: true }) =>
-      new List<SourceBuffer>.from(this, growable: growable);
-
-  Set<SourceBuffer> toSet() => new Set<SourceBuffer>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<SourceBuffer> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<SourceBuffer> takeWhile(bool test(SourceBuffer value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<SourceBuffer> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<SourceBuffer> skipWhile(bool test(SourceBuffer value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  SourceBuffer firstWhere(bool test(SourceBuffer value), { SourceBuffer orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  SourceBuffer lastWhere(bool test(SourceBuffer value), {SourceBuffer orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  SourceBuffer singleWhere(bool test(SourceBuffer value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  SourceBuffer elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<SourceBuffer>:
-
-  void add(SourceBuffer value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<SourceBuffer> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<SourceBuffer>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<SourceBuffer> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(SourceBuffer a, SourceBuffer b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(SourceBuffer element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(SourceBuffer element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  SourceBuffer get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  SourceBuffer get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  SourceBuffer get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, SourceBuffer element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<SourceBuffer> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<SourceBuffer> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  SourceBuffer removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  SourceBuffer removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(SourceBuffer element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(SourceBuffer element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<SourceBuffer> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<SourceBuffer> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [SourceBuffer fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<SourceBuffer> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<SourceBuffer> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <SourceBuffer>[]);
-  }
-
-  Map<int, SourceBuffer> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<SourceBuffer> mixins.
 
   @DomName('SourceBufferList.addEventListener')
@@ -20261,7 +19029,7 @@
 
 @DocsEditable
 @DomName('SpeechGrammarList')
-class SpeechGrammarList extends NativeFieldWrapperClass1 implements List<SpeechGrammar> {
+class SpeechGrammarList extends NativeFieldWrapperClass1 with ListMixin<SpeechGrammar>, ImmutableListMixin<SpeechGrammar> implements List<SpeechGrammar> {
   SpeechGrammarList.internal();
 
   @DomName('SpeechGrammarList.SpeechGrammarList')
@@ -20285,196 +19053,11 @@
   // -- start List<SpeechGrammar> mixins.
   // SpeechGrammar is the element type.
 
-  // From Iterable<SpeechGrammar>:
 
-  Iterator<SpeechGrammar> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<SpeechGrammar>(this);
-  }
-
-  SpeechGrammar reduce(SpeechGrammar combine(SpeechGrammar value, SpeechGrammar element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, SpeechGrammar element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(SpeechGrammar element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(SpeechGrammar element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(SpeechGrammar element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<SpeechGrammar> where(bool f(SpeechGrammar element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(SpeechGrammar element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(SpeechGrammar element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(SpeechGrammar element)) => IterableMixinWorkaround.any(this, f);
-
-  List<SpeechGrammar> toList({ bool growable: true }) =>
-      new List<SpeechGrammar>.from(this, growable: growable);
-
-  Set<SpeechGrammar> toSet() => new Set<SpeechGrammar>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<SpeechGrammar> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<SpeechGrammar> takeWhile(bool test(SpeechGrammar value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<SpeechGrammar> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<SpeechGrammar> skipWhile(bool test(SpeechGrammar value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  SpeechGrammar firstWhere(bool test(SpeechGrammar value), { SpeechGrammar orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  SpeechGrammar lastWhere(bool test(SpeechGrammar value), {SpeechGrammar orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  SpeechGrammar singleWhere(bool test(SpeechGrammar value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  SpeechGrammar elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<SpeechGrammar>:
-
-  void add(SpeechGrammar value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<SpeechGrammar> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<SpeechGrammar>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<SpeechGrammar> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(SpeechGrammar a, SpeechGrammar b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(SpeechGrammar element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(SpeechGrammar element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  SpeechGrammar get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  SpeechGrammar get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  SpeechGrammar get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, SpeechGrammar element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<SpeechGrammar> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<SpeechGrammar> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  SpeechGrammar removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  SpeechGrammar removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(SpeechGrammar element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(SpeechGrammar element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<SpeechGrammar> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<SpeechGrammar> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [SpeechGrammar fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<SpeechGrammar> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<SpeechGrammar> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <SpeechGrammar>[]);
-  }
-
-  Map<int, SpeechGrammar> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<SpeechGrammar> mixins.
 
   void addFromString(String string, [num weight]) {
@@ -21485,15 +20068,131 @@
 // WARNING: Do not edit - generated code.
 
 
-@DocsEditable
+@Experimental
 @DomName('HTMLTemplateElement')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@Experimental
 class TemplateElement extends _Element_Merged {
   TemplateElement.internal() : super.internal();
 
+  @DomName('HTMLTemplateElement.HTMLTemplateElement')
+  @DocsEditable
+  factory TemplateElement() => document.$dom_createElement("template");
+
+  /// Checks if this type is supported on the current platform.
+  static bool get supported => true;
+
   @DomName('HTMLTemplateElement.content')
   @DocsEditable
-  DocumentFragment get content native "HTMLTemplateElement_content_Getter";
+  DocumentFragment get $dom_content native "HTMLTemplateElement_content_Getter";
 
+
+  // For real TemplateElement use the actual DOM .content field instead of
+  // our polyfilled expando.
+  @Experimental
+  DocumentFragment get content => $dom_content;
+
+  static StreamController<DocumentFragment> _instanceCreated;
+
+  /**
+   * *Warning*: This is an implementation helper for Model-Driven Views and
+   * should not be used in your code.
+   *
+   * This event is fired whenever a template is instantiated via
+   * [createInstance].
+   */
+  // TODO(rafaelw): This is a hack, and is neccesary for the polyfill
+  // because custom elements are not upgraded during clone()
+  @Experimental
+  static Stream<DocumentFragment> get instanceCreated {
+    if (_instanceCreated == null) {
+      _instanceCreated = new StreamController<DocumentFragment>();
+    }
+    return _instanceCreated.stream;
+  }
+
+  /**
+   * Ensures proper API and content model for template elements.
+   *
+   * [instanceRef] can be used to set the [Element.ref] property of [template],
+   * and use the ref's content will be used as source when createInstance() is
+   * invoked.
+   *
+   * Returns true if this template was just decorated, or false if it was
+   * already decorated.
+   */
+  @Experimental
+  static bool decorate(Element template, [Element instanceRef]) {
+    // == true check because it starts as a null field.
+    if (template._templateIsDecorated == true) return false;
+
+    template._templateIsDecorated = true;
+
+    _injectStylesheet();
+
+    // Create content
+    if (template is! TemplateElement) {
+      var doc = _getTemplateContentsOwner(template.document);
+      template._templateContent = doc.createDocumentFragment();
+    }
+
+    if (instanceRef != null) {
+      template._templateInstanceRef = instanceRef;
+      return true; // content is empty.
+    }
+
+    if (template is TemplateElement) {
+      _bootstrapTemplatesRecursivelyFrom(template.content);
+    } else {
+      _liftNonNativeTemplateChildrenIntoContent(template);
+    }
+
+    return true;
+  }
+
+  /**
+   * This used to decorate recursively all templates from a given node.
+   *
+   * By default [decorate] will be called on templates lazily when certain
+   * properties such as [model] are accessed, but it can be run eagerly to
+   * decorate an entire tree recursively.
+   */
+  // TODO(rafaelw): Review whether this is the right public API.
+  @Experimental
+  static void bootstrap(Node content) {
+    _bootstrapTemplatesRecursivelyFrom(content);
+  }
+
+  static bool _initStyles;
+
+  static void _injectStylesheet() {
+    if (_initStyles == true) return;
+    _initStyles = true;
+
+    var style = new StyleElement();
+    style.text = r'''
+template,
+thead[template],
+tbody[template],
+tfoot[template],
+th[template],
+tr[template],
+td[template],
+caption[template],
+colgroup[template],
+col[template],
+option[template] {
+  display: none;
+}''';
+    document.head.append(style);
+  }
+
+  /**
+   * A mapping of names to Custom Syntax objects. See [CustomBindingSyntax] for
+   * more information.
+   */
+  @Experimental
+  static Map<String, CustomBindingSyntax> syntax = {};
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -21526,6 +20225,41 @@
   @DocsEditable
   Text splitText(int offset) native "Text_splitText_Callback";
 
+
+  StreamSubscription _textBinding;
+
+  @Experimental
+  void bind(String name, model, String path) {
+    if (name != 'text') {
+      super.bind(name, model, path);
+      return;
+    }
+
+    unbind('text');
+
+    _textBinding = new PathObserver(model, path).bindSync((value) {
+      text = value == null ? '' : '$value';
+    });
+  }
+
+  @Experimental
+  void unbind(String name) {
+    if (name != 'text') {
+      super.unbind(name);
+      return;
+    }
+
+    if (_textBinding == null) return;
+
+    _textBinding.cancel();
+    _textBinding = null;
+  }
+
+  @Experimental
+  void unbindAll() {
+    unbind('text');
+    super.unbindAll();
+  }
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -22011,7 +20745,7 @@
 
 @DocsEditable
 @DomName('TextTrackCueList')
-class TextTrackCueList extends NativeFieldWrapperClass1 implements List<TextTrackCue> {
+class TextTrackCueList extends NativeFieldWrapperClass1 with ListMixin<TextTrackCue>, ImmutableListMixin<TextTrackCue> implements List<TextTrackCue> {
   TextTrackCueList.internal();
 
   @DomName('TextTrackCueList.length')
@@ -22026,196 +20760,11 @@
   // -- start List<TextTrackCue> mixins.
   // TextTrackCue is the element type.
 
-  // From Iterable<TextTrackCue>:
 
-  Iterator<TextTrackCue> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<TextTrackCue>(this);
-  }
-
-  TextTrackCue reduce(TextTrackCue combine(TextTrackCue value, TextTrackCue element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, TextTrackCue element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(TextTrackCue element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(TextTrackCue element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(TextTrackCue element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<TextTrackCue> where(bool f(TextTrackCue element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(TextTrackCue element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(TextTrackCue element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(TextTrackCue element)) => IterableMixinWorkaround.any(this, f);
-
-  List<TextTrackCue> toList({ bool growable: true }) =>
-      new List<TextTrackCue>.from(this, growable: growable);
-
-  Set<TextTrackCue> toSet() => new Set<TextTrackCue>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<TextTrackCue> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<TextTrackCue> takeWhile(bool test(TextTrackCue value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<TextTrackCue> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<TextTrackCue> skipWhile(bool test(TextTrackCue value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  TextTrackCue firstWhere(bool test(TextTrackCue value), { TextTrackCue orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  TextTrackCue lastWhere(bool test(TextTrackCue value), {TextTrackCue orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  TextTrackCue singleWhere(bool test(TextTrackCue value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  TextTrackCue elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<TextTrackCue>:
-
-  void add(TextTrackCue value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<TextTrackCue> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<TextTrackCue>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<TextTrackCue> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(TextTrackCue a, TextTrackCue b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(TextTrackCue element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(TextTrackCue element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  TextTrackCue get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  TextTrackCue get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  TextTrackCue get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, TextTrackCue element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<TextTrackCue> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<TextTrackCue> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  TextTrackCue removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  TextTrackCue removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(TextTrackCue element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(TextTrackCue element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<TextTrackCue> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<TextTrackCue> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [TextTrackCue fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<TextTrackCue> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<TextTrackCue> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <TextTrackCue>[]);
-  }
-
-  Map<int, TextTrackCue> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<TextTrackCue> mixins.
 
   @DomName('TextTrackCueList.getCueById')
@@ -22236,7 +20785,7 @@
 
 @DocsEditable
 @DomName('TextTrackList')
-class TextTrackList extends EventTarget implements List<TextTrack> {
+class TextTrackList extends EventTarget with ListMixin<TextTrack>, ImmutableListMixin<TextTrack> implements List<TextTrack> {
   TextTrackList.internal() : super.internal();
 
   @DomName('TextTrackList.addtrackEvent')
@@ -22255,196 +20804,11 @@
   // -- start List<TextTrack> mixins.
   // TextTrack is the element type.
 
-  // From Iterable<TextTrack>:
 
-  Iterator<TextTrack> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<TextTrack>(this);
-  }
-
-  TextTrack reduce(TextTrack combine(TextTrack value, TextTrack element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, TextTrack element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(TextTrack element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(TextTrack element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(TextTrack element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<TextTrack> where(bool f(TextTrack element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(TextTrack element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(TextTrack element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(TextTrack element)) => IterableMixinWorkaround.any(this, f);
-
-  List<TextTrack> toList({ bool growable: true }) =>
-      new List<TextTrack>.from(this, growable: growable);
-
-  Set<TextTrack> toSet() => new Set<TextTrack>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<TextTrack> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<TextTrack> takeWhile(bool test(TextTrack value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<TextTrack> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<TextTrack> skipWhile(bool test(TextTrack value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  TextTrack firstWhere(bool test(TextTrack value), { TextTrack orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  TextTrack lastWhere(bool test(TextTrack value), {TextTrack orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  TextTrack singleWhere(bool test(TextTrack value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  TextTrack elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<TextTrack>:
-
-  void add(TextTrack value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<TextTrack> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<TextTrack>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<TextTrack> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(TextTrack a, TextTrack b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(TextTrack element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(TextTrack element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  TextTrack get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  TextTrack get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  TextTrack get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, TextTrack element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<TextTrack> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<TextTrack> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  TextTrack removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  TextTrack removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(TextTrack element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(TextTrack element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<TextTrack> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<TextTrack> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [TextTrack fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<TextTrack> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<TextTrack> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <TextTrack>[]);
-  }
-
-  Map<int, TextTrack> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<TextTrack> mixins.
 
   @DomName('TextTrackList.addEventListener')
@@ -22679,7 +21043,7 @@
 
 
 @DomName('TouchList')
-class TouchList extends NativeFieldWrapperClass1 implements List<Touch> {
+class TouchList extends NativeFieldWrapperClass1 with ListMixin<Touch>, ImmutableListMixin<Touch> implements List<Touch> {
   /// NB: This constructor likely does not work as you might expect it to! This
   /// constructor will simply fail (returning null) if you are not on a device
   /// with touch enabled. See dartbug.com/8314.
@@ -22701,196 +21065,11 @@
   // -- start List<Touch> mixins.
   // Touch is the element type.
 
-  // From Iterable<Touch>:
 
-  Iterator<Touch> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<Touch>(this);
-  }
-
-  Touch reduce(Touch combine(Touch value, Touch element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, Touch element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(Touch element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(Touch element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(Touch element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<Touch> where(bool f(Touch element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(Touch element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(Touch element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(Touch element)) => IterableMixinWorkaround.any(this, f);
-
-  List<Touch> toList({ bool growable: true }) =>
-      new List<Touch>.from(this, growable: growable);
-
-  Set<Touch> toSet() => new Set<Touch>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<Touch> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<Touch> takeWhile(bool test(Touch value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<Touch> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<Touch> skipWhile(bool test(Touch value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  Touch firstWhere(bool test(Touch value), { Touch orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  Touch lastWhere(bool test(Touch value), {Touch orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  Touch singleWhere(bool test(Touch value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  Touch elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<Touch>:
-
-  void add(Touch value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<Touch> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<Touch>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<Touch> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(Touch a, Touch b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(Touch element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(Touch element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  Touch get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  Touch get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  Touch get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, Touch element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<Touch> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<Touch> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Touch removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  Touch removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(Touch element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(Touch element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<Touch> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<Touch> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [Touch fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<Touch> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<Touch> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <Touch>[]);
-  }
-
-  Map<int, Touch> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<Touch> mixins.
 
   @DomName('TouchList.item')
@@ -23231,7 +21410,7 @@
 
   static String _createObjectURL_3(blob_OR_source_OR_stream) native "DOMURL__createObjectURL_3_Callback";
 
-  @DomName('DOMURL.revokeObjectURL')
+  @DomName('URL.revokeObjectURL')
   @DocsEditable
   static void revokeObjectUrl(String url) native "DOMURL_revokeObjectURL_Callback";
 
@@ -23787,73 +21966,73 @@
   static bool get supportsPointConversions => _DomPoint.supported;
   Window.internal() : super.internal();
 
-  @DomName('DOMWindow.DOMContentLoadedEvent')
+  @DomName('Window.DOMContentLoadedEvent')
   @DocsEditable
   static const EventStreamProvider<Event> contentLoadedEvent = const EventStreamProvider<Event>('DOMContentLoaded');
 
-  @DomName('DOMWindow.devicemotionEvent')
+  @DomName('Window.devicemotionEvent')
   @DocsEditable
   static const EventStreamProvider<DeviceMotionEvent> deviceMotionEvent = const EventStreamProvider<DeviceMotionEvent>('devicemotion');
 
-  @DomName('DOMWindow.deviceorientationEvent')
+  @DomName('Window.deviceorientationEvent')
   @DocsEditable
   static const EventStreamProvider<DeviceOrientationEvent> deviceOrientationEvent = const EventStreamProvider<DeviceOrientationEvent>('deviceorientation');
 
-  @DomName('DOMWindow.hashchangeEvent')
+  @DomName('Window.hashchangeEvent')
   @DocsEditable
   static const EventStreamProvider<Event> hashChangeEvent = const EventStreamProvider<Event>('hashchange');
 
-  @DomName('DOMWindow.messageEvent')
+  @DomName('Window.messageEvent')
   @DocsEditable
   static const EventStreamProvider<MessageEvent> messageEvent = const EventStreamProvider<MessageEvent>('message');
 
-  @DomName('DOMWindow.offlineEvent')
+  @DomName('Window.offlineEvent')
   @DocsEditable
   static const EventStreamProvider<Event> offlineEvent = const EventStreamProvider<Event>('offline');
 
-  @DomName('DOMWindow.onlineEvent')
+  @DomName('Window.onlineEvent')
   @DocsEditable
   static const EventStreamProvider<Event> onlineEvent = const EventStreamProvider<Event>('online');
 
-  @DomName('DOMWindow.pagehideEvent')
+  @DomName('Window.pagehideEvent')
   @DocsEditable
   static const EventStreamProvider<Event> pageHideEvent = const EventStreamProvider<Event>('pagehide');
 
-  @DomName('DOMWindow.pageshowEvent')
+  @DomName('Window.pageshowEvent')
   @DocsEditable
   static const EventStreamProvider<Event> pageShowEvent = const EventStreamProvider<Event>('pageshow');
 
-  @DomName('DOMWindow.popstateEvent')
+  @DomName('Window.popstateEvent')
   @DocsEditable
   static const EventStreamProvider<PopStateEvent> popStateEvent = const EventStreamProvider<PopStateEvent>('popstate');
 
-  @DomName('DOMWindow.resizeEvent')
+  @DomName('Window.resizeEvent')
   @DocsEditable
   static const EventStreamProvider<Event> resizeEvent = const EventStreamProvider<Event>('resize');
 
-  @DomName('DOMWindow.storageEvent')
+  @DomName('Window.storageEvent')
   @DocsEditable
   static const EventStreamProvider<StorageEvent> storageEvent = const EventStreamProvider<StorageEvent>('storage');
 
-  @DomName('DOMWindow.unloadEvent')
+  @DomName('Window.unloadEvent')
   @DocsEditable
   static const EventStreamProvider<Event> unloadEvent = const EventStreamProvider<Event>('unload');
 
-  @DomName('DOMWindow.webkitAnimationEndEvent')
+  @DomName('Window.webkitAnimationEndEvent')
   @DocsEditable
   @SupportedBrowser(SupportedBrowser.CHROME)
   @SupportedBrowser(SupportedBrowser.SAFARI)
   @Experimental
   static const EventStreamProvider<AnimationEvent> animationEndEvent = const EventStreamProvider<AnimationEvent>('webkitAnimationEnd');
 
-  @DomName('DOMWindow.webkitAnimationIterationEvent')
+  @DomName('Window.webkitAnimationIterationEvent')
   @DocsEditable
   @SupportedBrowser(SupportedBrowser.CHROME)
   @SupportedBrowser(SupportedBrowser.SAFARI)
   @Experimental
   static const EventStreamProvider<AnimationEvent> animationIterationEvent = const EventStreamProvider<AnimationEvent>('webkitAnimationIteration');
 
-  @DomName('DOMWindow.webkitAnimationStartEvent')
+  @DomName('Window.webkitAnimationStartEvent')
   @DocsEditable
   @SupportedBrowser(SupportedBrowser.CHROME)
   @SupportedBrowser(SupportedBrowser.SAFARI)
@@ -23864,55 +22043,55 @@
 
   static const int TEMPORARY = 0;
 
-  @DomName('DOMWindow.applicationCache')
+  @DomName('Window.applicationCache')
   @DocsEditable
   ApplicationCache get applicationCache native "DOMWindow_applicationCache_Getter";
 
-  @DomName('DOMWindow.closed')
+  @DomName('Window.closed')
   @DocsEditable
   bool get closed native "DOMWindow_closed_Getter";
 
-  @DomName('DOMWindow.console')
+  @DomName('Window.console')
   @DocsEditable
   Console get console native "DOMWindow_console_Getter";
 
-  @DomName('DOMWindow.crypto')
+  @DomName('Window.crypto')
   @DocsEditable
   Crypto get crypto native "DOMWindow_crypto_Getter";
 
-  @DomName('DOMWindow.defaultStatus')
+  @DomName('Window.defaultStatus')
   @DocsEditable
   String get defaultStatus native "DOMWindow_defaultStatus_Getter";
 
-  @DomName('DOMWindow.defaultStatus')
+  @DomName('Window.defaultStatus')
   @DocsEditable
   void set defaultStatus(String value) native "DOMWindow_defaultStatus_Setter";
 
-  @DomName('DOMWindow.defaultstatus')
+  @DomName('Window.defaultstatus')
   @DocsEditable
   String get defaultstatus native "DOMWindow_defaultstatus_Getter";
 
-  @DomName('DOMWindow.defaultstatus')
+  @DomName('Window.defaultstatus')
   @DocsEditable
   void set defaultstatus(String value) native "DOMWindow_defaultstatus_Setter";
 
-  @DomName('DOMWindow.devicePixelRatio')
+  @DomName('Window.devicePixelRatio')
   @DocsEditable
   num get devicePixelRatio native "DOMWindow_devicePixelRatio_Getter";
 
-  @DomName('DOMWindow.document')
+  @DomName('Window.document')
   @DocsEditable
   Document get document native "DOMWindow_document_Getter";
 
-  @DomName('DOMWindow.event')
+  @DomName('Window.event')
   @DocsEditable
   Event get event native "DOMWindow_event_Getter";
 
-  @DomName('DOMWindow.history')
+  @DomName('Window.history')
   @DocsEditable
   History get history native "DOMWindow_history_Getter";
 
-  @DomName('DOMWindow.indexedDB')
+  @DomName('Window.indexedDB')
   @DocsEditable
   @SupportedBrowser(SupportedBrowser.CHROME)
   @SupportedBrowser(SupportedBrowser.FIREFOX, '15')
@@ -23920,325 +22099,325 @@
   @Experimental
   IdbFactory get indexedDB native "DOMWindow_indexedDB_Getter";
 
-  @DomName('DOMWindow.innerHeight')
+  @DomName('Window.innerHeight')
   @DocsEditable
   int get innerHeight native "DOMWindow_innerHeight_Getter";
 
-  @DomName('DOMWindow.innerWidth')
+  @DomName('Window.innerWidth')
   @DocsEditable
   int get innerWidth native "DOMWindow_innerWidth_Getter";
 
-  @DomName('DOMWindow.localStorage')
+  @DomName('Window.localStorage')
   @DocsEditable
   Storage get localStorage native "DOMWindow_localStorage_Getter";
 
-  @DomName('DOMWindow.location')
+  @DomName('Window.location')
   @DocsEditable
   Location get location native "DOMWindow_location_Getter";
 
-  @DomName('DOMWindow.location')
+  @DomName('Window.location')
   @DocsEditable
   void set location(Location value) native "DOMWindow_location_Setter";
 
-  @DomName('DOMWindow.locationbar')
+  @DomName('Window.locationbar')
   @DocsEditable
   BarInfo get locationbar native "DOMWindow_locationbar_Getter";
 
-  @DomName('DOMWindow.menubar')
+  @DomName('Window.menubar')
   @DocsEditable
   BarInfo get menubar native "DOMWindow_menubar_Getter";
 
-  @DomName('DOMWindow.name')
+  @DomName('Window.name')
   @DocsEditable
   String get name native "DOMWindow_name_Getter";
 
-  @DomName('DOMWindow.name')
+  @DomName('Window.name')
   @DocsEditable
   void set name(String value) native "DOMWindow_name_Setter";
 
-  @DomName('DOMWindow.navigator')
+  @DomName('Window.navigator')
   @DocsEditable
   Navigator get navigator native "DOMWindow_navigator_Getter";
 
-  @DomName('DOMWindow.offscreenBuffering')
+  @DomName('Window.offscreenBuffering')
   @DocsEditable
   bool get offscreenBuffering native "DOMWindow_offscreenBuffering_Getter";
 
-  @DomName('DOMWindow.opener')
+  @DomName('Window.opener')
   @DocsEditable
   WindowBase get opener native "DOMWindow_opener_Getter";
 
-  @DomName('DOMWindow.outerHeight')
+  @DomName('Window.outerHeight')
   @DocsEditable
   int get outerHeight native "DOMWindow_outerHeight_Getter";
 
-  @DomName('DOMWindow.outerWidth')
+  @DomName('Window.outerWidth')
   @DocsEditable
   int get outerWidth native "DOMWindow_outerWidth_Getter";
 
-  @DomName('DOMWindow.pageXOffset')
+  @DomName('Window.pageXOffset')
   @DocsEditable
   int get pageXOffset native "DOMWindow_pageXOffset_Getter";
 
-  @DomName('DOMWindow.pageYOffset')
+  @DomName('Window.pageYOffset')
   @DocsEditable
   int get pageYOffset native "DOMWindow_pageYOffset_Getter";
 
-  @DomName('DOMWindow.parent')
+  @DomName('Window.parent')
   @DocsEditable
   WindowBase get parent native "DOMWindow_parent_Getter";
 
-  @DomName('DOMWindow.performance')
+  @DomName('Window.performance')
   @DocsEditable
   @SupportedBrowser(SupportedBrowser.CHROME)
   @SupportedBrowser(SupportedBrowser.FIREFOX)
   @SupportedBrowser(SupportedBrowser.IE)
   Performance get performance native "DOMWindow_performance_Getter";
 
-  @DomName('DOMWindow.personalbar')
+  @DomName('Window.personalbar')
   @DocsEditable
   BarInfo get personalbar native "DOMWindow_personalbar_Getter";
 
-  @DomName('DOMWindow.screen')
+  @DomName('Window.screen')
   @DocsEditable
   Screen get screen native "DOMWindow_screen_Getter";
 
-  @DomName('DOMWindow.screenLeft')
+  @DomName('Window.screenLeft')
   @DocsEditable
   int get screenLeft native "DOMWindow_screenLeft_Getter";
 
-  @DomName('DOMWindow.screenTop')
+  @DomName('Window.screenTop')
   @DocsEditable
   int get screenTop native "DOMWindow_screenTop_Getter";
 
-  @DomName('DOMWindow.screenX')
+  @DomName('Window.screenX')
   @DocsEditable
   int get screenX native "DOMWindow_screenX_Getter";
 
-  @DomName('DOMWindow.screenY')
+  @DomName('Window.screenY')
   @DocsEditable
   int get screenY native "DOMWindow_screenY_Getter";
 
-  @DomName('DOMWindow.scrollX')
+  @DomName('Window.scrollX')
   @DocsEditable
   int get scrollX native "DOMWindow_scrollX_Getter";
 
-  @DomName('DOMWindow.scrollY')
+  @DomName('Window.scrollY')
   @DocsEditable
   int get scrollY native "DOMWindow_scrollY_Getter";
 
-  @DomName('DOMWindow.scrollbars')
+  @DomName('Window.scrollbars')
   @DocsEditable
   BarInfo get scrollbars native "DOMWindow_scrollbars_Getter";
 
-  @DomName('DOMWindow.self')
+  @DomName('Window.self')
   @DocsEditable
   WindowBase get self native "DOMWindow_self_Getter";
 
-  @DomName('DOMWindow.sessionStorage')
+  @DomName('Window.sessionStorage')
   @DocsEditable
   Storage get sessionStorage native "DOMWindow_sessionStorage_Getter";
 
-  @DomName('DOMWindow.status')
+  @DomName('Window.status')
   @DocsEditable
   String get status native "DOMWindow_status_Getter";
 
-  @DomName('DOMWindow.status')
+  @DomName('Window.status')
   @DocsEditable
   void set status(String value) native "DOMWindow_status_Setter";
 
-  @DomName('DOMWindow.statusbar')
+  @DomName('Window.statusbar')
   @DocsEditable
   BarInfo get statusbar native "DOMWindow_statusbar_Getter";
 
-  @DomName('DOMWindow.styleMedia')
+  @DomName('Window.styleMedia')
   @DocsEditable
   StyleMedia get styleMedia native "DOMWindow_styleMedia_Getter";
 
-  @DomName('DOMWindow.toolbar')
+  @DomName('Window.toolbar')
   @DocsEditable
   BarInfo get toolbar native "DOMWindow_toolbar_Getter";
 
-  @DomName('DOMWindow.top')
+  @DomName('Window.top')
   @DocsEditable
   WindowBase get top native "DOMWindow_top_Getter";
 
-  @DomName('DOMWindow.webkitNotifications')
+  @DomName('Window.webkitNotifications')
   @DocsEditable
   @SupportedBrowser(SupportedBrowser.CHROME)
   @SupportedBrowser(SupportedBrowser.SAFARI)
   @Experimental
   NotificationCenter get notifications native "DOMWindow_webkitNotifications_Getter";
 
-  @DomName('DOMWindow.webkitStorageInfo')
+  @DomName('Window.webkitStorageInfo')
   @DocsEditable
   @SupportedBrowser(SupportedBrowser.CHROME)
   @SupportedBrowser(SupportedBrowser.SAFARI)
   @Experimental
   StorageInfo get storageInfo native "DOMWindow_webkitStorageInfo_Getter";
 
-  @DomName('DOMWindow.window')
+  @DomName('Window.window')
   @DocsEditable
   WindowBase get window native "DOMWindow_window_Getter";
 
-  @DomName('DOMWindow.addEventListener')
+  @DomName('Window.addEventListener')
   @DocsEditable
   void $dom_addEventListener(String type, EventListener listener, [bool useCapture]) native "DOMWindow_addEventListener_Callback";
 
-  @DomName('DOMWindow.alert')
+  @DomName('Window.alert')
   @DocsEditable
   void alert(String message) native "DOMWindow_alert_Callback";
 
-  @DomName('DOMWindow.atob')
+  @DomName('Window.atob')
   @DocsEditable
   String atob(String string) native "DOMWindow_atob_Callback";
 
-  @DomName('DOMWindow.btoa')
+  @DomName('Window.btoa')
   @DocsEditable
   String btoa(String string) native "DOMWindow_btoa_Callback";
 
-  @DomName('DOMWindow.cancelAnimationFrame')
+  @DomName('Window.cancelAnimationFrame')
   @DocsEditable
   void cancelAnimationFrame(int id) native "DOMWindow_cancelAnimationFrame_Callback";
 
-  @DomName('DOMWindow.captureEvents')
+  @DomName('Window.captureEvents')
   @DocsEditable
   void captureEvents() native "DOMWindow_captureEvents_Callback";
 
-  @DomName('DOMWindow.clearInterval')
+  @DomName('Window.clearInterval')
   @DocsEditable
   void _clearInterval(int handle) native "DOMWindow_clearInterval_Callback";
 
-  @DomName('DOMWindow.clearTimeout')
+  @DomName('Window.clearTimeout')
   @DocsEditable
   void _clearTimeout(int handle) native "DOMWindow_clearTimeout_Callback";
 
-  @DomName('DOMWindow.close')
+  @DomName('Window.close')
   @DocsEditable
   void close() native "DOMWindow_close_Callback";
 
-  @DomName('DOMWindow.confirm')
+  @DomName('Window.confirm')
   @DocsEditable
   bool confirm(String message) native "DOMWindow_confirm_Callback";
 
-  @DomName('DOMWindow.dispatchEvent')
+  @DomName('Window.dispatchEvent')
   @DocsEditable
   bool dispatchEvent(Event evt) native "DOMWindow_dispatchEvent_Callback";
 
-  @DomName('DOMWindow.find')
+  @DomName('Window.find')
   @DocsEditable
   bool find(String string, bool caseSensitive, bool backwards, bool wrap, bool wholeWord, bool searchInFrames, bool showDialog) native "DOMWindow_find_Callback";
 
-  @DomName('DOMWindow.getComputedStyle')
+  @DomName('Window.getComputedStyle')
   @DocsEditable
   CssStyleDeclaration $dom_getComputedStyle(Element element, String pseudoElement) native "DOMWindow_getComputedStyle_Callback";
 
-  @DomName('DOMWindow.getMatchedCSSRules')
+  @DomName('Window.getMatchedCSSRules')
   @DocsEditable
   List<CssRule> getMatchedCssRules(Element element, String pseudoElement) native "DOMWindow_getMatchedCSSRules_Callback";
 
-  @DomName('DOMWindow.getSelection')
+  @DomName('Window.getSelection')
   @DocsEditable
   Selection getSelection() native "DOMWindow_getSelection_Callback";
 
-  @DomName('DOMWindow.matchMedia')
+  @DomName('Window.matchMedia')
   @DocsEditable
   MediaQueryList matchMedia(String query) native "DOMWindow_matchMedia_Callback";
 
-  @DomName('DOMWindow.moveBy')
+  @DomName('Window.moveBy')
   @DocsEditable
   void moveBy(num x, num y) native "DOMWindow_moveBy_Callback";
 
-  @DomName('DOMWindow.moveTo')
+  @DomName('Window.moveTo')
   @DocsEditable
   void moveTo(num x, num y) native "DOMWindow_moveTo_Callback";
 
-  @DomName('DOMWindow.open')
+  @DomName('Window.open')
   @DocsEditable
   WindowBase open(String url, String name, [String options]) native "DOMWindow_open_Callback";
 
-  @DomName('DOMWindow.openDatabase')
+  @DomName('Window.openDatabase')
   @DocsEditable
   @SupportedBrowser(SupportedBrowser.CHROME)
   @SupportedBrowser(SupportedBrowser.SAFARI)
   @Experimental
   SqlDatabase openDatabase(String name, String version, String displayName, int estimatedSize, [DatabaseCallback creationCallback]) native "DOMWindow_openDatabase_Callback";
 
-  @DomName('DOMWindow.postMessage')
+  @DomName('Window.postMessage')
   @DocsEditable
   void postMessage(/*SerializedScriptValue*/ message, String targetOrigin, [List messagePorts]) native "DOMWindow_postMessage_Callback";
 
-  @DomName('DOMWindow.print')
+  @DomName('Window.print')
   @DocsEditable
   void print() native "DOMWindow_print_Callback";
 
-  @DomName('DOMWindow.releaseEvents')
+  @DomName('Window.releaseEvents')
   @DocsEditable
   void releaseEvents() native "DOMWindow_releaseEvents_Callback";
 
-  @DomName('DOMWindow.removeEventListener')
+  @DomName('Window.removeEventListener')
   @DocsEditable
   void $dom_removeEventListener(String type, EventListener listener, [bool useCapture]) native "DOMWindow_removeEventListener_Callback";
 
-  @DomName('DOMWindow.requestAnimationFrame')
+  @DomName('Window.requestAnimationFrame')
   @DocsEditable
   int requestAnimationFrame(RequestAnimationFrameCallback callback) native "DOMWindow_requestAnimationFrame_Callback";
 
-  @DomName('DOMWindow.resizeBy')
+  @DomName('Window.resizeBy')
   @DocsEditable
   void resizeBy(num x, num y) native "DOMWindow_resizeBy_Callback";
 
-  @DomName('DOMWindow.resizeTo')
+  @DomName('Window.resizeTo')
   @DocsEditable
   void resizeTo(num width, num height) native "DOMWindow_resizeTo_Callback";
 
-  @DomName('DOMWindow.scroll')
+  @DomName('Window.scroll')
   @DocsEditable
   void scroll(int x, int y) native "DOMWindow_scroll_Callback";
 
-  @DomName('DOMWindow.scrollBy')
+  @DomName('Window.scrollBy')
   @DocsEditable
   void scrollBy(int x, int y) native "DOMWindow_scrollBy_Callback";
 
-  @DomName('DOMWindow.scrollTo')
+  @DomName('Window.scrollTo')
   @DocsEditable
   void scrollTo(int x, int y) native "DOMWindow_scrollTo_Callback";
 
-  @DomName('DOMWindow.setInterval')
+  @DomName('Window.setInterval')
   @DocsEditable
   int _setInterval(Object handler, int timeout) native "DOMWindow_setInterval_Callback";
 
-  @DomName('DOMWindow.setTimeout')
+  @DomName('Window.setTimeout')
   @DocsEditable
   int _setTimeout(Object handler, int timeout) native "DOMWindow_setTimeout_Callback";
 
-  @DomName('DOMWindow.showModalDialog')
+  @DomName('Window.showModalDialog')
   @DocsEditable
   Object showModalDialog(String url, [Object dialogArgs, String featureArgs]) native "DOMWindow_showModalDialog_Callback";
 
-  @DomName('DOMWindow.stop')
+  @DomName('Window.stop')
   @DocsEditable
   void stop() native "DOMWindow_stop_Callback";
 
-  @DomName('DOMWindow.toString')
+  @DomName('Window.toString')
   @DocsEditable
   String toString() native "DOMWindow_toString_Callback";
 
-  @DomName('DOMWindow.webkitConvertPointFromNodeToPage')
+  @DomName('Window.webkitConvertPointFromNodeToPage')
   @DocsEditable
   @SupportedBrowser(SupportedBrowser.CHROME)
   @SupportedBrowser(SupportedBrowser.SAFARI)
   @Experimental
   _DomPoint _convertPointFromNodeToPage(Node node, _DomPoint p) native "DOMWindow_webkitConvertPointFromNodeToPage_Callback";
 
-  @DomName('DOMWindow.webkitConvertPointFromPageToNode')
+  @DomName('Window.webkitConvertPointFromPageToNode')
   @DocsEditable
   @SupportedBrowser(SupportedBrowser.CHROME)
   @SupportedBrowser(SupportedBrowser.SAFARI)
   @Experimental
   _DomPoint _convertPointFromPageToNode(Node node, _DomPoint p) native "DOMWindow_webkitConvertPointFromPageToNode_Callback";
 
-  @DomName('DOMWindow.webkitRequestFileSystem')
+  @DomName('Window.webkitRequestFileSystem')
   @DocsEditable
   @SupportedBrowser(SupportedBrowser.CHROME)
   @Experimental
@@ -24252,7 +22431,7 @@
     return completer.future;
   }
 
-  @DomName('DOMWindow.webkitResolveLocalFileSystemURL')
+  @DomName('Window.webkitResolveLocalFileSystemURL')
   @DocsEditable
   @SupportedBrowser(SupportedBrowser.CHROME)
   @Experimental
@@ -24266,215 +22445,215 @@
     return completer.future;
   }
 
-  @DomName('DOMWindow.onDOMContentLoaded')
+  @DomName('Window.onDOMContentLoaded')
   @DocsEditable
   Stream<Event> get onContentLoaded => contentLoadedEvent.forTarget(this);
 
-  @DomName('DOMWindow.onabort')
+  @DomName('Window.onabort')
   @DocsEditable
   Stream<Event> get onAbort => Element.abortEvent.forTarget(this);
 
-  @DomName('DOMWindow.onblur')
+  @DomName('Window.onblur')
   @DocsEditable
   Stream<Event> get onBlur => Element.blurEvent.forTarget(this);
 
-  @DomName('DOMWindow.onchange')
+  @DomName('Window.onchange')
   @DocsEditable
   Stream<Event> get onChange => Element.changeEvent.forTarget(this);
 
-  @DomName('DOMWindow.onclick')
+  @DomName('Window.onclick')
   @DocsEditable
   Stream<MouseEvent> get onClick => Element.clickEvent.forTarget(this);
 
-  @DomName('DOMWindow.oncontextmenu')
+  @DomName('Window.oncontextmenu')
   @DocsEditable
   Stream<MouseEvent> get onContextMenu => Element.contextMenuEvent.forTarget(this);
 
-  @DomName('DOMWindow.ondblclick')
+  @DomName('Window.ondblclick')
   @DocsEditable
   Stream<Event> get onDoubleClick => Element.doubleClickEvent.forTarget(this);
 
-  @DomName('DOMWindow.ondevicemotion')
+  @DomName('Window.ondevicemotion')
   @DocsEditable
   Stream<DeviceMotionEvent> get onDeviceMotion => deviceMotionEvent.forTarget(this);
 
-  @DomName('DOMWindow.ondeviceorientation')
+  @DomName('Window.ondeviceorientation')
   @DocsEditable
   Stream<DeviceOrientationEvent> get onDeviceOrientation => deviceOrientationEvent.forTarget(this);
 
-  @DomName('DOMWindow.ondrag')
+  @DomName('Window.ondrag')
   @DocsEditable
   Stream<MouseEvent> get onDrag => Element.dragEvent.forTarget(this);
 
-  @DomName('DOMWindow.ondragend')
+  @DomName('Window.ondragend')
   @DocsEditable
   Stream<MouseEvent> get onDragEnd => Element.dragEndEvent.forTarget(this);
 
-  @DomName('DOMWindow.ondragenter')
+  @DomName('Window.ondragenter')
   @DocsEditable
   Stream<MouseEvent> get onDragEnter => Element.dragEnterEvent.forTarget(this);
 
-  @DomName('DOMWindow.ondragleave')
+  @DomName('Window.ondragleave')
   @DocsEditable
   Stream<MouseEvent> get onDragLeave => Element.dragLeaveEvent.forTarget(this);
 
-  @DomName('DOMWindow.ondragover')
+  @DomName('Window.ondragover')
   @DocsEditable
   Stream<MouseEvent> get onDragOver => Element.dragOverEvent.forTarget(this);
 
-  @DomName('DOMWindow.ondragstart')
+  @DomName('Window.ondragstart')
   @DocsEditable
   Stream<MouseEvent> get onDragStart => Element.dragStartEvent.forTarget(this);
 
-  @DomName('DOMWindow.ondrop')
+  @DomName('Window.ondrop')
   @DocsEditable
   Stream<MouseEvent> get onDrop => Element.dropEvent.forTarget(this);
 
-  @DomName('DOMWindow.onerror')
+  @DomName('Window.onerror')
   @DocsEditable
   Stream<Event> get onError => Element.errorEvent.forTarget(this);
 
-  @DomName('DOMWindow.onfocus')
+  @DomName('Window.onfocus')
   @DocsEditable
   Stream<Event> get onFocus => Element.focusEvent.forTarget(this);
 
-  @DomName('DOMWindow.onhashchange')
+  @DomName('Window.onhashchange')
   @DocsEditable
   Stream<Event> get onHashChange => hashChangeEvent.forTarget(this);
 
-  @DomName('DOMWindow.oninput')
+  @DomName('Window.oninput')
   @DocsEditable
   Stream<Event> get onInput => Element.inputEvent.forTarget(this);
 
-  @DomName('DOMWindow.oninvalid')
+  @DomName('Window.oninvalid')
   @DocsEditable
   Stream<Event> get onInvalid => Element.invalidEvent.forTarget(this);
 
-  @DomName('DOMWindow.onkeydown')
+  @DomName('Window.onkeydown')
   @DocsEditable
   Stream<KeyboardEvent> get onKeyDown => Element.keyDownEvent.forTarget(this);
 
-  @DomName('DOMWindow.onkeypress')
+  @DomName('Window.onkeypress')
   @DocsEditable
   Stream<KeyboardEvent> get onKeyPress => Element.keyPressEvent.forTarget(this);
 
-  @DomName('DOMWindow.onkeyup')
+  @DomName('Window.onkeyup')
   @DocsEditable
   Stream<KeyboardEvent> get onKeyUp => Element.keyUpEvent.forTarget(this);
 
-  @DomName('DOMWindow.onload')
+  @DomName('Window.onload')
   @DocsEditable
   Stream<Event> get onLoad => Element.loadEvent.forTarget(this);
 
-  @DomName('DOMWindow.onmessage')
+  @DomName('Window.onmessage')
   @DocsEditable
   Stream<MessageEvent> get onMessage => messageEvent.forTarget(this);
 
-  @DomName('DOMWindow.onmousedown')
+  @DomName('Window.onmousedown')
   @DocsEditable
   Stream<MouseEvent> get onMouseDown => Element.mouseDownEvent.forTarget(this);
 
-  @DomName('DOMWindow.onmousemove')
+  @DomName('Window.onmousemove')
   @DocsEditable
   Stream<MouseEvent> get onMouseMove => Element.mouseMoveEvent.forTarget(this);
 
-  @DomName('DOMWindow.onmouseout')
+  @DomName('Window.onmouseout')
   @DocsEditable
   Stream<MouseEvent> get onMouseOut => Element.mouseOutEvent.forTarget(this);
 
-  @DomName('DOMWindow.onmouseover')
+  @DomName('Window.onmouseover')
   @DocsEditable
   Stream<MouseEvent> get onMouseOver => Element.mouseOverEvent.forTarget(this);
 
-  @DomName('DOMWindow.onmouseup')
+  @DomName('Window.onmouseup')
   @DocsEditable
   Stream<MouseEvent> get onMouseUp => Element.mouseUpEvent.forTarget(this);
 
-  @DomName('DOMWindow.onmousewheel')
+  @DomName('Window.onmousewheel')
   @DocsEditable
   Stream<WheelEvent> get onMouseWheel => Element.mouseWheelEvent.forTarget(this);
 
-  @DomName('DOMWindow.onoffline')
+  @DomName('Window.onoffline')
   @DocsEditable
   Stream<Event> get onOffline => offlineEvent.forTarget(this);
 
-  @DomName('DOMWindow.ononline')
+  @DomName('Window.ononline')
   @DocsEditable
   Stream<Event> get onOnline => onlineEvent.forTarget(this);
 
-  @DomName('DOMWindow.onpagehide')
+  @DomName('Window.onpagehide')
   @DocsEditable
   Stream<Event> get onPageHide => pageHideEvent.forTarget(this);
 
-  @DomName('DOMWindow.onpageshow')
+  @DomName('Window.onpageshow')
   @DocsEditable
   Stream<Event> get onPageShow => pageShowEvent.forTarget(this);
 
-  @DomName('DOMWindow.onpopstate')
+  @DomName('Window.onpopstate')
   @DocsEditable
   Stream<PopStateEvent> get onPopState => popStateEvent.forTarget(this);
 
-  @DomName('DOMWindow.onreset')
+  @DomName('Window.onreset')
   @DocsEditable
   Stream<Event> get onReset => Element.resetEvent.forTarget(this);
 
-  @DomName('DOMWindow.onresize')
+  @DomName('Window.onresize')
   @DocsEditable
   Stream<Event> get onResize => resizeEvent.forTarget(this);
 
-  @DomName('DOMWindow.onscroll')
+  @DomName('Window.onscroll')
   @DocsEditable
   Stream<Event> get onScroll => Element.scrollEvent.forTarget(this);
 
-  @DomName('DOMWindow.onsearch')
+  @DomName('Window.onsearch')
   @DocsEditable
   Stream<Event> get onSearch => Element.searchEvent.forTarget(this);
 
-  @DomName('DOMWindow.onselect')
+  @DomName('Window.onselect')
   @DocsEditable
   Stream<Event> get onSelect => Element.selectEvent.forTarget(this);
 
-  @DomName('DOMWindow.onstorage')
+  @DomName('Window.onstorage')
   @DocsEditable
   Stream<StorageEvent> get onStorage => storageEvent.forTarget(this);
 
-  @DomName('DOMWindow.onsubmit')
+  @DomName('Window.onsubmit')
   @DocsEditable
   Stream<Event> get onSubmit => Element.submitEvent.forTarget(this);
 
-  @DomName('DOMWindow.ontouchcancel')
+  @DomName('Window.ontouchcancel')
   @DocsEditable
   Stream<TouchEvent> get onTouchCancel => Element.touchCancelEvent.forTarget(this);
 
-  @DomName('DOMWindow.ontouchend')
+  @DomName('Window.ontouchend')
   @DocsEditable
   Stream<TouchEvent> get onTouchEnd => Element.touchEndEvent.forTarget(this);
 
-  @DomName('DOMWindow.ontouchmove')
+  @DomName('Window.ontouchmove')
   @DocsEditable
   Stream<TouchEvent> get onTouchMove => Element.touchMoveEvent.forTarget(this);
 
-  @DomName('DOMWindow.ontouchstart')
+  @DomName('Window.ontouchstart')
   @DocsEditable
   Stream<TouchEvent> get onTouchStart => Element.touchStartEvent.forTarget(this);
 
-  @DomName('DOMWindow.onunload')
+  @DomName('Window.onunload')
   @DocsEditable
   Stream<Event> get onUnload => unloadEvent.forTarget(this);
 
-  @DomName('DOMWindow.onwebkitAnimationEnd')
+  @DomName('Window.onwebkitAnimationEnd')
   @DocsEditable
   Stream<AnimationEvent> get onAnimationEnd => animationEndEvent.forTarget(this);
 
-  @DomName('DOMWindow.onwebkitAnimationIteration')
+  @DomName('Window.onwebkitAnimationIteration')
   @DocsEditable
   Stream<AnimationEvent> get onAnimationIteration => animationIterationEvent.forTarget(this);
 
-  @DomName('DOMWindow.onwebkitAnimationStart')
+  @DomName('Window.onwebkitAnimationStart')
   @DocsEditable
   Stream<AnimationEvent> get onAnimationStart => animationStartEvent.forTarget(this);
 
-  @DomName('DOMWindow.onwebkitTransitionEnd')
+  @DomName('Window.onwebkitTransitionEnd')
   @DocsEditable
   Stream<TransitionEvent> get onTransitionEnd => Element.transitionEndEvent.forTarget(this);
 
@@ -25000,7 +23179,7 @@
 
 @DocsEditable
 @DomName('ClientRectList')
-class _ClientRectList extends NativeFieldWrapperClass1 implements List<Rect> {
+class _ClientRectList extends NativeFieldWrapperClass1 with ListMixin<Rect>, ImmutableListMixin<Rect> implements List<Rect> {
   _ClientRectList.internal();
 
   @DomName('ClientRectList.length')
@@ -25015,196 +23194,11 @@
   // -- start List<Rect> mixins.
   // Rect is the element type.
 
-  // From Iterable<Rect>:
 
-  Iterator<Rect> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<Rect>(this);
-  }
-
-  Rect reduce(Rect combine(Rect value, Rect element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, Rect element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(Rect element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(Rect element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(Rect element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<Rect> where(bool f(Rect element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(Rect element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(Rect element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(Rect element)) => IterableMixinWorkaround.any(this, f);
-
-  List<Rect> toList({ bool growable: true }) =>
-      new List<Rect>.from(this, growable: growable);
-
-  Set<Rect> toSet() => new Set<Rect>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<Rect> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<Rect> takeWhile(bool test(Rect value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<Rect> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<Rect> skipWhile(bool test(Rect value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  Rect firstWhere(bool test(Rect value), { Rect orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  Rect lastWhere(bool test(Rect value), {Rect orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  Rect singleWhere(bool test(Rect value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  Rect elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<Rect>:
-
-  void add(Rect value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<Rect> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<Rect>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<Rect> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(Rect a, Rect b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(Rect element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(Rect element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  Rect get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  Rect get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  Rect get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, Rect element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<Rect> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<Rect> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Rect removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  Rect removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(Rect element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(Rect element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<Rect> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<Rect> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [Rect fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<Rect> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<Rect> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <Rect>[]);
-  }
-
-  Map<int, Rect> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<Rect> mixins.
 
   @DomName('ClientRectList.item')
@@ -25234,7 +23228,7 @@
 
 @DocsEditable
 @DomName('CSSRuleList')
-class _CssRuleList extends NativeFieldWrapperClass1 implements List<CssRule> {
+class _CssRuleList extends NativeFieldWrapperClass1 with ListMixin<CssRule>, ImmutableListMixin<CssRule> implements List<CssRule> {
   _CssRuleList.internal();
 
   @DomName('CSSRuleList.length')
@@ -25249,196 +23243,11 @@
   // -- start List<CssRule> mixins.
   // CssRule is the element type.
 
-  // From Iterable<CssRule>:
 
-  Iterator<CssRule> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<CssRule>(this);
-  }
-
-  CssRule reduce(CssRule combine(CssRule value, CssRule element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, CssRule element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(CssRule element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(CssRule element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(CssRule element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<CssRule> where(bool f(CssRule element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(CssRule element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(CssRule element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(CssRule element)) => IterableMixinWorkaround.any(this, f);
-
-  List<CssRule> toList({ bool growable: true }) =>
-      new List<CssRule>.from(this, growable: growable);
-
-  Set<CssRule> toSet() => new Set<CssRule>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<CssRule> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<CssRule> takeWhile(bool test(CssRule value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<CssRule> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<CssRule> skipWhile(bool test(CssRule value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  CssRule firstWhere(bool test(CssRule value), { CssRule orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  CssRule lastWhere(bool test(CssRule value), {CssRule orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  CssRule singleWhere(bool test(CssRule value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  CssRule elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<CssRule>:
-
-  void add(CssRule value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<CssRule> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<CssRule>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<CssRule> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(CssRule a, CssRule b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(CssRule element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(CssRule element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  CssRule get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  CssRule get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  CssRule get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, CssRule element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<CssRule> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<CssRule> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  CssRule removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  CssRule removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(CssRule element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(CssRule element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<CssRule> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<CssRule> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [CssRule fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<CssRule> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<CssRule> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <CssRule>[]);
-  }
-
-  Map<int, CssRule> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<CssRule> mixins.
 
   @DomName('CSSRuleList.item')
@@ -25455,7 +23264,7 @@
 
 @DocsEditable
 @DomName('CSSValueList')
-class _CssValueList extends _CSSValue implements List<_CSSValue> {
+class _CssValueList extends _CSSValue with ListMixin<_CSSValue>, ImmutableListMixin<_CSSValue> implements List<_CSSValue> {
   _CssValueList.internal() : super.internal();
 
   @DomName('CSSValueList.length')
@@ -25470,196 +23279,11 @@
   // -- start List<_CSSValue> mixins.
   // _CSSValue is the element type.
 
-  // From Iterable<_CSSValue>:
 
-  Iterator<_CSSValue> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<_CSSValue>(this);
-  }
-
-  _CSSValue reduce(_CSSValue combine(_CSSValue value, _CSSValue element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, _CSSValue element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(_CSSValue element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(_CSSValue element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(_CSSValue element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<_CSSValue> where(bool f(_CSSValue element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(_CSSValue element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(_CSSValue element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(_CSSValue element)) => IterableMixinWorkaround.any(this, f);
-
-  List<_CSSValue> toList({ bool growable: true }) =>
-      new List<_CSSValue>.from(this, growable: growable);
-
-  Set<_CSSValue> toSet() => new Set<_CSSValue>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<_CSSValue> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<_CSSValue> takeWhile(bool test(_CSSValue value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<_CSSValue> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<_CSSValue> skipWhile(bool test(_CSSValue value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  _CSSValue firstWhere(bool test(_CSSValue value), { _CSSValue orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  _CSSValue lastWhere(bool test(_CSSValue value), {_CSSValue orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  _CSSValue singleWhere(bool test(_CSSValue value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  _CSSValue elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<_CSSValue>:
-
-  void add(_CSSValue value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<_CSSValue> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<_CSSValue>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<_CSSValue> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(_CSSValue a, _CSSValue b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(_CSSValue element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(_CSSValue element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  _CSSValue get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  _CSSValue get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  _CSSValue get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, _CSSValue element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<_CSSValue> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<_CSSValue> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  _CSSValue removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  _CSSValue removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(_CSSValue element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(_CSSValue element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<_CSSValue> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<_CSSValue> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [_CSSValue fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<_CSSValue> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<_CSSValue> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <_CSSValue>[]);
-  }
-
-  Map<int, _CSSValue> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<_CSSValue> mixins.
 
   @DomName('CSSValueList.item')
@@ -25749,9 +23373,6 @@
 @SupportedBrowser(SupportedBrowser.CHROME)
 @SupportedBrowser(SupportedBrowser.SAFARI)
 @Experimental
-@SupportedBrowser(SupportedBrowser.CHROME)
-@SupportedBrowser(SupportedBrowser.SAFARI)
-@Experimental
 class _DomPoint extends NativeFieldWrapperClass1 {
   _DomPoint.internal();
   factory _DomPoint(num x, num y) => _create(x, y);
@@ -25762,19 +23383,19 @@
   /// Checks if this type is supported on the current platform.
   static bool get supported => true;
 
-  @DomName('DOMPoint.x')
+  @DomName('WebKitPoint.x')
   @DocsEditable
   num get x native "DOMPoint_x_Getter";
 
-  @DomName('DOMPoint.x')
+  @DomName('WebKitPoint.x')
   @DocsEditable
   void set x(num value) native "DOMPoint_x_Setter";
 
-  @DomName('DOMPoint.y')
+  @DomName('WebKitPoint.y')
   @DocsEditable
   num get y native "DOMPoint_y_Getter";
 
-  @DomName('DOMPoint.y')
+  @DomName('WebKitPoint.y')
   @DocsEditable
   void set y(num value) native "DOMPoint_y_Setter";
 
@@ -25944,7 +23565,7 @@
 
 @DocsEditable
 @DomName('EntryArray')
-class _EntryArray extends NativeFieldWrapperClass1 implements List<Entry> {
+class _EntryArray extends NativeFieldWrapperClass1 with ListMixin<Entry>, ImmutableListMixin<Entry> implements List<Entry> {
   _EntryArray.internal();
 
   @DomName('EntryArray.length')
@@ -25959,196 +23580,11 @@
   // -- start List<Entry> mixins.
   // Entry is the element type.
 
-  // From Iterable<Entry>:
 
-  Iterator<Entry> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<Entry>(this);
-  }
-
-  Entry reduce(Entry combine(Entry value, Entry element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, Entry element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(Entry element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(Entry element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(Entry element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<Entry> where(bool f(Entry element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(Entry element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(Entry element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(Entry element)) => IterableMixinWorkaround.any(this, f);
-
-  List<Entry> toList({ bool growable: true }) =>
-      new List<Entry>.from(this, growable: growable);
-
-  Set<Entry> toSet() => new Set<Entry>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<Entry> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<Entry> takeWhile(bool test(Entry value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<Entry> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<Entry> skipWhile(bool test(Entry value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  Entry firstWhere(bool test(Entry value), { Entry orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  Entry lastWhere(bool test(Entry value), {Entry orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  Entry singleWhere(bool test(Entry value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  Entry elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<Entry>:
-
-  void add(Entry value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<Entry> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<Entry>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<Entry> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(Entry a, Entry b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(Entry element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(Entry element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  Entry get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  Entry get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  Entry get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, Entry element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<Entry> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<Entry> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Entry removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  Entry removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(Entry element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(Entry element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<Entry> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<Entry> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [Entry fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<Entry> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<Entry> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <Entry>[]);
-  }
-
-  Map<int, Entry> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<Entry> mixins.
 
   @DomName('EntryArray.item')
@@ -26165,7 +23601,7 @@
 
 @DocsEditable
 @DomName('EntryArraySync')
-class _EntryArraySync extends NativeFieldWrapperClass1 implements List<_EntrySync> {
+class _EntryArraySync extends NativeFieldWrapperClass1 with ListMixin<_EntrySync>, ImmutableListMixin<_EntrySync> implements List<_EntrySync> {
   _EntryArraySync.internal();
 
   @DomName('EntryArraySync.length')
@@ -26180,196 +23616,11 @@
   // -- start List<_EntrySync> mixins.
   // _EntrySync is the element type.
 
-  // From Iterable<_EntrySync>:
 
-  Iterator<_EntrySync> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<_EntrySync>(this);
-  }
-
-  _EntrySync reduce(_EntrySync combine(_EntrySync value, _EntrySync element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, _EntrySync element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(_EntrySync element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(_EntrySync element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(_EntrySync element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<_EntrySync> where(bool f(_EntrySync element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(_EntrySync element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(_EntrySync element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(_EntrySync element)) => IterableMixinWorkaround.any(this, f);
-
-  List<_EntrySync> toList({ bool growable: true }) =>
-      new List<_EntrySync>.from(this, growable: growable);
-
-  Set<_EntrySync> toSet() => new Set<_EntrySync>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<_EntrySync> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<_EntrySync> takeWhile(bool test(_EntrySync value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<_EntrySync> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<_EntrySync> skipWhile(bool test(_EntrySync value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  _EntrySync firstWhere(bool test(_EntrySync value), { _EntrySync orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  _EntrySync lastWhere(bool test(_EntrySync value), {_EntrySync orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  _EntrySync singleWhere(bool test(_EntrySync value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  _EntrySync elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<_EntrySync>:
-
-  void add(_EntrySync value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<_EntrySync> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<_EntrySync>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<_EntrySync> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(_EntrySync a, _EntrySync b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(_EntrySync element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(_EntrySync element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  _EntrySync get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  _EntrySync get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  _EntrySync get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, _EntrySync element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<_EntrySync> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<_EntrySync> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  _EntrySync removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  _EntrySync removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(_EntrySync element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(_EntrySync element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<_EntrySync> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<_EntrySync> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [_EntrySync fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<_EntrySync> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<_EntrySync> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <_EntrySync>[]);
-  }
-
-  Map<int, _EntrySync> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<_EntrySync> mixins.
 
   @DomName('EntryArraySync.item')
@@ -26447,7 +23698,7 @@
 
 @DocsEditable
 @DomName('GamepadList')
-class _GamepadList extends NativeFieldWrapperClass1 implements List<Gamepad> {
+class _GamepadList extends NativeFieldWrapperClass1 with ListMixin<Gamepad>, ImmutableListMixin<Gamepad> implements List<Gamepad> {
   _GamepadList.internal();
 
   @DomName('GamepadList.length')
@@ -26462,196 +23713,11 @@
   // -- start List<Gamepad> mixins.
   // Gamepad is the element type.
 
-  // From Iterable<Gamepad>:
 
-  Iterator<Gamepad> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<Gamepad>(this);
-  }
-
-  Gamepad reduce(Gamepad combine(Gamepad value, Gamepad element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, Gamepad element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(Gamepad element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(Gamepad element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(Gamepad element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<Gamepad> where(bool f(Gamepad element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(Gamepad element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(Gamepad element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(Gamepad element)) => IterableMixinWorkaround.any(this, f);
-
-  List<Gamepad> toList({ bool growable: true }) =>
-      new List<Gamepad>.from(this, growable: growable);
-
-  Set<Gamepad> toSet() => new Set<Gamepad>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<Gamepad> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<Gamepad> takeWhile(bool test(Gamepad value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<Gamepad> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<Gamepad> skipWhile(bool test(Gamepad value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  Gamepad firstWhere(bool test(Gamepad value), { Gamepad orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  Gamepad lastWhere(bool test(Gamepad value), {Gamepad orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  Gamepad singleWhere(bool test(Gamepad value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  Gamepad elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<Gamepad>:
-
-  void add(Gamepad value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<Gamepad> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<Gamepad>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<Gamepad> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(Gamepad a, Gamepad b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(Gamepad element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(Gamepad element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  Gamepad get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  Gamepad get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  Gamepad get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, Gamepad element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<Gamepad> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<Gamepad> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Gamepad removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  Gamepad removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(Gamepad element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(Gamepad element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<Gamepad> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<Gamepad> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [Gamepad fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<Gamepad> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<Gamepad> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <Gamepad>[]);
-  }
-
-  Map<int, Gamepad> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<Gamepad> mixins.
 
   @DomName('GamepadList.item')
@@ -26759,7 +23825,7 @@
 
 @DocsEditable
 @DomName('NamedNodeMap')
-class _NamedNodeMap extends NativeFieldWrapperClass1 implements List<Node> {
+class _NamedNodeMap extends NativeFieldWrapperClass1 with ListMixin<Node>, ImmutableListMixin<Node> implements List<Node> {
   _NamedNodeMap.internal();
 
   @DomName('NamedNodeMap.length')
@@ -26774,196 +23840,11 @@
   // -- start List<Node> mixins.
   // Node is the element type.
 
-  // From Iterable<Node>:
 
-  Iterator<Node> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<Node>(this);
-  }
-
-  Node reduce(Node combine(Node value, Node element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, Node element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(Node element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(Node element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(Node element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<Node> where(bool f(Node element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(Node element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(Node element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(Node element)) => IterableMixinWorkaround.any(this, f);
-
-  List<Node> toList({ bool growable: true }) =>
-      new List<Node>.from(this, growable: growable);
-
-  Set<Node> toSet() => new Set<Node>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<Node> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<Node> takeWhile(bool test(Node value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<Node> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<Node> skipWhile(bool test(Node value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  Node firstWhere(bool test(Node value), { Node orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  Node lastWhere(bool test(Node value), {Node orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  Node singleWhere(bool test(Node value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  Node elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<Node>:
-
-  void add(Node value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<Node>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<Node> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(Node a, Node b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(Node element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(Node element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  Node get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  Node get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  Node get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, Node element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Node removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  Node removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(Node element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(Node element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<Node> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<Node> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [Node fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<Node> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<Node> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <Node>[]);
-  }
-
-  Map<int, Node> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<Node> mixins.
 
   @DomName('NamedNodeMap.getNamedItem')
@@ -27088,7 +23969,7 @@
 
 @DocsEditable
 @DomName('SpeechInputResultList')
-class _SpeechInputResultList extends NativeFieldWrapperClass1 implements List<SpeechInputResult> {
+class _SpeechInputResultList extends NativeFieldWrapperClass1 with ListMixin<SpeechInputResult>, ImmutableListMixin<SpeechInputResult> implements List<SpeechInputResult> {
   _SpeechInputResultList.internal();
 
   @DomName('SpeechInputResultList.length')
@@ -27103,196 +23984,11 @@
   // -- start List<SpeechInputResult> mixins.
   // SpeechInputResult is the element type.
 
-  // From Iterable<SpeechInputResult>:
 
-  Iterator<SpeechInputResult> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<SpeechInputResult>(this);
-  }
-
-  SpeechInputResult reduce(SpeechInputResult combine(SpeechInputResult value, SpeechInputResult element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, SpeechInputResult element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(SpeechInputResult element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(SpeechInputResult element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(SpeechInputResult element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<SpeechInputResult> where(bool f(SpeechInputResult element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(SpeechInputResult element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(SpeechInputResult element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(SpeechInputResult element)) => IterableMixinWorkaround.any(this, f);
-
-  List<SpeechInputResult> toList({ bool growable: true }) =>
-      new List<SpeechInputResult>.from(this, growable: growable);
-
-  Set<SpeechInputResult> toSet() => new Set<SpeechInputResult>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<SpeechInputResult> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<SpeechInputResult> takeWhile(bool test(SpeechInputResult value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<SpeechInputResult> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<SpeechInputResult> skipWhile(bool test(SpeechInputResult value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  SpeechInputResult firstWhere(bool test(SpeechInputResult value), { SpeechInputResult orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  SpeechInputResult lastWhere(bool test(SpeechInputResult value), {SpeechInputResult orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  SpeechInputResult singleWhere(bool test(SpeechInputResult value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  SpeechInputResult elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<SpeechInputResult>:
-
-  void add(SpeechInputResult value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<SpeechInputResult> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<SpeechInputResult>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<SpeechInputResult> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(SpeechInputResult a, SpeechInputResult b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(SpeechInputResult element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(SpeechInputResult element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  SpeechInputResult get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  SpeechInputResult get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  SpeechInputResult get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, SpeechInputResult element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<SpeechInputResult> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<SpeechInputResult> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  SpeechInputResult removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  SpeechInputResult removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(SpeechInputResult element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(SpeechInputResult element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<SpeechInputResult> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<SpeechInputResult> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [SpeechInputResult fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<SpeechInputResult> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<SpeechInputResult> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <SpeechInputResult>[]);
-  }
-
-  Map<int, SpeechInputResult> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<SpeechInputResult> mixins.
 
   @DomName('SpeechInputResultList.item')
@@ -27309,7 +24005,7 @@
 
 @DocsEditable
 @DomName('SpeechRecognitionResultList')
-class _SpeechRecognitionResultList extends NativeFieldWrapperClass1 implements List<SpeechRecognitionResult> {
+class _SpeechRecognitionResultList extends NativeFieldWrapperClass1 with ListMixin<SpeechRecognitionResult>, ImmutableListMixin<SpeechRecognitionResult> implements List<SpeechRecognitionResult> {
   _SpeechRecognitionResultList.internal();
 
   @DomName('SpeechRecognitionResultList.length')
@@ -27324,196 +24020,11 @@
   // -- start List<SpeechRecognitionResult> mixins.
   // SpeechRecognitionResult is the element type.
 
-  // From Iterable<SpeechRecognitionResult>:
 
-  Iterator<SpeechRecognitionResult> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<SpeechRecognitionResult>(this);
-  }
-
-  SpeechRecognitionResult reduce(SpeechRecognitionResult combine(SpeechRecognitionResult value, SpeechRecognitionResult element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, SpeechRecognitionResult element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(SpeechRecognitionResult element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(SpeechRecognitionResult element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(SpeechRecognitionResult element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<SpeechRecognitionResult> where(bool f(SpeechRecognitionResult element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(SpeechRecognitionResult element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(SpeechRecognitionResult element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(SpeechRecognitionResult element)) => IterableMixinWorkaround.any(this, f);
-
-  List<SpeechRecognitionResult> toList({ bool growable: true }) =>
-      new List<SpeechRecognitionResult>.from(this, growable: growable);
-
-  Set<SpeechRecognitionResult> toSet() => new Set<SpeechRecognitionResult>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<SpeechRecognitionResult> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<SpeechRecognitionResult> takeWhile(bool test(SpeechRecognitionResult value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<SpeechRecognitionResult> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<SpeechRecognitionResult> skipWhile(bool test(SpeechRecognitionResult value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  SpeechRecognitionResult firstWhere(bool test(SpeechRecognitionResult value), { SpeechRecognitionResult orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  SpeechRecognitionResult lastWhere(bool test(SpeechRecognitionResult value), {SpeechRecognitionResult orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  SpeechRecognitionResult singleWhere(bool test(SpeechRecognitionResult value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  SpeechRecognitionResult elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<SpeechRecognitionResult>:
-
-  void add(SpeechRecognitionResult value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<SpeechRecognitionResult> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<SpeechRecognitionResult>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<SpeechRecognitionResult> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(SpeechRecognitionResult a, SpeechRecognitionResult b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(SpeechRecognitionResult element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(SpeechRecognitionResult element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  SpeechRecognitionResult get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  SpeechRecognitionResult get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  SpeechRecognitionResult get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, SpeechRecognitionResult element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<SpeechRecognitionResult> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<SpeechRecognitionResult> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  SpeechRecognitionResult removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  SpeechRecognitionResult removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(SpeechRecognitionResult element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(SpeechRecognitionResult element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<SpeechRecognitionResult> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<SpeechRecognitionResult> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [SpeechRecognitionResult fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<SpeechRecognitionResult> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<SpeechRecognitionResult> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <SpeechRecognitionResult>[]);
-  }
-
-  Map<int, SpeechRecognitionResult> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<SpeechRecognitionResult> mixins.
 
   @DomName('SpeechRecognitionResultList.item')
@@ -27530,7 +24041,7 @@
 
 @DocsEditable
 @DomName('StyleSheetList')
-class _StyleSheetList extends NativeFieldWrapperClass1 implements List<StyleSheet> {
+class _StyleSheetList extends NativeFieldWrapperClass1 with ListMixin<StyleSheet>, ImmutableListMixin<StyleSheet> implements List<StyleSheet> {
   _StyleSheetList.internal();
 
   @DomName('StyleSheetList.length')
@@ -27545,196 +24056,11 @@
   // -- start List<StyleSheet> mixins.
   // StyleSheet is the element type.
 
-  // From Iterable<StyleSheet>:
 
-  Iterator<StyleSheet> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<StyleSheet>(this);
-  }
-
-  StyleSheet reduce(StyleSheet combine(StyleSheet value, StyleSheet element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, StyleSheet element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(StyleSheet element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(StyleSheet element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(StyleSheet element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<StyleSheet> where(bool f(StyleSheet element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(StyleSheet element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(StyleSheet element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(StyleSheet element)) => IterableMixinWorkaround.any(this, f);
-
-  List<StyleSheet> toList({ bool growable: true }) =>
-      new List<StyleSheet>.from(this, growable: growable);
-
-  Set<StyleSheet> toSet() => new Set<StyleSheet>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<StyleSheet> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<StyleSheet> takeWhile(bool test(StyleSheet value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<StyleSheet> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<StyleSheet> skipWhile(bool test(StyleSheet value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  StyleSheet firstWhere(bool test(StyleSheet value), { StyleSheet orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  StyleSheet lastWhere(bool test(StyleSheet value), {StyleSheet orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  StyleSheet singleWhere(bool test(StyleSheet value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  StyleSheet elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<StyleSheet>:
-
-  void add(StyleSheet value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<StyleSheet> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<StyleSheet>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<StyleSheet> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(StyleSheet a, StyleSheet b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(StyleSheet element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(StyleSheet element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  StyleSheet get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  StyleSheet get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  StyleSheet get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, StyleSheet element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<StyleSheet> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<StyleSheet> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  StyleSheet removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  StyleSheet removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(StyleSheet element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(StyleSheet element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<StyleSheet> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<StyleSheet> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [StyleSheet fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<StyleSheet> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<StyleSheet> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <StyleSheet>[]);
-  }
-
-  Map<int, StyleSheet> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<StyleSheet> mixins.
 
   @DomName('StyleSheetList.item')
@@ -28558,6 +24884,82 @@
 // BSD-style license that can be found in the LICENSE file.
 
 
+abstract class ImmutableListMixin<E> implements List<E> {
+  // From Iterable<$E>:
+  Iterator<E> get iterator {
+    // Note: NodeLists are not fixed size. And most probably length shouldn't
+    // be cached in both iterator _and_ forEach method. For now caching it
+    // for consistency.
+    return new FixedSizeListIterator<E>(this);
+  }
+
+  // From Collection<E>:
+  void add(E value) {
+    throw new UnsupportedError("Cannot add to immutable List.");
+  }
+
+  void addAll(Iterable<E> iterable) {
+    throw new UnsupportedError("Cannot add to immutable List.");
+  }
+
+  // From List<E>:
+  void sort([int compare(E a, E b)]) {
+    throw new UnsupportedError("Cannot sort immutable List.");
+  }
+
+  void insert(int index, E element) {
+    throw new UnsupportedError("Cannot add to immutable List.");
+  }
+
+  void insertAll(int index, Iterable<E> iterable) {
+    throw new UnsupportedError("Cannot add to immutable List.");
+  }
+
+  void setAll(int index, Iterable<E> iterable) {
+    throw new UnsupportedError("Cannot modify an immutable List.");
+  }
+
+  E removeAt(int pos) {
+    throw new UnsupportedError("Cannot remove from immutable List.");
+  }
+
+  E removeLast() {
+    throw new UnsupportedError("Cannot remove from immutable List.");
+  }
+
+  void remove(Object object) {
+    throw new UnsupportedError("Cannot remove from immutable List.");
+  }
+
+  void removeWhere(bool test(E element)) {
+    throw new UnsupportedError("Cannot remove from immutable List.");
+  }
+
+  void retainWhere(bool test(E element)) {
+    throw new UnsupportedError("Cannot remove from immutable List.");
+  }
+
+  void setRange(int start, int end, Iterable<E> iterable, [int skipCount]) {
+    throw new UnsupportedError("Cannot setRange on immutable List.");
+  }
+
+  void removeRange(int start, int end) {
+    throw new UnsupportedError("Cannot removeRange on immutable List.");
+  }
+
+  void replaceRange(int start, int end, Iterable<E> iterable) {
+    throw new UnsupportedError("Cannot modify an immutable List.");
+  }
+
+  void fillRange(int start, int end, [E fillValue]) {
+    throw new UnsupportedError("Cannot modify an immutable List.");
+  }
+}
+// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+
 /**
  * Internal class that does the actual calculations to determine keyCode and
  * charCode for keydown, keypress, and keyup events for all browsers.
@@ -29728,100 +26130,286 @@
 // BSD-style license that can be found in the LICENSE file.
 
 
-class _ModelTreeObserver {
-  static bool _initialized = false;
+// This code is inspired by ChangeSummary:
+// https://github.com/rafaelw/ChangeSummary/blob/master/change_summary.js
+// ...which underlies MDV. Since we don't need the functionality of
+// ChangeSummary, we just implement what we need for data bindings.
+// This allows our implementation to be much simpler.
+
+// TODO(jmesserly): should we make these types stronger, and require
+// Observable objects? Currently, it is fine to say something like:
+//     var path = new PathObserver(123, '');
+//     print(path.value); // "123"
+//
+// Furthermore this degenerate case is allowed:
+//     var path = new PathObserver(123, 'foo.bar.baz.qux');
+//     print(path.value); // "null"
+//
+// Here we see that any invalid (i.e. not Observable) value will break the
+// path chain without producing an error or exception.
+//
+// Now the real question: should we do this? For the former case, the behavior
+// is correct but we could chose to handle it in the dart:html bindings layer.
+// For the latter case, it might be better to throw an error so users can find
+// the problem.
+
+
+/**
+ * A data-bound path starting from a view-model or model object, for example
+ * `foo.bar.baz`.
+ *
+ * When the [values] stream is being listened to, this will observe changes to
+ * the object and any intermediate object along the path, and send [values]
+ * accordingly. When all listeners are unregistered it will stop observing
+ * the objects.
+ *
+ * This class is used to implement [Node.bind] and similar functionality.
+ */
+// TODO(jmesserly): find a better home for this type.
+@Experimental
+class PathObserver {
+  /** The object being observed. */
+  final object;
+
+  /** The path string. */
+  final String path;
+
+  /** True if the path is valid, otherwise false. */
+  final bool _isValid;
+
+  // TODO(jmesserly): same issue here as ObservableMixin: is there an easier
+  // way to get a broadcast stream?
+  StreamController _values;
+  Stream _valueStream;
+
+  _PropertyObserver _observer, _lastObserver;
+
+  Object _lastValue;
+  bool _scheduled = false;
 
   /**
-   * Start an observer watching the document for tree changes to automatically
-   * propagate model changes.
-   *
-   * Currently this does not support propagation through Shadow DOMs.
+   * Observes [path] on [object] for changes. This returns an object that can be
+   * used to get the changes and get/set the value at this path.
+   * See [PathObserver.values] and [PathObserver.value].
    */
-  static void initialize() {
-    if (!_initialized) {
-      _initialized = true;
+  PathObserver(this.object, String path)
+    : path = path, _isValid = _isPathValid(path) {
 
-      if (MutationObserver.supported) {
-        var observer = new MutationObserver(_processTreeChange);
-        observer.observe(document, childList: true, subtree: true);
-      } else {
-        document.on['DOMNodeInserted'].listen(_handleNodeInserted);
-        document.on['DOMNodeRemoved'].listen(_handleNodeRemoved);
+    // TODO(jmesserly): if the path is empty, or the object is! Observable, we
+    // can optimize the PathObserver to be more lightweight.
+
+    _values = new StreamController(onListen: _observe, onCancel: _unobserve);
+
+    if (_isValid) {
+      var segments = [];
+      for (var segment in path.trim().split('.')) {
+        if (segment == '') continue;
+        var index = int.parse(segment, onError: (_) {});
+        segments.add(index != null ? index : new Symbol(segment));
+      }
+
+      // Create the property observer linked list.
+      // Note that the structure of a path can't change after it is initially
+      // constructed, even though the objects along the path can change.
+      for (int i = segments.length - 1; i >= 0; i--) {
+        _observer = new _PropertyObserver(this, segments[i], _observer);
+        if (_lastObserver == null) _lastObserver = _observer;
       }
     }
   }
 
-  static void _processTreeChange(List<MutationRecord> mutations,
-      MutationObserver observer) {
-    for (var record in mutations) {
-      for (var node in record.addedNodes) {
-        // When nodes enter the document we need to make sure that all of the
-        // models are properly propagated through the entire sub-tree.
-        propagateModel(node, _calculatedModel(node), true);
-      }
-      for (var node in record.removedNodes) {
-        propagateModel(node, _calculatedModel(node), false);
-      }
-    }
-  }
-
-  static void _handleNodeInserted(MutationEvent e) {
-    var node = e.target;
-    window.setImmediate(() {
-      propagateModel(node, _calculatedModel(node), true);
-    });
-  }
-
-  static void _handleNodeRemoved(MutationEvent e) {
-    var node = e.target;
-    window.setImmediate(() {
-      propagateModel(node, _calculatedModel(node), false);
-    });
-  }
-
+  // TODO(jmesserly): we could try adding the first value to the stream, but
+  // that delivers the first record async.
   /**
-   * Figures out what the model should be for a node, avoiding any cached
-   * model values.
+   * Listens to the stream, and invokes the [callback] immediately with the
+   * current [value]. This is useful for bindings, which want to be up-to-date
+   * immediately.
    */
-  static _calculatedModel(node) {
-    if (node._hasLocalModel == true) {
-      return node._model;
-    } else if (node.parentNode != null) {
-      return node.parentNode._model;
-    }
-    return null;
+  StreamSubscription bindSync(void callback(value)) {
+    var result = values.listen(callback);
+    callback(value);
+    return result;
   }
 
+  // TODO(jmesserly): should this be a change record with the old value?
+  // TODO(jmesserly): should this be a broadcast stream? We only need
+  // single-subscription in the bindings system, so single sub saves overhead.
   /**
-   * Pushes model changes down through the tree.
-   *
-   * Set fullTree to true if the state of the tree is unknown and model changes
-   * should be propagated through the entire tree.
+   * Gets the stream of values that were observed at this path.
+   * This returns a single-subscription stream.
    */
-  static void propagateModel(Node node, model, bool fullTree) {
-    // Calling into user code with the != call could generate exceptions.
-    // Catch and report them a global exceptions.
-    try {
-      if (node._hasLocalModel != true && node._model != model &&
-          node._modelChangedStreams != null &&
-          !node._modelChangedStreams.isEmpty) {
-        node._model = model;
-        node._modelChangedStreams.toList()
-          .forEach((controller) => controller.add(node));
-      }
-    } catch (e, s) {
-      new Future.error(e, s);
+  Stream get values => _values.stream;
+
+  /** Force synchronous delivery of [values]. */
+  void _deliverValues() {
+    _scheduled = false;
+
+    var newValue = value;
+    if (!identical(_lastValue, newValue)) {
+      _values.add(newValue);
+      _lastValue = newValue;
     }
-    for (var child = node.$dom_firstChild; child != null;
-        child = child.nextNode) {
-      if (child._hasLocalModel != true) {
-        propagateModel(child, model, fullTree);
-      } else if (fullTree) {
-        propagateModel(child, child._model, true);
+  }
+
+  void _observe() {
+    if (_observer != null) {
+      _lastValue = value;
+      _observer.observe();
+    }
+  }
+
+  void _unobserve() {
+    if (_observer != null) _observer.unobserve();
+  }
+
+  void _notifyChange() {
+    if (_scheduled) return;
+    _scheduled = true;
+
+    // TODO(jmesserly): should we have a guarenteed order with respect to other
+    // paths? If so, we could implement this fairly easily by sorting instances
+    // of this class by birth order before delivery.
+    queueChangeRecords(_deliverValues);
+  }
+
+  /** Gets the last reported value at this path. */
+  get value {
+    if (!_isValid) return null;
+    if (_observer == null) return object;
+    _observer.ensureValue(object);
+    return _lastObserver.value;
+  }
+
+  /** Sets the value at this path. */
+  void set value(Object value) {
+    // TODO(jmesserly): throw if property cannot be set?
+    // MDV seems tolerant of these error.
+    if (_observer == null || !_isValid) return;
+    _observer.ensureValue(object);
+    var last = _lastObserver;
+    if (_setObjectProperty(last._object, last._property, value)) {
+      // Technically, this would get updated asynchronously via a change record.
+      // However, it is nice if calling the getter will yield the same value
+      // that was just set. So we use this opportunity to update our cache.
+      last.value = value;
+    }
+  }
+}
+
+// TODO(jmesserly): these should go away in favor of mirrors!
+_getObjectProperty(object, property) {
+  if (object is List && property is int) {
+    if (property >= 0 && property < object.length) {
+      return object[property];
+    } else {
+      return null;
+    }
+  }
+
+  // TODO(jmesserly): what about length?
+  if (object is Map) return object[property];
+
+  if (object is Observable) return object.getValueWorkaround(property);
+
+  return null;
+}
+
+bool _setObjectProperty(object, property, value) {
+  if (object is List && property is int) {
+    object[property] = value;
+  } else if (object is Map) {
+    object[property] = value;
+  } else if (object is Observable) {
+    (object as Observable).setValueWorkaround(property, value);
+  } else {
+    return false;
+  }
+  return true;
+}
+
+
+class _PropertyObserver {
+  final PathObserver _path;
+  final _property;
+  final _PropertyObserver _next;
+
+  // TODO(jmesserly): would be nice not to store both of these.
+  Object _object;
+  Object _value;
+  StreamSubscription _sub;
+
+  _PropertyObserver(this._path, this._property, this._next);
+
+  get value => _value;
+
+  void set value(Object newValue) {
+    _value = newValue;
+    if (_next != null) {
+      if (_sub != null) _next.unobserve();
+      _next.ensureValue(_value);
+      if (_sub != null) _next.observe();
+    }
+  }
+
+  void ensureValue(object) {
+    // If we're observing, values should be up to date already.
+    if (_sub != null) return;
+
+    _object = object;
+    value = _getObjectProperty(object, _property);
+  }
+
+  void observe() {
+    if (_object is Observable) {
+      assert(_sub == null);
+      _sub = (_object as Observable).changes.listen(_onChange);
+    }
+    if (_next != null) _next.observe();
+  }
+
+  void unobserve() {
+    if (_sub == null) return;
+
+    _sub.cancel();
+    _sub = null;
+    if (_next != null) _next.unobserve();
+  }
+
+  void _onChange(List<ChangeRecord> changes) {
+    for (var change in changes) {
+      // TODO(jmesserly): what to do about "new Symbol" here?
+      // Ideally this would only preserve names if the user has opted in to
+      // them being preserved.
+      // TODO(jmesserly): should we drop observable maps with String keys?
+      // If so then we only need one check here.
+      if (change.changes(_property)) {
+        value = _getObjectProperty(_object, _property);
+        _path._notifyChange();
+        return;
       }
     }
   }
 }
+
+// From: https://github.com/rafaelw/ChangeSummary/blob/master/change_summary.js
+
+const _pathIndentPart = r'[$a-z0-9_]+[$a-z0-9_\d]*';
+final _pathRegExp = new RegExp('^'
+    '(?:#?' + _pathIndentPart + ')?'
+    '(?:'
+      '(?:\\.' + _pathIndentPart + ')'
+    ')*'
+    r'$', caseSensitive: false);
+
+final _spacesRegExp = new RegExp(r'\s');
+
+bool _isPathValid(String s) {
+  s = s.replaceAll(_spacesRegExp, '');
+
+  if (s == '') return true;
+  if (s[0] == '.') return false;
+  return _pathRegExp.hasMatch(s);
+}
 // Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
@@ -30049,6 +26637,781 @@
 // BSD-style license that can be found in the LICENSE file.
 
 
+// This code is a port of Model-Driven-Views:
+// https://github.com/toolkitchen/mdv
+// The code mostly comes from src/template_element.js
+
+typedef void _ChangeHandler(value);
+
+/**
+ * Model-Driven Views (MDV)'s native features enables a wide-range of use cases,
+ * but (by design) don't attempt to implement a wide array of specialized
+ * behaviors.
+ *
+ * Enabling these features in MDV is a matter of implementing and registering an
+ * MDV Custom Syntax. A Custom Syntax is an object which contains one or more
+ * delegation functions which implement specialized behavior. This object is
+ * registered with MDV via [TemplateElement.syntax]:
+ *
+ *
+ * HTML:
+ *     <template bind syntax="MySyntax">
+ *       {{ What!Ever('crazy')->thing^^^I+Want(data) }}
+ *     </template>
+ *
+ * Dart:
+ *     class MySyntax extends CustomBindingSyntax {
+ *       getBinding(model, path, name, node) {
+ *         // The magic happens here!
+ *       }
+ *     }
+ *
+ *     ...
+ *
+ *     TemplateElement.syntax['MySyntax'] = new MySyntax();
+ *
+ * See <https://github.com/toolkitchen/mdv/blob/master/docs/syntax.md> for more
+ * information about Custom Syntax.
+ */
+// TODO(jmesserly): if this is just one method, a function type would make it
+// more Dart-friendly.
+@Experimental
+abstract class CustomBindingSyntax {
+  // TODO(jmesserly): I had to remove type annotations from "name" and "node"
+  // Normally they are String and Node respectively. But sometimes it will pass
+  // (int name, CompoundBinding node). That seems very confusing; we may want
+  // to change this API.
+  getBinding(model, String path, name, node);
+}
+
+/** The callback used in the [CompoundBinding.combinator] field. */
+@Experimental
+typedef Object CompoundBindingCombinator(Map objects);
+
+/** Information about the instantiated template. */
+@Experimental
+class TemplateInstance {
+  // TODO(rafaelw): firstNode & lastNode should be read-synchronous
+  // in cases where script has modified the template instance boundary.
+
+  /** The first node of this template instantiation. */
+  final Node firstNode;
+
+  /**
+   * The last node of this template instantiation.
+   * This could be identical to [firstNode] if the template only expanded to a
+   * single node.
+   */
+  final Node lastNode;
+
+  /** The model used to instantiate the template. */
+  final model;
+
+  TemplateInstance(this.firstNode, this.lastNode, this.model);
+}
+
+/**
+ * Model-Driven Views contains a helper object which is useful for the
+ * implementation of a Custom Syntax.
+ *
+ *     var binding = new CompoundBinding((values) {
+ *       var combinedValue;
+ *       // compute combinedValue based on the current values which are provided
+ *       return combinedValue;
+ *     });
+ *     binding.bind('name1', obj1, path1);
+ *     binding.bind('name2', obj2, path2);
+ *     //...
+ *     binding.bind('nameN', objN, pathN);
+ *
+ * CompoundBinding is an object which knows how to listen to multiple path
+ * values (registered via [bind]) and invoke its [combinator] when one or more
+ * of the values have changed and set its [value] property to the return value
+ * of the function. When any value has changed, all current values are provided
+ * to the [combinator] in the single `values` argument.
+ *
+ * See [CustomBindingSyntax] for more information.
+ */
+// TODO(jmesserly): what is the public API surface here? I just guessed;
+// most of it seemed non-public.
+@Experimental
+class CompoundBinding extends ObservableBase {
+  CompoundBindingCombinator _combinator;
+
+  // TODO(jmesserly): ideally these would be String keys, but sometimes we
+  // use integers.
+  Map<dynamic, StreamSubscription> _bindings = new Map();
+  Map _values = new Map();
+  bool _scheduled = false;
+  bool _disposed = false;
+  Object _value;
+
+  CompoundBinding([CompoundBindingCombinator combinator]) {
+    // TODO(jmesserly): this is a tweak to the original code, it seemed to me
+    // that passing the combinator to the constructor should be equivalent to
+    // setting it via the property.
+    // I also added a null check to the combinator setter.
+    this.combinator = combinator;
+  }
+
+  CompoundBindingCombinator get combinator => _combinator;
+
+  set combinator(CompoundBindingCombinator combinator) {
+    _combinator = combinator;
+    if (combinator != null) _scheduleResolve();
+  }
+
+  static const _VALUE = const Symbol('value');
+
+  get value => _value;
+
+  void set value(newValue) {
+    _value = notifyPropertyChange(_VALUE, _value, newValue);
+  }
+
+  // TODO(jmesserly): remove these workarounds when dart2js supports mirrors!
+  getValueWorkaround(key) {
+    if (key == _VALUE) return value;
+    return null;
+  }
+  setValueWorkaround(key, val) {
+    if (key == _VALUE) value = val;
+  }
+
+  void bind(name, model, String path) {
+    unbind(name);
+
+    _bindings[name] = new PathObserver(model, path).bindSync((value) {
+      _values[name] = value;
+      _scheduleResolve();
+    });
+  }
+
+  void unbind(name, {bool suppressResolve: false}) {
+    var binding = _bindings.remove(name);
+    if (binding == null) return;
+
+    binding.cancel();
+    _values.remove(name);
+    if (!suppressResolve) _scheduleResolve();
+  }
+
+  // TODO(rafaelw): Is this the right processing model?
+  // TODO(rafaelw): Consider having a seperate ChangeSummary for
+  // CompoundBindings so to excess dirtyChecks.
+  void _scheduleResolve() {
+    if (_scheduled) return;
+    _scheduled = true;
+    queueChangeRecords(resolve);
+  }
+
+  void resolve() {
+    if (_disposed) return;
+    _scheduled = false;
+
+    if (_combinator == null) {
+      throw new StateError(
+          'CompoundBinding attempted to resolve without a combinator');
+    }
+
+    value = _combinator(_values);
+  }
+
+  void dispose() {
+    for (var binding in _bindings.values) {
+      binding.cancel();
+    }
+    _bindings.clear();
+    _values.clear();
+
+    _disposed = true;
+    value = null;
+  }
+}
+
+Stream<Event> _getStreamForInputType(InputElement element) {
+  switch (element.type) {
+    case 'checkbox':
+      return element.onClick;
+    case 'radio':
+    case 'select-multiple':
+    case 'select-one':
+      return element.onChange;
+    default:
+      return element.onInput;
+  }
+}
+
+abstract class _InputBinding {
+  final InputElement element;
+  PathObserver binding;
+  StreamSubscription _pathSub;
+  StreamSubscription _eventSub;
+
+  _InputBinding(this.element, model, String path) {
+    binding = new PathObserver(model, path);
+    _pathSub = binding.bindSync(valueChanged);
+    _eventSub = _getStreamForInputType(element).listen(updateBinding);
+  }
+
+  void valueChanged(newValue);
+
+  void updateBinding(e);
+
+  void unbind() {
+    binding = null;
+    _pathSub.cancel();
+    _eventSub.cancel();
+  }
+}
+
+class _ValueBinding extends _InputBinding {
+  _ValueBinding(element, model, path) : super(element, model, path);
+
+  void valueChanged(value) {
+    element.value = value == null ? '' : '$value';
+  }
+
+  void updateBinding(e) {
+    binding.value = element.value;
+  }
+}
+
+// TODO(jmesserly): not sure what kind of boolean conversion rules to
+// apply for template data-binding. HTML attributes are true if they're present.
+// However Dart only treats "true" as true. Since this is HTML we'll use
+// something closer to the HTML rules: null (missing) and false are false,
+// everything else is true. See: https://github.com/toolkitchen/mdv/issues/59
+bool _templateBooleanConversion(value) => null != value && false != value;
+
+class _CheckedBinding extends _InputBinding {
+  _CheckedBinding(element, model, path) : super(element, model, path);
+
+  void valueChanged(value) {
+    element.checked = _templateBooleanConversion(value);
+  }
+
+  void updateBinding(e) {
+    binding.value = element.checked;
+
+    // Only the radio button that is getting checked gets an event. We
+    // therefore find all the associated radio buttons and update their
+    // CheckedBinding manually.
+    if (element is InputElement && element.type == 'radio') {
+      for (var r in _getAssociatedRadioButtons(element)) {
+        var checkedBinding = r._checkedBinding;
+        if (checkedBinding != null) {
+          // Set the value directly to avoid an infinite call stack.
+          checkedBinding.binding.value = false;
+        }
+      }
+    }
+  }
+}
+
+// TODO(jmesserly): polyfill document.contains API instead of doing it here
+bool _isNodeInDocument(Node node) {
+  // On non-IE this works:
+  // return node.document.contains(node);
+  var document = node.document;
+  if (node == document || node.parentNode == document) return true;
+  return document.documentElement.contains(node);
+}
+
+// |element| is assumed to be an HTMLInputElement with |type| == 'radio'.
+// Returns an array containing all radio buttons other than |element| that
+// have the same |name|, either in the form that |element| belongs to or,
+// if no form, in the document tree to which |element| belongs.
+//
+// This implementation is based upon the HTML spec definition of a
+// "radio button group":
+//   http://www.whatwg.org/specs/web-apps/current-work/multipage/number-state.html#radio-button-group
+//
+Iterable _getAssociatedRadioButtons(element) {
+  if (!_isNodeInDocument(element)) return [];
+  if (element.form != null) {
+    return element.form.nodes.where((el) {
+      return el != element &&
+          el is InputElement &&
+          el.type == 'radio' &&
+          el.name == element.name;
+    });
+  } else {
+    var radios = element.document.queryAll(
+        'input[type="radio"][name="${element.name}"]');
+    return radios.where((el) => el != element && el.form == null);
+  }
+}
+
+Node _createDeepCloneAndDecorateTemplates(Node node, String syntax) {
+  var clone = node.clone(false); // Shallow clone.
+  if (clone is Element && clone.isTemplate) {
+    TemplateElement.decorate(clone, node);
+    if (syntax != null) {
+      clone.attributes.putIfAbsent('syntax', () => syntax);
+    }
+  }
+
+  for (var c = node.$dom_firstChild; c != null; c = c.nextNode) {
+    clone.append(_createDeepCloneAndDecorateTemplates(c, syntax));
+  }
+  return clone;
+}
+
+// http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html#dfn-template-contents-owner
+Document _getTemplateContentsOwner(Document doc) {
+  if (doc.window == null) {
+    return doc;
+  }
+  var d = doc._templateContentsOwner;
+  if (d == null) {
+    // TODO(arv): This should either be a Document or HTMLDocument depending
+    // on doc.
+    d = doc.implementation.createHtmlDocument('');
+    while (d.$dom_lastChild != null) {
+      d.$dom_lastChild.remove();
+    }
+    doc._templateContentsOwner = d;
+  }
+  return d;
+}
+
+Element _cloneAndSeperateAttributeTemplate(Element templateElement) {
+  var clone = templateElement.clone(false);
+  var attributes = templateElement.attributes;
+  for (var name in attributes.keys.toList()) {
+    switch (name) {
+      case 'template':
+      case 'repeat':
+      case 'bind':
+      case 'ref':
+        clone.attributes.remove(name);
+        break;
+      default:
+        attributes.remove(name);
+        break;
+    }
+  }
+
+  return clone;
+}
+
+void _liftNonNativeTemplateChildrenIntoContent(Element templateElement) {
+  var content = templateElement.content;
+
+  if (!templateElement._isAttributeTemplate) {
+    var child;
+    while ((child = templateElement.$dom_firstChild) != null) {
+      content.append(child);
+    }
+    return;
+  }
+
+  // For attribute templates we copy the whole thing into the content and
+  // we move the non template attributes into the content.
+  //
+  //   <tr foo template>
+  //
+  // becomes
+  //
+  //   <tr template>
+  //   + #document-fragment
+  //     + <tr foo>
+  //
+  var newRoot = _cloneAndSeperateAttributeTemplate(templateElement);
+  var child;
+  while ((child = templateElement.$dom_firstChild) != null) {
+    newRoot.append(child);
+  }
+  content.append(newRoot);
+}
+
+void _bootstrapTemplatesRecursivelyFrom(Node node) {
+  void bootstrap(template) {
+    if (!TemplateElement.decorate(template)) {
+      _bootstrapTemplatesRecursivelyFrom(template.content);
+    }
+  }
+
+  // Need to do this first as the contents may get lifted if |node| is
+  // template.
+  // TODO(jmesserly): node is DocumentFragment or Element
+  var templateDescendents = (node as dynamic).queryAll(_allTemplatesSelectors);
+  if (node is Element && node.isTemplate) bootstrap(node);
+
+  templateDescendents.forEach(bootstrap);
+}
+
+final String _allTemplatesSelectors = 'template, option[template], ' +
+    Element._TABLE_TAGS.keys.map((k) => "$k[template]").join(", ");
+
+void _addBindings(Node node, model, [CustomBindingSyntax syntax]) {
+  if (node is Element) {
+    _addAttributeBindings(node, model, syntax);
+  } else if (node is Text) {
+    _parseAndBind(node, node.text, 'text', model, syntax);
+  }
+
+  for (var c = node.$dom_firstChild; c != null; c = c.nextNode) {
+    _addBindings(c, model, syntax);
+  }
+}
+
+
+void _addAttributeBindings(Element element, model, syntax) {
+  element.attributes.forEach((name, value) {
+    if (value == '' && (name == 'bind' || name == 'repeat')) {
+      value = '{{}}';
+    }
+    _parseAndBind(element, value, name, model, syntax);
+  });
+}
+
+void _parseAndBind(Node node, String text, String name, model,
+    CustomBindingSyntax syntax) {
+
+  var tokens = _parseMustacheTokens(text);
+  if (tokens.length == 0 || (tokens.length == 1 && tokens[0].isText)) {
+    return;
+  }
+
+  if (tokens.length == 1 && tokens[0].isBinding) {
+    _bindOrDelegate(node, name, model, tokens[0].value, syntax);
+    return;
+  }
+
+  var replacementBinding = new CompoundBinding();
+  for (var i = 0; i < tokens.length; i++) {
+    var token = tokens[i];
+    if (token.isBinding) {
+      _bindOrDelegate(replacementBinding, i, model, token.value, syntax);
+    }
+  }
+
+  replacementBinding.combinator = (values) {
+    var newValue = new StringBuffer();
+
+    for (var i = 0; i < tokens.length; i++) {
+      var token = tokens[i];
+      if (token.isText) {
+        newValue.write(token.value);
+      } else {
+        var value = values[i];
+        if (value != null) {
+          newValue.write(value);
+        }
+      }
+    }
+
+    return newValue.toString();
+  };
+
+  node.bind(name, replacementBinding, 'value');
+}
+
+void _bindOrDelegate(node, name, model, String path,
+    CustomBindingSyntax syntax) {
+
+  if (syntax != null) {
+    var delegateBinding = syntax.getBinding(model, path, name, node);
+    if (delegateBinding != null) {
+      model = delegateBinding;
+      path = 'value';
+    }
+  }
+
+  node.bind(name, model, path);
+}
+
+class _BindingToken {
+  final String value;
+  final bool isBinding;
+
+  _BindingToken(this.value, {this.isBinding: false});
+
+  bool get isText => !isBinding;
+}
+
+List<_BindingToken> _parseMustacheTokens(String s) {
+  var result = [];
+  var length = s.length;
+  var index = 0, lastIndex = 0;
+  while (lastIndex < length) {
+    index = s.indexOf('{{', lastIndex);
+    if (index < 0) {
+      result.add(new _BindingToken(s.substring(lastIndex)));
+      break;
+    } else {
+      // There is a non-empty text run before the next path token.
+      if (index > 0 && lastIndex < index) {
+        result.add(new _BindingToken(s.substring(lastIndex, index)));
+      }
+      lastIndex = index + 2;
+      index = s.indexOf('}}', lastIndex);
+      if (index < 0) {
+        var text = s.substring(lastIndex - 2);
+        if (result.length > 0 && result.last.isText) {
+          result.last.value += text;
+        } else {
+          result.add(new _BindingToken(text));
+        }
+        break;
+      }
+
+      var value = s.substring(lastIndex, index).trim();
+      result.add(new _BindingToken(value, isBinding: true));
+      lastIndex = index + 2;
+    }
+  }
+  return result;
+}
+
+void _addTemplateInstanceRecord(fragment, model) {
+  if (fragment.$dom_firstChild == null) {
+    return;
+  }
+
+  var instanceRecord = new TemplateInstance(
+      fragment.$dom_firstChild, fragment.$dom_lastChild, model);
+
+  var node = instanceRecord.firstNode;
+  while (node != null) {
+    node._templateInstance = instanceRecord;
+    node = node.nextNode;
+  }
+}
+
+void _removeAllBindingsRecursively(Node node) {
+  node.unbindAll();
+  for (var c = node.$dom_firstChild; c != null; c = c.nextNode) {
+    _removeAllBindingsRecursively(c);
+  }
+}
+
+void _removeTemplateChild(Node parent, Node child) {
+  child._templateInstance = null;
+  if (child is Element && child.isTemplate) {
+    // Make sure we stop observing when we remove an element.
+    var templateIterator = child._templateIterator;
+    if (templateIterator != null) {
+      templateIterator.abandon();
+      child._templateIterator = null;
+    }
+  }
+  child.remove();
+  _removeAllBindingsRecursively(child);
+}
+
+class _InstanceCursor {
+  final Element _template;
+  Node _terminator;
+  Node _previousTerminator;
+  int _previousIndex = -1;
+  int _index = 0;
+
+  _InstanceCursor(this._template, [index]) {
+    _terminator = _template;
+    if (index != null) {
+      while (index-- > 0) {
+        next();
+      }
+    }
+  }
+
+  void next() {
+    _previousTerminator = _terminator;
+    _previousIndex = _index;
+    _index++;
+
+    while (_index > _terminator._instanceTerminatorCount) {
+      _index -= _terminator._instanceTerminatorCount;
+      _terminator = _terminator.nextNode;
+      if (_terminator is Element && _terminator.tagName == 'TEMPLATE') {
+        _index += _instanceCount(_terminator);
+      }
+    }
+  }
+
+  void abandon() {
+    assert(_instanceCount(_template) > 0);
+    assert(_terminator._instanceTerminatorCount > 0);
+    assert(_index > 0);
+
+    _terminator._instanceTerminatorCount--;
+    _index--;
+  }
+
+  void insert(fragment) {
+    assert(_template.parentNode != null);
+
+    _previousTerminator = _terminator;
+    _previousIndex = _index;
+    _index++;
+
+    _terminator = fragment.$dom_lastChild;
+    if (_terminator == null) _terminator = _previousTerminator;
+    _template.parentNode.insertBefore(fragment, _previousTerminator.nextNode);
+
+    _terminator._instanceTerminatorCount++;
+    if (_terminator != _previousTerminator) {
+      while (_previousTerminator._instanceTerminatorCount >
+              _previousIndex) {
+        _previousTerminator._instanceTerminatorCount--;
+        _terminator._instanceTerminatorCount++;
+      }
+    }
+  }
+
+  void remove() {
+    assert(_previousIndex != -1);
+    assert(_previousTerminator != null &&
+           (_previousIndex > 0 || _previousTerminator == _template));
+    assert(_terminator != null && _index > 0);
+    assert(_template.parentNode != null);
+    assert(_instanceCount(_template) > 0);
+
+    if (_previousTerminator == _terminator) {
+      assert(_index == _previousIndex + 1);
+      _terminator._instanceTerminatorCount--;
+      _terminator = _template;
+      _previousTerminator = null;
+      _previousIndex = -1;
+      return;
+    }
+
+    _terminator._instanceTerminatorCount--;
+
+    var parent = _template.parentNode;
+    while (_previousTerminator.nextNode != _terminator) {
+      _removeTemplateChild(parent, _previousTerminator.nextNode);
+    }
+    _removeTemplateChild(parent, _terminator);
+
+    _terminator = _previousTerminator;
+    _index = _previousIndex;
+    _previousTerminator = null;
+    _previousIndex = -1;  // 0?
+  }
+}
+
+
+class _TemplateIterator {
+  final Element _templateElement;
+  int instanceCount = 0;
+  List iteratedValue;
+  bool observing = false;
+  final CompoundBinding inputs;
+
+  StreamSubscription _sub;
+  StreamSubscription _valueBinding;
+
+  _TemplateIterator(this._templateElement)
+    : inputs = new CompoundBinding(resolveInputs) {
+
+    _valueBinding = new PathObserver(inputs, 'value').bindSync(valueChanged);
+  }
+
+  static Object resolveInputs(Map values) {
+    if (values.containsKey('if') && !_templateBooleanConversion(values['if'])) {
+      return null;
+    }
+
+    if (values.containsKey('repeat')) {
+      return values['repeat'];
+    }
+
+    if (values.containsKey('bind')) {
+      return [values['bind']];
+    }
+
+    return null;
+  }
+
+  void valueChanged(value) {
+    clear();
+    if (value is! List) return;
+
+    iteratedValue = value;
+
+    if (value is Observable) {
+      _sub = value.changes.listen(_handleChanges);
+    }
+
+    int len = iteratedValue.length;
+    if (len > 0) {
+      _handleChanges([new ListChangeRecord(0, addedCount: len)]);
+    }
+  }
+
+  // TODO(jmesserly): port MDV v3.
+  getInstanceModel(model, syntax) => model;
+  getInstanceFragment(syntax) => _templateElement.createInstance();
+
+  void _handleChanges(List<ListChangeRecord> splices) {
+    var syntax = TemplateElement.syntax[_templateElement.attributes['syntax']];
+
+    for (var splice in splices) {
+      if (splice is! ListChangeRecord) continue;
+
+      for (int i = 0; i < splice.removedCount; i++) {
+        var cursor = new _InstanceCursor(_templateElement, splice.index + 1);
+        cursor.remove();
+        instanceCount--;
+      }
+
+      for (var addIndex = splice.index;
+          addIndex < splice.index + splice.addedCount;
+          addIndex++) {
+
+        var model = getInstanceModel(iteratedValue[addIndex], syntax);
+        var fragment = getInstanceFragment(syntax);
+
+        _addBindings(fragment, model, syntax);
+        _addTemplateInstanceRecord(fragment, model);
+
+        var cursor = new _InstanceCursor(_templateElement, addIndex);
+        cursor.insert(fragment);
+        instanceCount++;
+      }
+    }
+  }
+
+  void unobserve() {
+    if (_sub == null) return;
+    _sub.cancel();
+    _sub = null;
+  }
+
+  void clear() {
+    unobserve();
+
+    iteratedValue = null;
+    if (instanceCount == 0) return;
+
+    for (var i = 0; i < instanceCount; i++) {
+      var cursor = new _InstanceCursor(_templateElement, 1);
+      cursor.remove();
+    }
+
+    instanceCount = 0;
+  }
+
+  void abandon() {
+    unobserve();
+    _valueBinding.cancel();
+    inputs.dispose();
+  }
+}
+
+int _instanceCount(Element element) {
+  var templateIterator = element._templateIterator;
+  return templateIterator != null ? templateIterator.instanceCount : 0;
+}
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+
 /**
  * Helper class to implement custom events which wrap DOM events.
  */
@@ -30997,7 +28360,7 @@
 
 
 _makeSendPortFuture(spawnRequest) {
-  final completer = new Completer<SendPort>();
+  final completer = new Completer<SendPort>.sync();
   final port = new ReceivePort();
   port.receive((result, _) {
     completer.complete(result);
diff --git a/sdk/lib/io/http_impl.dart b/sdk/lib/io/http_impl.dart
index 2f5e15a..a0f514d 100644
--- a/sdk/lib/io/http_impl.dart
+++ b/sdk/lib/io/http_impl.dart
@@ -271,24 +271,62 @@
         challenge != null && challenge.length == 1;
   }
 
-  Future<HttpClientResponse> _authenticate() {
-    Future<HttpClientResponse> retryWithCredentials(_Credentials cr) {
-      if (cr != null && cr.scheme != _AuthenticationScheme.UNKNOWN) {
-        // Drain body and retry.
-        return fold(null, (x, y) {}).then((_) {
-            return _httpClient._openUrlFromRequest(_httpRequest.method,
-                                                   _httpRequest.uri,
-                                                   _httpRequest)
-                .then((request) => request.close());
+  Future<HttpClientResponse> _authenticate(bool proxyAuth) {
+    Future<HttpClientResponse> retry() {
+      // Drain body and retry.
+      return fold(null, (x, y) {}).then((_) {
+          return _httpClient._openUrlFromRequest(_httpRequest.method,
+                                                 _httpRequest.uri,
+                                                 _httpRequest)
+              .then((request) => request.close());
           });
-      }
-
-      // Fall through to here to perform normal response handling if
-      // there is no sensible authorization handling.
-      return new Future.value(this);
     }
 
-    List<String> challenge = headers[HttpHeaders.WWW_AUTHENTICATE];
+    List<String> authChallenge() {
+      if (proxyAuth) {
+        return headers[HttpHeaders.PROXY_AUTHENTICATE];
+      } else {
+        return headers[HttpHeaders.WWW_AUTHENTICATE];
+      }
+    }
+
+    _Credentials findCredentials(_AuthenticationScheme scheme) {
+      if (proxyAuth) {
+        return  _httpClient._findProxyCredentials(_httpRequest._proxy, scheme);
+      } else {
+        return _httpClient._findCredentials(_httpRequest.uri, scheme);
+      }
+    }
+
+    void removeCredentials(_Credentials cr) {
+      if (proxyAuth) {
+        _httpClient._removeProxyCredentials(cr);
+      } else {
+        _httpClient._removeCredentials(cr);
+      }
+    }
+
+    Future requestAuthentication(_AuthenticationScheme scheme, String realm) {
+      if (proxyAuth) {
+        if (_httpClient._authenticateProxy == null) {
+          return new Future.value(false);
+        }
+        var proxy = _httpRequest._proxy;
+        return _httpClient._authenticateProxy(proxy.host,
+                                              proxy.port,
+                                              scheme.toString(),
+                                              realm);
+      } else {
+        if (_httpClient._authenticate == null) {
+          return new Future.value(false);
+        }
+        return _httpClient._authenticate(_httpRequest.uri,
+                                         scheme.toString(),
+                                         realm);
+      }
+    }
+
+    List<String> challenge = authChallenge();
     assert(challenge != null || challenge.length == 1);
     _HeaderValue header =
         _HeaderValue.parse(challenge[0], parameterSeparator: ",");
@@ -297,14 +335,14 @@
     String realm = header.parameters["realm"];
 
     // See if any matching credentials are available.
-    _Credentials cr = _httpClient._findCredentials(_httpRequest.uri, scheme);
+    _Credentials cr = findCredentials(scheme);
     if (cr != null) {
       // For basic authentication don't retry already used credentials
       // as they must have already been added to the request causing
       // this authenticate response.
       if (cr.scheme == _AuthenticationScheme.BASIC && !cr.used) {
         // Credentials where found, prepare for retrying the request.
-        return retryWithCredentials(cr);
+        return retry();
       }
 
       // Digest authentication only supports the MD5 algorithm.
@@ -321,13 +359,13 @@
             cr.nonceCount = 0;
           }
           // Credentials where found, prepare for retrying the request.
-          return retryWithCredentials(cr);
+          return retry();
         } else if (header.parameters["stale"] != null &&
                    header.parameters["stale"].toLowerCase() == "true") {
           // If stale is true retry with new nonce.
           cr.nonce = header.parameters["nonce"];
           // Credentials where found, prepare for retrying the request.
-          return retryWithCredentials(cr);
+          return retry();
         }
       }
     }
@@ -336,99 +374,18 @@
     // already been used. If it has already been used it must now be
     // invalid and is removed.
     if (cr != null) {
-      _httpClient._removeCredentials(cr);
+      removeCredentials(cr);
       cr = null;
     }
-    if (_httpClient._authenticate != null) {
-      Future authComplete = _httpClient._authenticate(_httpRequest.uri,
-                                                      scheme.toString(),
-                                                      realm);
-      return authComplete.then((credsAvailable) {
-        if (credsAvailable) {
-          cr = _httpClient._findCredentials(_httpRequest.uri, scheme);
-          return retryWithCredentials(cr);
-        } else {
-          // No credentials available, complete with original response.
-          return this;
-        }
-      });
-    }
-
-    // No credentials were found and the callback was not set.
-    return new Future.value(this);
-  }
-
-  Future<HttpClientResponse> _authenticateProxy() {
-    Future<HttpClientResponse> retryWithProxyCredentials(_ProxyCredentials cr) {
-      return fold(null, (x, y) {}).then((_) {
-          return _httpClient._openUrlFromRequest(_httpRequest.method,
-                                                 _httpRequest.uri,
-                                                 _httpRequest)
-              .then((request) => request.close());
-        });
-    }
-
-    List<String> challenge = headers[HttpHeaders.PROXY_AUTHENTICATE];
-    assert(challenge != null || challenge.length == 1);
-    _HeaderValue header =
-        _HeaderValue.parse(challenge[0], parameterSeparator: ",");
-    _AuthenticationScheme scheme =
-        new _AuthenticationScheme.fromString(header.value);
-    String realm = header.parameters["realm"];
-
-    // See if any credentials are available.
-    var proxy = _httpRequest._proxy;
-
-    var cr =  _httpClient._findProxyCredentials(proxy);
-    if (cr != null) {
-      if (cr.scheme == _AuthenticationScheme.BASIC) {
-        return retryWithProxyCredentials(cr);
+    return requestAuthentication(scheme, realm).then((credsAvailable) {
+      if (credsAvailable) {
+        cr = _httpClient._findCredentials(_httpRequest.uri, scheme);
+        return retry();
+      } else {
+        // No credentials available, complete with original response.
+        return this;
       }
-
-      // Digest authentication only supports the MD5 algorithm.
-      if (cr.scheme == _AuthenticationScheme.DIGEST &&
-          (header.parameters["algorithm"] == null ||
-           header.parameters["algorithm"].toLowerCase() == "md5")) {
-        if (cr.nonce == null || cr.nonce == header.parameters["nonce"]) {
-          // If the nonce is not set then this is the first authenticate
-          // response for these credentials. Set up authentication state.
-          if (cr.nonce == null) {
-            cr.nonce = header.parameters["nonce"];
-            cr.algorithm = "MD5";
-            cr.qop = header.parameters["qop"];
-            cr.nonceCount = 0;
-          }
-          // Credentials where found, prepare for retrying the request.
-          return retryWithProxyCredentials(cr);
-        } else if (header.parameters["stale"] != null &&
-                   header.parameters["stale"].toLowerCase() == "true") {
-          // If stale is true retry with new nonce.
-          cr.nonce = header.parameters["nonce"];
-          // Credentials where found, prepare for retrying the request.
-          return retryWithProxyCredentials(cr);
-        }
-      }
-    }
-
-    // Ask for more credentials if none found.
-    if (_httpClient._authenticateProxy != null) {
-      Future authComplete = _httpClient._authenticateProxy(proxy.host,
-                                                           proxy.port,
-                                                           "basic",
-                                                           realm);
-      return authComplete.then((credsAvailable) {
-        if (credsAvailable) {
-          var cr =  _httpClient._findProxyCredentials(proxy);
-          return retryWithProxyCredentials(cr);
-        } else {
-          // No credentials available, complete with original response.
-          return this;
-        }
-      });
-    }
-
-    // No credentials were found and the callback was not set.
-    return new Future.value(this);
+    });
   }
 }
 
@@ -610,6 +567,7 @@
                                        onCancel: _onListen);
     _outbound._addStream(_controller.stream)
         .then((_) {
+                _onListen();  // Make sure we unsubscribe.
                 _done();
                 _closeCompleter.complete(_outbound);
               },
@@ -923,9 +881,9 @@
                 new RedirectLimitExceededException(response.redirects)));
       }
     } else if (response._shouldAuthenticateProxy) {
-      future = response._authenticateProxy();
+      future = response._authenticate(true);
     } else if (response._shouldAuthenticate) {
-      future = response._authenticate();
+      future = response._authenticate(false);
     } else {
       future = new Future<HttpClientResponse>.value(response);
     }
@@ -1588,11 +1546,12 @@
     return cr;
   }
 
-  _ProxyCredentials _findProxyCredentials(_Proxy proxy) {
+  _ProxyCredentials _findProxyCredentials(_Proxy proxy,
+                                          [_AuthenticationScheme scheme]) {
     // Look for credentials.
     var it = _proxyCredentials.iterator;
     while (it.moveNext()) {
-      if (it.current.applies(proxy, _AuthenticationScheme.BASIC)) {
+      if (it.current.applies(proxy, scheme)) {
         return it.current;
       }
     }
@@ -1605,6 +1564,13 @@
     }
   }
 
+  void _removeProxyCredentials(_Credentials cr) {
+    int index = _proxyCredentials.indexOf(cr);
+    if (index != -1) {
+      _proxyCredentials.removeAt(index);
+    }
+  }
+
   static String _findProxyFromEnvironment(Uri url,
                                           Map<String, String> environment) {
     checkNoProxy(String option) {
@@ -2131,6 +2097,7 @@
   : super(creds, realm);
 
   bool applies(_Proxy proxy, _AuthenticationScheme scheme) {
+    if (scheme != null && credentials.scheme != scheme) return false;
     return proxy.host == host && proxy.port == port;
   }
 
diff --git a/sdk/lib/io/string_transformer.dart b/sdk/lib/io/string_transformer.dart
index 3332879..ec6d0e2 100644
--- a/sdk/lib/io/string_transformer.dart
+++ b/sdk/lib/io/string_transformer.dart
@@ -185,12 +185,34 @@
 String _decodeString(List<int> bytes, [Encoding encoding = Encoding.UTF_8]) {
   if (bytes.length == 0) return "";
   var string;
+  var error;
   var controller = new StreamController();
   controller.stream
     .transform(new StringDecoder(encoding))
-    .listen((data) => string = data);
+    .listen((data) => string = data,
+            onError: (e) => error = e);
   controller.add(bytes);
   controller.close();
+  if (error != null) throw error;
+  assert(string != null);
+  return string;
+}
+
+
+// Utility function to synchronously decode a utf8-encoded list of bytes,
+// throwing on error.
+String _decodeUtf8Strict(List<int> bytes) {
+  if (bytes.length == 0) return "";
+  var string;
+  var error;
+  var controller = new StreamController();
+  controller.stream
+    .transform(new Utf8DecoderTransformer(null))
+    .listen((data) => string = data,
+            onError: (e) => error = e);
+  controller.add(bytes);
+  controller.close();
+  if (error != null) throw error;
   assert(string != null);
   return string;
 }
diff --git a/sdk/lib/io/websocket.dart b/sdk/lib/io/websocket.dart
index 5cbe278..994fdec 100644
--- a/sdk/lib/io/websocket.dart
+++ b/sdk/lib/io/websocket.dart
@@ -34,9 +34,9 @@
  *     HttpServer server;
  *     server.listen((request) {
  *       if (...) {
- *         WebSocketTransformer.upgrade(request).then(websocket) {
+ *         WebSocketTransformer.upgrade(request).then((websocket) {
  *           ...
- *         }
+ *         });
  *       } else {
  *         // Do normal HTTP request processing.
  *       }
diff --git a/sdk/lib/io/websocket_impl.dart b/sdk/lib/io/websocket_impl.dart
index 52587ed..74df425 100644
--- a/sdk/lib/io/websocket_impl.dart
+++ b/sdk/lib/io/websocket_impl.dart
@@ -60,7 +60,7 @@
   /**
    * Process data received from the underlying communication channel.
    */
-  void handleData(List<int> buffer, EventSink sink) {
+  void handleData(Uint8List buffer, EventSink sink) {
     int count = buffer.length;
     int index = 0;
     int lastIndex = count;
@@ -76,6 +76,10 @@
         switch (_state) {
           case START:
             _fin = (byte & 0x80) != 0;
+            if ((byte & 0x70) != 0) {
+              // The RSV1, RSV2 bits RSV3 most be all zero.
+              throw new WebSocketException("Protocol error");
+            }
             _opcode = (byte & 0xF);
             switch (_opcode) {
             case _WebSocketOpcode.CONTINUATION:
@@ -89,7 +93,15 @@
                 throw new WebSocketException("Protocol error");
               }
               _currentMessageType = _WebSocketMessageType.TEXT;
-              _buffer = new StringBuffer();
+              _controller = new StreamController();
+              _controller.stream
+                  .transform(new Utf8DecoderTransformer(null))
+                  .fold(new StringBuffer(), (buffer, str) => buffer..write(str))
+                  .then((buffer) {
+                    sink.add(buffer.toString());
+                  }, onError: (error) {
+                    sink.addError(error);
+                  });
               break;
 
             case _WebSocketOpcode.BINARY:
@@ -97,7 +109,14 @@
                 throw new WebSocketException("Protocol error");
               }
               _currentMessageType = _WebSocketMessageType.BINARY;
-              _buffer = new _BufferList();
+              _controller = new StreamController();
+              _controller.stream
+                  .fold(new _BufferList(), (buffer, data) => buffer..add(data))
+                  .then((buffer) {
+                    sink.add(buffer.readBytes());
+                  }, onError: (error) {
+                    sink.addError(error);
+                  });
               break;
 
             case _WebSocketOpcode.CLOSE:
@@ -116,7 +135,7 @@
           case LEN_FIRST:
             _masked = (byte & 0x80) != 0;
             _len = byte & 0x7F;
-            if (_isControlFrame() && _len > 126) {
+            if (_isControlFrame() && _len > 125) {
               throw new WebSocketException("Protocol error");
             }
             if (_len < 126) {
@@ -183,29 +202,15 @@
                 _controlFrameEnd(sink);
               }
             } else {
-              switch (_currentMessageType) {
-                case _WebSocketMessageType.NONE:
+              if (_currentMessageType != _WebSocketMessageType.TEXT &&
+                  _currentMessageType != _WebSocketMessageType.BINARY) {
                   throw new WebSocketException("Protocol error");
-
-                case _WebSocketMessageType.TEXT:
-                  _buffer.write(_decodeString(
-                      buffer.sublist(index, index + payload)));
-                  index += payload;
-                  if (_remainingPayloadBytes == 0) {
-                    _messageFrameEnd(sink);
-                  }
-                  break;
-
-                case _WebSocketMessageType.BINARY:
-                  _buffer.write(buffer.sublist(index, index + payload));
-                  index += payload;
-                  if (_remainingPayloadBytes == 0) {
-                    _messageFrameEnd(sink);
-                  }
-                  break;
-
-                default:
-                  throw new WebSocketException("Protocol error");
+              }
+              _controller.add(
+                  new Uint8List.view(buffer.buffer, index, payload));
+              index += payload;
+              if (_remainingPayloadBytes == 0) {
+                _messageFrameEnd(sink);
               }
             }
 
@@ -255,10 +260,10 @@
             sink.close();
             break;
           case _WebSocketOpcode.PING:
-            // TODO(ajohnsen): Handle ping.
+            sink.add(new _WebSocketPing());
             break;
           case _WebSocketOpcode.PONG:
-            // TODO(ajohnsen): Handle pong.
+            sink.add(new _WebSocketPong());
             break;
         }
         _prepareForNextFrame();
@@ -274,13 +279,13 @@
     if (_fin) {
       switch (_currentMessageType) {
         case _WebSocketMessageType.TEXT:
-          sink.add(_buffer.toString());
+          _controller.close();
           break;
         case _WebSocketMessageType.BINARY:
-          sink.add(_buffer.readBytes());
+          _controller.close();
           break;
       }
-      _buffer = null;
+      _controller = null;
       _currentMessageType = _WebSocketMessageType.NONE;
     }
     _prepareForNextFrame();
@@ -299,8 +304,7 @@
             throw new WebSocketException("Protocol error");
           }
           if (_controlPayload.length > 2) {
-            closeReason = _decodeString(
-                _controlPayload.sublist(2));
+            closeReason = _decodeUtf8Strict(_controlPayload.sublist(2));
           }
         }
         _state = CLOSED;
@@ -308,11 +312,11 @@
         break;
 
       case _WebSocketOpcode.PING:
-        // TODO(ajohnsen): Handle ping.
+        sink.add(new _WebSocketPing(_controlPayload));
         break;
 
       case _WebSocketOpcode.PONG:
-        // TODO(ajohnsen): Handle pong.
+        sink.add(new _WebSocketPong(_controlPayload));
         break;
     }
     _prepareForNextFrame();
@@ -351,13 +355,25 @@
 
   int _currentMessageType;
   List<int> _controlPayload;
-  var _buffer;  // Either StringBuffer or _BufferList.
+  StreamController _controller;
 
   int closeCode = WebSocketStatus.NO_STATUS_RECEIVED;
   String closeReason = "";
 }
 
 
+class _WebSocketPing {
+  final List<int> payload;
+  _WebSocketPing([this.payload = null]);
+}
+
+
+class _WebSocketPong {
+  final List<int> payload;
+  _WebSocketPong([this.payload = null]);
+}
+
+
 class _WebSocketTransformerImpl implements WebSocketTransformer {
   final StreamController<WebSocket> _controller =
       new StreamController<WebSocket>();
@@ -434,6 +450,14 @@
   _WebSocketOutgoingTransformer(_WebSocketImpl this.webSocket);
 
   void handleData(message, EventSink<List<int>> sink) {
+    if (message is _WebSocketPong) {
+      addFrame(_WebSocketOpcode.PONG, message.payload, sink);
+      return;
+    }
+    if (message is _WebSocketPing) {
+      addFrame(_WebSocketOpcode.PONG, message.payload, sink);
+      return;
+    }
     List<int> data;
     int opcode;
     if (message != null) {
@@ -470,7 +494,11 @@
   }
 
   void addFrame(int opcode, List<int> data, EventSink<List<int>> sink) {
-    bool mask = !webSocket._serverSide;  // Masking not implemented for server.
+    createFrame(opcode, data, webSocket._serverSide).forEach(sink.add);
+  }
+
+  static Iterable createFrame(int opcode, List<int> data, bool serverSide) {
+    bool mask = !serverSide;  // Masking not implemented for server.
     int dataLength = data == null ? 0 : data.length;
     // Determine the header size.
     int headerSize = (mask) ? 6 : 2;
@@ -511,9 +539,10 @@
       }
     }
     assert(index == headerSize);
-    sink.add(header);
-    if (data != null) {
-      sink.add(data);
+    if (data == null) {
+      return [header];
+    } else {
+      return [header, data];
     }
   }
 }
@@ -584,13 +613,18 @@
   }
 
   Future close() {
+    _ensureController();
     Future closeSocket() {
       return socket.close().then((_) => webSocket);
     }
-    if (_controller == null) return closeSocket();
     _controller.close();
     return _closeCompleter.future.then((_) => closeSocket());
   }
+
+  void add(data) {
+    _ensureController();
+    _controller.add(data);
+  }
 }
 
 
@@ -682,22 +716,34 @@
 
   _WebSocketImpl._fromSocket(Socket this._socket,
                              [bool this._serverSide = false]) {
-    _sink = new _StreamSinkImpl(new _WebSocketConsumer(this, _socket));
+    var consumer = new _WebSocketConsumer(this, _socket);
+    _sink = new _StreamSinkImpl(consumer);
     _readyState = WebSocket.OPEN;
 
     var transformer = new _WebSocketProtocolTransformer(_serverSide);
     _socket.transform(transformer).listen(
         (data) {
-          _controller.add(data);
+          if (data is _WebSocketPing) {
+            consumer.add(new _WebSocketPong(data.payload));
+          } else if (data is _WebSocketPong) {
+            // TODO(ajohnsen): Notify pong?
+          } else {
+            _controller.add(data);
+          }
         },
         onError: (error) {
+          if (error is ArgumentError) {
+            close(WebSocketStatus.INVALID_FRAME_PAYLOAD_DATA);
+          } else {
+            close(WebSocketStatus.PROTOCOL_ERROR);
+          }
           _controller.addError(error);
           _controller.close();
         },
         onDone: () {
           if (_readyState == WebSocket.OPEN) {
             _readyState = WebSocket.CLOSING;
-            if (transformer.closeCode != WebSocketStatus.NO_STATUS_RECEIVED) {
+            if (!_isReservedStatusCode(transformer.closeCode)) {
               close(transformer.closeCode);
             } else {
               close();
@@ -735,9 +781,7 @@
 
   Future close([int code, String reason]) {
     if (!_writeClosed) {
-      if (code == WebSocketStatus.RESERVED_1004 ||
-          code == WebSocketStatus.NO_STATUS_RECEIVED ||
-          code == WebSocketStatus.RESERVED_1015) {
+      if (_isReservedStatusCode(code)) {
         throw new WebSocketException("Reserved status code $code");
       }
       _outCloseCode = code;
@@ -746,4 +790,16 @@
     }
     return _sink.close();
   }
+
+  static bool _isReservedStatusCode(int code) {
+    return code != null &&
+           (code < WebSocketStatus.NORMAL_CLOSURE ||
+            code == WebSocketStatus.RESERVED_1004 ||
+            code == WebSocketStatus.NO_STATUS_RECEIVED ||
+            code == WebSocketStatus.ABNORMAL_CLOSURE ||
+            (code > WebSocketStatus.INTERNAL_SERVER_ERROR &&
+             code < WebSocketStatus.RESERVED_1015) ||
+            (code >= WebSocketStatus.RESERVED_1015 &&
+             code < 3000));
+  }
 }
diff --git a/sdk/lib/mdv_observe_impl/mdv_observe_impl.dart b/sdk/lib/mdv_observe_impl/mdv_observe_impl.dart
new file mode 100644
index 0000000..e56554b
--- /dev/null
+++ b/sdk/lib/mdv_observe_impl/mdv_observe_impl.dart
@@ -0,0 +1,226 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This library itself is undocumented and not supported for end use.
+// Because dart:html must use some of this functionality, it has to be available
+// via a dart:* library. The public APIs are reexported via package:mdv_observe.
+// Generally we try to keep this library minimal, with utility types and
+// functions in the package.
+library dart.mdv_observe_impl;
+
+import 'dart:async';
+import 'dart:collection';
+
+/**
+ * Interface representing an observable object. This is used by data in
+ * model-view architectures to notify interested parties of [changes].
+ *
+ * This object does not require any specific technique to implement
+ * observability.
+ *
+ * You can use [ObservableMixin] as a base class or mixin to implement this.
+ */
+abstract class Observable {
+  /**
+   * The stream of change records to this object.
+   *
+   * Changes should be delivered in asynchronous batches by calling
+   * [queueChangeRecords].
+   *
+   * [deliverChangeRecords] can be called to force delivery.
+   */
+  Stream<List<ChangeRecord>> get changes;
+
+  // TODO(jmesserly): remove these ASAP.
+  /**
+   * *Warning*: this method is temporary until dart2js supports mirrors.
+   * Gets the value of a field or index. This should return null if it was
+   * not found.
+   */
+  getValueWorkaround(key);
+
+  /**
+   * *Warning*: this method is temporary until dart2js supports mirrors.
+   * Sets the value of a field or index. This should have no effect if the field
+   * was not found.
+   */
+  void setValueWorkaround(key, Object value);
+}
+
+/**
+ * Base class implementing [Observable].
+ *
+ * When a field, property, or indexable item is changed, a derived class should
+ * call [notifyPropertyChange]. See that method for an example.
+ */
+typedef ObservableBase = Object with ObservableMixin;
+
+/**
+ * Mixin for implementing [Observable] objects.
+ *
+ * When a field, property, or indexable item is changed, a derived class should
+ * call [notifyPropertyChange]. See that method for an example.
+ */
+abstract class ObservableMixin implements Observable {
+  StreamController<List<ChangeRecord>> _observers;
+  Stream<List<ChangeRecord>> _stream;
+  List<ChangeRecord> _changes;
+
+  Stream<List<ChangeRecord>> get changes {
+    if (_observers == null) {
+      _observers = new StreamController<List<ChangeRecord>>();
+      _stream = _observers.stream.asBroadcastStream();
+    }
+    return _stream;
+  }
+
+  void _deliverChanges() {
+    var changes = _changes;
+    _changes = null;
+    if (hasObservers && changes != null) {
+      // TODO(jmesserly): make "changes" immutable
+      _observers.add(changes);
+    }
+  }
+
+  /**
+   * True if this object has any observers, and should call
+   * [notifyPropertyChange] for changes.
+   */
+  bool get hasObservers => _observers != null && _observers.hasListener;
+
+  /**
+   * Notify that the field [name] of this object has been changed.
+   *
+   * The [oldValue] and [newValue] are also recorded. If the two values are
+   * identical, no change will be recorded.
+   *
+   * For convenience this returns [newValue]. This makes it easy to use in a
+   * setter:
+   *
+   *     var _myField;
+   *     get myField => _myField;
+   *     set myField(value) {
+   *       _myField = notifyPropertyChange(
+   *           const Symbol('myField'), _myField, value);
+   *     }
+   */
+  // TODO(jmesserly): should this be == instead of identical, to prevent
+  // spurious loops?
+  notifyPropertyChange(Symbol field, Object oldValue, Object newValue) {
+    if (hasObservers && !identical(oldValue, newValue)) {
+      notifyChange(new PropertyChangeRecord(field));
+    }
+    return newValue;
+  }
+
+  /**
+   * Notify observers of a change. For most objects [notifyPropertyChange] is
+   * more convenient, but collections sometimes deliver other types of changes
+   * such as a [ListChangeRecord].
+   */
+  void notifyChange(ChangeRecord record) {
+    if (!hasObservers) return;
+
+    if (_changes == null) {
+      _changes = [];
+      queueChangeRecords(_deliverChanges);
+    }
+    _changes.add(record);
+  }
+}
+
+
+/** Records a change to an [Observable]. */
+abstract class ChangeRecord {
+  /** True if the change affected the given item, otherwise false. */
+  bool change(key);
+}
+
+/** A change record to a field of an observable object. */
+class PropertyChangeRecord extends ChangeRecord {
+  /** The field that was changed. */
+  final Symbol field;
+
+  PropertyChangeRecord(this.field);
+
+  bool changes(key) => key is Symbol && field == key;
+
+  String toString() => '#<PropertyChangeRecord $field>';
+}
+
+/** A change record for an observable list. */
+class ListChangeRecord extends ChangeRecord {
+  /** The starting index of the change. */
+  final int index;
+
+  /** The number of items removed. */
+  final int removedCount;
+
+  /** The number of items added. */
+  final int addedCount;
+
+  ListChangeRecord(this.index, {this.removedCount: 0, this.addedCount: 0}) {
+    if (addedCount == 0 && removedCount == 0) {
+      throw new ArgumentError('added and removed counts should not both be '
+          'zero. Use 1 if this was a single item update.');
+    }
+  }
+
+  /** Returns true if the provided index was changed by this operation. */
+  bool changes(key) {
+    // If key isn't an int, or before the index, then it wasn't changed.
+    if (key is! int || key < index) return false;
+
+    // If this was a shift operation, anything after index is changed.
+    if (addedCount != removedCount) return true;
+
+    // Otherwise, anything in the update range was changed.
+    return key < index + addedCount;
+  }
+
+  String toString() => '#<ListChangeRecord index: $index, '
+      'removed: $removedCount, addedCount: $addedCount>';
+}
+
+/**
+ * Synchronously deliver [Observable.changes] for all observables.
+ * If new changes are added as a result of delivery, this will keep running
+ * until all pending change records are delivered.
+ */
+// TODO(jmesserly): this is a bit different from the ES Harmony version, which
+// allows delivery of changes to a particular observer:
+// http://wiki.ecmascript.org/doku.php?id=harmony:observe#object.deliverchangerecords
+// However the binding system needs delivery of everything, along the lines of:
+// https://github.com/toolkitchen/mdv/blob/stable/src/model.js#L19
+// https://github.com/rafaelw/ChangeSummary/blob/master/change_summary.js#L590
+// TODO(jmesserly): in the future, we can use this to trigger dirty checking.
+void deliverChangeRecords() {
+  if (_deliverCallbacks == null) return;
+
+  while (!_deliverCallbacks.isEmpty) {
+    var deliver = _deliverCallbacks.removeFirst();
+
+    try {
+      deliver();
+    } catch (e, s) {
+      // Schedule the error to be top-leveled later.
+      new Completer().completeError(e, s);
+    }
+  }
+
+  // Null it out, so [queueChangeRecords] will reschedule this method.
+  _deliverCallbacks = null;
+}
+
+/** Queues an action to happen during the [deliverChangeRecords] timeslice. */
+void queueChangeRecords(void deliverChanges()) {
+  if (_deliverCallbacks == null) {
+    _deliverCallbacks = new Queue<Function>();
+    runAsync(deliverChangeRecords);
+  }
+  _deliverCallbacks.add(deliverChanges);
+}
+
+Queue _deliverCallbacks;
diff --git a/sdk/lib/svg/dart2js/svg_dart2js.dart b/sdk/lib/svg/dart2js/svg_dart2js.dart
index 37ded8b..a2bba24 100644
--- a/sdk/lib/svg/dart2js/svg_dart2js.dart
+++ b/sdk/lib/svg/dart2js/svg_dart2js.dart
@@ -3006,7 +3006,7 @@
 
 @DocsEditable
 @DomName('SVGLengthList')
-class LengthList implements JavaScriptIndexingBehavior, List<Length> native "SVGLengthList" {
+class LengthList extends Object with ListMixin<Length>, ImmutableListMixin<Length> implements JavaScriptIndexingBehavior, List<Length> native "SVGLengthList" {
 
   @DomName('SVGLengthList.numberOfItems')
   @DocsEditable
@@ -3020,196 +3020,13 @@
   // -- start List<Length> mixins.
   // Length is the element type.
 
-  // From Iterable<Length>:
-
-  Iterator<Length> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<Length>(this);
-  }
-
   // SVG Collections expose numberOfItems rather than length.
   int get length => numberOfItems;
-  Length reduce(Length combine(Length value, Length element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
 
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, Length element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(Length element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(Length element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(Length element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<Length> where(bool f(Length element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(Length element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(Length element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(Length element)) => IterableMixinWorkaround.any(this, f);
-
-  List<Length> toList({ bool growable: true }) =>
-      new List<Length>.from(this, growable: growable);
-
-  Set<Length> toSet() => new Set<Length>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<Length> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<Length> takeWhile(bool test(Length value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<Length> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<Length> skipWhile(bool test(Length value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  Length firstWhere(bool test(Length value), { Length orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  Length lastWhere(bool test(Length value), {Length orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  Length singleWhere(bool test(Length value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  Length elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<Length>:
-
-  void add(Length value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<Length> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<Length>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  // clear() defined by IDL.
-
-  Iterable<Length> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(Length a, Length b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(Length element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(Length element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  Length get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  Length get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  Length get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, Length element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<Length> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<Length> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Length removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  Length removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(Length element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(Length element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<Length> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<Length> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [Length fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<Length> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<Length> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <Length>[]);
-  }
-
-  Map<int, Length> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<Length> mixins.
 
   @DomName('SVGLengthList.appendItem')
@@ -3650,7 +3467,7 @@
 
 @DocsEditable
 @DomName('SVGNumberList')
-class NumberList implements JavaScriptIndexingBehavior, List<Number> native "SVGNumberList" {
+class NumberList extends Object with ListMixin<Number>, ImmutableListMixin<Number> implements JavaScriptIndexingBehavior, List<Number> native "SVGNumberList" {
 
   @DomName('SVGNumberList.numberOfItems')
   @DocsEditable
@@ -3664,196 +3481,13 @@
   // -- start List<Number> mixins.
   // Number is the element type.
 
-  // From Iterable<Number>:
-
-  Iterator<Number> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<Number>(this);
-  }
-
   // SVG Collections expose numberOfItems rather than length.
   int get length => numberOfItems;
-  Number reduce(Number combine(Number value, Number element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
 
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, Number element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(Number element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(Number element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(Number element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<Number> where(bool f(Number element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(Number element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(Number element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(Number element)) => IterableMixinWorkaround.any(this, f);
-
-  List<Number> toList({ bool growable: true }) =>
-      new List<Number>.from(this, growable: growable);
-
-  Set<Number> toSet() => new Set<Number>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<Number> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<Number> takeWhile(bool test(Number value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<Number> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<Number> skipWhile(bool test(Number value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  Number firstWhere(bool test(Number value), { Number orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  Number lastWhere(bool test(Number value), {Number orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  Number singleWhere(bool test(Number value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  Number elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<Number>:
-
-  void add(Number value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<Number> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<Number>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  // clear() defined by IDL.
-
-  Iterable<Number> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(Number a, Number b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(Number element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(Number element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  Number get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  Number get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  Number get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, Number element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<Number> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<Number> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Number removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  Number removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(Number element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(Number element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<Number> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<Number> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [Number fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<Number> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<Number> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <Number>[]);
-  }
-
-  Map<int, Number> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<Number> mixins.
 
   @DomName('SVGNumberList.appendItem')
@@ -4525,7 +4159,7 @@
 
 @DocsEditable
 @DomName('SVGPathSegList')
-class PathSegList implements JavaScriptIndexingBehavior, List<PathSeg> native "SVGPathSegList" {
+class PathSegList extends Object with ListMixin<PathSeg>, ImmutableListMixin<PathSeg> implements JavaScriptIndexingBehavior, List<PathSeg> native "SVGPathSegList" {
 
   @DomName('SVGPathSegList.numberOfItems')
   @DocsEditable
@@ -4539,196 +4173,13 @@
   // -- start List<PathSeg> mixins.
   // PathSeg is the element type.
 
-  // From Iterable<PathSeg>:
-
-  Iterator<PathSeg> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<PathSeg>(this);
-  }
-
   // SVG Collections expose numberOfItems rather than length.
   int get length => numberOfItems;
-  PathSeg reduce(PathSeg combine(PathSeg value, PathSeg element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
 
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, PathSeg element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(PathSeg element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(PathSeg element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(PathSeg element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<PathSeg> where(bool f(PathSeg element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(PathSeg element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(PathSeg element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(PathSeg element)) => IterableMixinWorkaround.any(this, f);
-
-  List<PathSeg> toList({ bool growable: true }) =>
-      new List<PathSeg>.from(this, growable: growable);
-
-  Set<PathSeg> toSet() => new Set<PathSeg>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<PathSeg> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<PathSeg> takeWhile(bool test(PathSeg value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<PathSeg> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<PathSeg> skipWhile(bool test(PathSeg value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  PathSeg firstWhere(bool test(PathSeg value), { PathSeg orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  PathSeg lastWhere(bool test(PathSeg value), {PathSeg orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  PathSeg singleWhere(bool test(PathSeg value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  PathSeg elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<PathSeg>:
-
-  void add(PathSeg value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<PathSeg> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<PathSeg>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  // clear() defined by IDL.
-
-  Iterable<PathSeg> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(PathSeg a, PathSeg b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(PathSeg element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(PathSeg element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  PathSeg get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  PathSeg get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  PathSeg get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, PathSeg element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<PathSeg> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<PathSeg> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  PathSeg removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  PathSeg removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(PathSeg element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(PathSeg element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<PathSeg> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<PathSeg> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [PathSeg fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<PathSeg> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<PathSeg> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <PathSeg>[]);
-  }
-
-  Map<int, PathSeg> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<PathSeg> mixins.
 
   @DomName('SVGPathSegList.appendItem')
@@ -5430,7 +4881,7 @@
 
 @DocsEditable
 @DomName('SVGStringList')
-class StringList implements JavaScriptIndexingBehavior, List<String> native "SVGStringList" {
+class StringList extends Object with ListMixin<String>, ImmutableListMixin<String> implements JavaScriptIndexingBehavior, List<String> native "SVGStringList" {
 
   @DomName('SVGStringList.numberOfItems')
   @DocsEditable
@@ -5444,196 +4895,13 @@
   // -- start List<String> mixins.
   // String is the element type.
 
-  // From Iterable<String>:
-
-  Iterator<String> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<String>(this);
-  }
-
   // SVG Collections expose numberOfItems rather than length.
   int get length => numberOfItems;
-  String reduce(String combine(String value, String element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
 
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, String element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(String element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(String element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(String element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<String> where(bool f(String element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(String element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(String element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(String element)) => IterableMixinWorkaround.any(this, f);
-
-  List<String> toList({ bool growable: true }) =>
-      new List<String>.from(this, growable: growable);
-
-  Set<String> toSet() => new Set<String>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<String> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<String> takeWhile(bool test(String value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<String> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<String> skipWhile(bool test(String value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  String firstWhere(bool test(String value), { String orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  String lastWhere(bool test(String value), {String orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  String singleWhere(bool test(String value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  String elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<String>:
-
-  void add(String value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<String> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<String>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  // clear() defined by IDL.
-
-  Iterable<String> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(String a, String b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(String element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(String element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  String get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  String get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  String get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, String element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<String> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<String> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  String removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  String removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(String element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(String element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<String> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<String> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [String fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<String> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<String> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <String>[]);
-  }
-
-  Map<int, String> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<String> mixins.
 
   @DomName('SVGStringList.appendItem')
@@ -6608,7 +5876,7 @@
 
 @DocsEditable
 @DomName('SVGTransformList')
-class TransformList implements List<Transform>, JavaScriptIndexingBehavior native "SVGTransformList" {
+class TransformList extends Object with ListMixin<Transform>, ImmutableListMixin<Transform> implements List<Transform>, JavaScriptIndexingBehavior native "SVGTransformList" {
 
   @DomName('SVGTransformList.numberOfItems')
   @DocsEditable
@@ -6622,196 +5890,13 @@
   // -- start List<Transform> mixins.
   // Transform is the element type.
 
-  // From Iterable<Transform>:
-
-  Iterator<Transform> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<Transform>(this);
-  }
-
   // SVG Collections expose numberOfItems rather than length.
   int get length => numberOfItems;
-  Transform reduce(Transform combine(Transform value, Transform element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
 
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, Transform element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(Transform element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(Transform element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(Transform element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<Transform> where(bool f(Transform element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(Transform element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(Transform element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(Transform element)) => IterableMixinWorkaround.any(this, f);
-
-  List<Transform> toList({ bool growable: true }) =>
-      new List<Transform>.from(this, growable: growable);
-
-  Set<Transform> toSet() => new Set<Transform>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<Transform> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<Transform> takeWhile(bool test(Transform value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<Transform> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<Transform> skipWhile(bool test(Transform value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  Transform firstWhere(bool test(Transform value), { Transform orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  Transform lastWhere(bool test(Transform value), {Transform orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  Transform singleWhere(bool test(Transform value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  Transform elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<Transform>:
-
-  void add(Transform value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<Transform> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<Transform>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  // clear() defined by IDL.
-
-  Iterable<Transform> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(Transform a, Transform b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(Transform element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(Transform element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  Transform get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  Transform get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  Transform get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, Transform element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<Transform> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<Transform> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Transform removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  Transform removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(Transform element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(Transform element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<Transform> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<Transform> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [Transform fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<Transform> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<Transform> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <Transform>[]);
-  }
-
-  Map<int, Transform> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<Transform> mixins.
 
   @DomName('SVGTransformList.appendItem')
@@ -7147,7 +6232,7 @@
 
 @DocsEditable
 @DomName('SVGElementInstanceList')
-class _ElementInstanceList implements JavaScriptIndexingBehavior, List<ElementInstance> native "SVGElementInstanceList" {
+class _ElementInstanceList extends Object with ListMixin<ElementInstance>, ImmutableListMixin<ElementInstance> implements JavaScriptIndexingBehavior, List<ElementInstance> native "SVGElementInstanceList" {
 
   @DomName('SVGElementInstanceList.length')
   @DocsEditable
@@ -7161,196 +6246,11 @@
   // -- start List<ElementInstance> mixins.
   // ElementInstance is the element type.
 
-  // From Iterable<ElementInstance>:
 
-  Iterator<ElementInstance> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<ElementInstance>(this);
-  }
-
-  ElementInstance reduce(ElementInstance combine(ElementInstance value, ElementInstance element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, ElementInstance element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(ElementInstance element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(ElementInstance element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(ElementInstance element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<ElementInstance> where(bool f(ElementInstance element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(ElementInstance element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(ElementInstance element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(ElementInstance element)) => IterableMixinWorkaround.any(this, f);
-
-  List<ElementInstance> toList({ bool growable: true }) =>
-      new List<ElementInstance>.from(this, growable: growable);
-
-  Set<ElementInstance> toSet() => new Set<ElementInstance>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<ElementInstance> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<ElementInstance> takeWhile(bool test(ElementInstance value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<ElementInstance> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<ElementInstance> skipWhile(bool test(ElementInstance value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  ElementInstance firstWhere(bool test(ElementInstance value), { ElementInstance orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  ElementInstance lastWhere(bool test(ElementInstance value), {ElementInstance orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  ElementInstance singleWhere(bool test(ElementInstance value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  ElementInstance elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<ElementInstance>:
-
-  void add(ElementInstance value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<ElementInstance> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<ElementInstance>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<ElementInstance> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(ElementInstance a, ElementInstance b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(ElementInstance element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(ElementInstance element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  ElementInstance get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  ElementInstance get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  ElementInstance get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, ElementInstance element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<ElementInstance> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<ElementInstance> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  ElementInstance removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  ElementInstance removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(ElementInstance element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(ElementInstance element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<ElementInstance> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<ElementInstance> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [ElementInstance fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<ElementInstance> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<ElementInstance> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <ElementInstance>[]);
-  }
-
-  Map<int, ElementInstance> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<ElementInstance> mixins.
 
   @DomName('SVGElementInstanceList.item')
diff --git a/sdk/lib/svg/dartium/svg_dartium.dart b/sdk/lib/svg/dartium/svg_dartium.dart
index 7cf7d6c..3310b73 100644
--- a/sdk/lib/svg/dartium/svg_dartium.dart
+++ b/sdk/lib/svg/dartium/svg_dartium.dart
@@ -3266,7 +3266,7 @@
 
 @DocsEditable
 @DomName('SVGLengthList')
-class LengthList extends NativeFieldWrapperClass1 implements List<Length> {
+class LengthList extends NativeFieldWrapperClass1 with ListMixin<Length>, ImmutableListMixin<Length> implements List<Length> {
   LengthList.internal();
 
   @DomName('SVGLengthList.numberOfItems')
@@ -3281,196 +3281,13 @@
   // -- start List<Length> mixins.
   // Length is the element type.
 
-  // From Iterable<Length>:
-
-  Iterator<Length> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<Length>(this);
-  }
-
   // SVG Collections expose numberOfItems rather than length.
   int get length => numberOfItems;
-  Length reduce(Length combine(Length value, Length element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
 
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, Length element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(Length element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(Length element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(Length element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<Length> where(bool f(Length element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(Length element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(Length element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(Length element)) => IterableMixinWorkaround.any(this, f);
-
-  List<Length> toList({ bool growable: true }) =>
-      new List<Length>.from(this, growable: growable);
-
-  Set<Length> toSet() => new Set<Length>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<Length> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<Length> takeWhile(bool test(Length value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<Length> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<Length> skipWhile(bool test(Length value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  Length firstWhere(bool test(Length value), { Length orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  Length lastWhere(bool test(Length value), {Length orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  Length singleWhere(bool test(Length value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  Length elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<Length>:
-
-  void add(Length value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<Length> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<Length>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  // clear() defined by IDL.
-
-  Iterable<Length> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(Length a, Length b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(Length element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(Length element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  Length get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  Length get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  Length get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, Length element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<Length> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<Length> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Length removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  Length removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(Length element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(Length element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<Length> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<Length> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [Length fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<Length> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<Length> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <Length>[]);
-  }
-
-  Map<int, Length> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<Length> mixins.
 
   @DomName('SVGLengthList.appendItem')
@@ -3987,7 +3804,7 @@
 
 @DocsEditable
 @DomName('SVGNumberList')
-class NumberList extends NativeFieldWrapperClass1 implements List<Number> {
+class NumberList extends NativeFieldWrapperClass1 with ListMixin<Number>, ImmutableListMixin<Number> implements List<Number> {
   NumberList.internal();
 
   @DomName('SVGNumberList.numberOfItems')
@@ -4002,196 +3819,13 @@
   // -- start List<Number> mixins.
   // Number is the element type.
 
-  // From Iterable<Number>:
-
-  Iterator<Number> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<Number>(this);
-  }
-
   // SVG Collections expose numberOfItems rather than length.
   int get length => numberOfItems;
-  Number reduce(Number combine(Number value, Number element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
 
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, Number element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(Number element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(Number element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(Number element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<Number> where(bool f(Number element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(Number element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(Number element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(Number element)) => IterableMixinWorkaround.any(this, f);
-
-  List<Number> toList({ bool growable: true }) =>
-      new List<Number>.from(this, growable: growable);
-
-  Set<Number> toSet() => new Set<Number>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<Number> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<Number> takeWhile(bool test(Number value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<Number> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<Number> skipWhile(bool test(Number value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  Number firstWhere(bool test(Number value), { Number orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  Number lastWhere(bool test(Number value), {Number orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  Number singleWhere(bool test(Number value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  Number elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<Number>:
-
-  void add(Number value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<Number> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<Number>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  // clear() defined by IDL.
-
-  Iterable<Number> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(Number a, Number b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(Number element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(Number element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  Number get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  Number get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  Number get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, Number element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<Number> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<Number> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Number removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  Number removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(Number element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(Number element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<Number> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<Number> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [Number fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<Number> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<Number> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <Number>[]);
-  }
-
-  Map<int, Number> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<Number> mixins.
 
   @DomName('SVGNumberList.appendItem')
@@ -5135,7 +4769,7 @@
 
 @DocsEditable
 @DomName('SVGPathSegList')
-class PathSegList extends NativeFieldWrapperClass1 implements List<PathSeg> {
+class PathSegList extends NativeFieldWrapperClass1 with ListMixin<PathSeg>, ImmutableListMixin<PathSeg> implements List<PathSeg> {
   PathSegList.internal();
 
   @DomName('SVGPathSegList.numberOfItems')
@@ -5150,196 +4784,13 @@
   // -- start List<PathSeg> mixins.
   // PathSeg is the element type.
 
-  // From Iterable<PathSeg>:
-
-  Iterator<PathSeg> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<PathSeg>(this);
-  }
-
   // SVG Collections expose numberOfItems rather than length.
   int get length => numberOfItems;
-  PathSeg reduce(PathSeg combine(PathSeg value, PathSeg element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
 
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, PathSeg element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(PathSeg element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(PathSeg element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(PathSeg element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<PathSeg> where(bool f(PathSeg element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(PathSeg element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(PathSeg element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(PathSeg element)) => IterableMixinWorkaround.any(this, f);
-
-  List<PathSeg> toList({ bool growable: true }) =>
-      new List<PathSeg>.from(this, growable: growable);
-
-  Set<PathSeg> toSet() => new Set<PathSeg>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<PathSeg> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<PathSeg> takeWhile(bool test(PathSeg value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<PathSeg> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<PathSeg> skipWhile(bool test(PathSeg value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  PathSeg firstWhere(bool test(PathSeg value), { PathSeg orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  PathSeg lastWhere(bool test(PathSeg value), {PathSeg orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  PathSeg singleWhere(bool test(PathSeg value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  PathSeg elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<PathSeg>:
-
-  void add(PathSeg value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<PathSeg> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<PathSeg>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  // clear() defined by IDL.
-
-  Iterable<PathSeg> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(PathSeg a, PathSeg b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(PathSeg element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(PathSeg element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  PathSeg get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  PathSeg get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  PathSeg get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, PathSeg element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<PathSeg> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<PathSeg> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  PathSeg removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  PathSeg removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(PathSeg element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(PathSeg element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<PathSeg> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<PathSeg> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [PathSeg fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<PathSeg> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<PathSeg> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <PathSeg>[]);
-  }
-
-  Map<int, PathSeg> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<PathSeg> mixins.
 
   @DomName('SVGPathSegList.appendItem')
@@ -6137,7 +5588,7 @@
 
 @DocsEditable
 @DomName('SVGStringList')
-class StringList extends NativeFieldWrapperClass1 implements List<String> {
+class StringList extends NativeFieldWrapperClass1 with ListMixin<String>, ImmutableListMixin<String> implements List<String> {
   StringList.internal();
 
   @DomName('SVGStringList.numberOfItems')
@@ -6152,196 +5603,13 @@
   // -- start List<String> mixins.
   // String is the element type.
 
-  // From Iterable<String>:
-
-  Iterator<String> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<String>(this);
-  }
-
   // SVG Collections expose numberOfItems rather than length.
   int get length => numberOfItems;
-  String reduce(String combine(String value, String element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
 
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, String element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(String element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(String element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(String element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<String> where(bool f(String element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(String element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(String element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(String element)) => IterableMixinWorkaround.any(this, f);
-
-  List<String> toList({ bool growable: true }) =>
-      new List<String>.from(this, growable: growable);
-
-  Set<String> toSet() => new Set<String>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<String> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<String> takeWhile(bool test(String value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<String> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<String> skipWhile(bool test(String value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  String firstWhere(bool test(String value), { String orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  String lastWhere(bool test(String value), {String orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  String singleWhere(bool test(String value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  String elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<String>:
-
-  void add(String value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<String> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<String>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  // clear() defined by IDL.
-
-  Iterable<String> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(String a, String b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(String element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(String element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  String get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  String get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  String get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, String element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<String> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<String> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  String removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  String removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(String element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(String element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<String> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<String> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [String fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<String> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<String> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <String>[]);
-  }
-
-  Map<int, String> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<String> mixins.
 
   @DomName('SVGStringList.appendItem')
@@ -7404,7 +6672,7 @@
 
 @DocsEditable
 @DomName('SVGTransformList')
-class TransformList extends NativeFieldWrapperClass1 implements List<Transform> {
+class TransformList extends NativeFieldWrapperClass1 with ListMixin<Transform>, ImmutableListMixin<Transform> implements List<Transform> {
   TransformList.internal();
 
   @DomName('SVGTransformList.numberOfItems')
@@ -7419,196 +6687,13 @@
   // -- start List<Transform> mixins.
   // Transform is the element type.
 
-  // From Iterable<Transform>:
-
-  Iterator<Transform> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<Transform>(this);
-  }
-
   // SVG Collections expose numberOfItems rather than length.
   int get length => numberOfItems;
-  Transform reduce(Transform combine(Transform value, Transform element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
 
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, Transform element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(Transform element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(Transform element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(Transform element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<Transform> where(bool f(Transform element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(Transform element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(Transform element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(Transform element)) => IterableMixinWorkaround.any(this, f);
-
-  List<Transform> toList({ bool growable: true }) =>
-      new List<Transform>.from(this, growable: growable);
-
-  Set<Transform> toSet() => new Set<Transform>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<Transform> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<Transform> takeWhile(bool test(Transform value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<Transform> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<Transform> skipWhile(bool test(Transform value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  Transform firstWhere(bool test(Transform value), { Transform orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  Transform lastWhere(bool test(Transform value), {Transform orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  Transform singleWhere(bool test(Transform value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  Transform elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<Transform>:
-
-  void add(Transform value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<Transform> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<Transform>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  // clear() defined by IDL.
-
-  Iterable<Transform> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(Transform a, Transform b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(Transform element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(Transform element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  Transform get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  Transform get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  Transform get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, Transform element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<Transform> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<Transform> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Transform removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  Transform removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(Transform element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(Transform element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<Transform> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<Transform> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [Transform fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<Transform> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<Transform> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <Transform>[]);
-  }
-
-  Map<int, Transform> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<Transform> mixins.
 
   @DomName('SVGTransformList.appendItem')
@@ -7997,7 +7082,7 @@
 
 @DocsEditable
 @DomName('SVGElementInstanceList')
-class _ElementInstanceList extends NativeFieldWrapperClass1 implements List<ElementInstance> {
+class _ElementInstanceList extends NativeFieldWrapperClass1 with ListMixin<ElementInstance>, ImmutableListMixin<ElementInstance> implements List<ElementInstance> {
   _ElementInstanceList.internal();
 
   @DomName('SVGElementInstanceList.length')
@@ -8012,196 +7097,11 @@
   // -- start List<ElementInstance> mixins.
   // ElementInstance is the element type.
 
-  // From Iterable<ElementInstance>:
 
-  Iterator<ElementInstance> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<ElementInstance>(this);
-  }
-
-  ElementInstance reduce(ElementInstance combine(ElementInstance value, ElementInstance element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, ElementInstance element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(ElementInstance element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(ElementInstance element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(ElementInstance element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<ElementInstance> where(bool f(ElementInstance element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(ElementInstance element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(ElementInstance element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(ElementInstance element)) => IterableMixinWorkaround.any(this, f);
-
-  List<ElementInstance> toList({ bool growable: true }) =>
-      new List<ElementInstance>.from(this, growable: growable);
-
-  Set<ElementInstance> toSet() => new Set<ElementInstance>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<ElementInstance> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<ElementInstance> takeWhile(bool test(ElementInstance value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<ElementInstance> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<ElementInstance> skipWhile(bool test(ElementInstance value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  ElementInstance firstWhere(bool test(ElementInstance value), { ElementInstance orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  ElementInstance lastWhere(bool test(ElementInstance value), {ElementInstance orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  ElementInstance singleWhere(bool test(ElementInstance value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  ElementInstance elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<ElementInstance>:
-
-  void add(ElementInstance value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<ElementInstance> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<ElementInstance>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<ElementInstance> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(ElementInstance a, ElementInstance b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(ElementInstance element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(ElementInstance element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  ElementInstance get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  ElementInstance get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  ElementInstance get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, ElementInstance element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<ElementInstance> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<ElementInstance> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  ElementInstance removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  ElementInstance removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(ElementInstance element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(ElementInstance element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<ElementInstance> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<ElementInstance> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [ElementInstance fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<ElementInstance> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<ElementInstance> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <ElementInstance>[]);
-  }
-
-  Map<int, ElementInstance> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<ElementInstance> mixins.
 
   @DomName('SVGElementInstanceList.item')
diff --git a/sdk/lib/utf/utf_stream.dart b/sdk/lib/utf/utf_stream.dart
index 1480aa1..7d72118 100644
--- a/sdk/lib/utf/utf_stream.dart
+++ b/sdk/lib/utf/utf_stream.dart
@@ -23,56 +23,64 @@
   _StringDecoder(int this._replacementChar);
 
   void handleData(List<int> bytes, EventSink<String> sink) {
-    _buffer = <int>[];
-    List<int> carry = _carry;
-    _carry = null;
-    int pos = 0;
-    int available = bytes.length;
-    // If we have carry-over data, start from negative index, indicating carry
-    // index.
-    int goodChars = 0;
-    if (carry != null) pos = -carry.length;
-    while (pos < available) {
-      int currentPos = pos;
-      int getNext() {
-        if (pos < 0) {
-          return carry[pos++ + carry.length];
-        } else if (pos < available) {
-          return bytes[pos++];
+    try {
+      _buffer = <int>[];
+      List<int> carry = _carry;
+      _carry = null;
+      int pos = 0;
+      int available = bytes.length;
+      // If we have carry-over data, start from negative index, indicating carry
+      // index.
+      int goodChars = 0;
+      if (carry != null) pos = -carry.length;
+      while (pos < available) {
+        int currentPos = pos;
+        int getNext() {
+          if (pos < 0) {
+            return carry[pos++ + carry.length];
+          } else if (pos < available) {
+            return bytes[pos++];
+          }
+          return null;
         }
-        return null;
-      }
-      int consumed = _processBytes(getNext);
-      if (consumed > 0) {
-        goodChars = _buffer.length;
-      } else if (consumed == 0) {
-        _buffer.length = goodChars;
-        if (currentPos < 0) {
-          _carry = [];
-          _carry.addAll(carry);
-          _carry.addAll(bytes);
+        int consumed = _processBytes(getNext);
+        if (consumed > 0) {
+          goodChars = _buffer.length;
+        } else if (consumed == 0) {
+          _buffer.length = goodChars;
+          if (currentPos < 0) {
+            _carry = [];
+            _carry.addAll(carry);
+            _carry.addAll(bytes);
+          } else {
+            _carry = bytes.sublist(currentPos);
+          }
+          break;
         } else {
-          _carry = bytes.sublist(currentPos);
+          // Invalid byte at position pos - 1
+          _buffer.length = goodChars;
+          _addChar(-1);
+          goodChars = _buffer.length;
         }
-        break;
-      } else {
-        // Invalid byte at position pos - 1
-        _buffer.length = goodChars;
-        _addChar(-1);
-        goodChars = _buffer.length;
       }
+      if (_buffer.length > 0) {
+        // Limit to 'goodChars', if lower than actual charCodes in the buffer.
+        sink.add(new String.fromCharCodes(_buffer));
+      }
+      _buffer = null;
+    } catch (e) {
+      sink.addError(e);
     }
-    if (_buffer.length > 0) {
-      // Limit to 'goodChars', if lower than actual charCodes in the buffer.
-      sink.add(new String.fromCharCodes(_buffer));
-    }
-    _buffer = null;
   }
 
   void handleDone(EventSink<String> sink) {
     if (_carry != null) {
-      sink.add(new String.fromCharCodes(
-          new List.filled(_carry.length, _replacementChar)));
+      if (_replacementChar != null) {
+        sink.add(new String.fromCharCodes(
+            new List.filled(_carry.length, _replacementChar)));
+      } else {
+        throw new ArgumentError('Invalid codepoint');
+      }
     }
     sink.close();
   }
@@ -80,7 +88,16 @@
   int _processBytes(int getNext());
 
   void _addChar(int char) {
-    if (char > 0x10FFFF || char < 0) char = _replacementChar;
+    void error() {
+      if (_replacementChar != null) {
+        char = _replacementChar;
+      } else {
+        throw new ArgumentError('Invalid codepoint');
+      }
+    }
+    if (char < 0) error();
+    if (char >= 0xD800 && char <= 0xDFFF) error();
+    if (char > 0x10FFFF) error();
     _buffer.add(char);
   }
 }
@@ -127,6 +144,9 @@
         if (next == null) return 0;  // Not enough chars, reset.
         if ((next & 0xc0) != 0x80 || (next & 0xff) != next) return -1;
         value = value << 6 | (next & 0x3f);
+        if (additionalBytes >= 3 && i == 0 && value << 12 > 0x10FFFF) {
+          _addChar(-1);
+        }
       }
       // Invalid charCode if less then minimum expected.
       if (value < min) value = -1;
diff --git a/sdk/lib/web_audio/dart2js/web_audio_dart2js.dart b/sdk/lib/web_audio/dart2js/web_audio_dart2js.dart
index 1dcc599..8192802 100644
--- a/sdk/lib/web_audio/dart2js/web_audio_dart2js.dart
+++ b/sdk/lib/web_audio/dart2js/web_audio_dart2js.dart
@@ -87,9 +87,7 @@
 
   @DomName('AudioBuffer.getChannelData')
   @DocsEditable
-  @Returns('Float32List')
-  @Creates('Float32List')
-  List<double> getChannelData(int channelIndex) native;
+  Float32List getChannelData(int channelIndex) native;
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -865,9 +863,7 @@
 
   @DomName('WaveShaperNode.curve')
   @DocsEditable
-  @Returns('Float32List')
-  @Creates('Float32List')
-  List<double> curve;
+  Float32List curve;
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
diff --git a/sdk/lib/web_audio/dartium/web_audio_dartium.dart b/sdk/lib/web_audio/dartium/web_audio_dartium.dart
index 3c0dc87..950707c 100644
--- a/sdk/lib/web_audio/dartium/web_audio_dartium.dart
+++ b/sdk/lib/web_audio/dartium/web_audio_dartium.dart
@@ -63,15 +63,15 @@
 
   @DomName('AnalyserNode.getByteFrequencyData')
   @DocsEditable
-  void getByteFrequencyData(List<int> array) native "AnalyserNode_getByteFrequencyData_Callback";
+  void getByteFrequencyData(Uint8List array) native "AnalyserNode_getByteFrequencyData_Callback";
 
   @DomName('AnalyserNode.getByteTimeDomainData')
   @DocsEditable
-  void getByteTimeDomainData(List<int> array) native "AnalyserNode_getByteTimeDomainData_Callback";
+  void getByteTimeDomainData(Uint8List array) native "AnalyserNode_getByteTimeDomainData_Callback";
 
   @DomName('AnalyserNode.getFloatFrequencyData')
   @DocsEditable
-  void getFloatFrequencyData(List<double> array) native "AnalyserNode_getFloatFrequencyData_Callback";
+  void getFloatFrequencyData(Float32List array) native "AnalyserNode_getFloatFrequencyData_Callback";
 
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
@@ -112,7 +112,7 @@
 
   @DomName('AudioBuffer.getChannelData')
   @DocsEditable
-  List<double> getChannelData(int channelIndex) native "AudioBuffer_getChannelData_Callback";
+  Float32List getChannelData(int channelIndex) native "AudioBuffer_getChannelData_Callback";
 
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
@@ -367,7 +367,7 @@
 
   @DomName('AudioContext.createWaveTable')
   @DocsEditable
-  WaveTable createWaveTable(List<double> real, List<double> imag) native "AudioContext_createWaveTable_Callback";
+  WaveTable createWaveTable(Float32List real, Float32List imag) native "AudioContext_createWaveTable_Callback";
 
   @DomName('AudioContext.decodeAudioData')
   @DocsEditable
@@ -571,7 +571,7 @@
 
   @DomName('AudioParam.setValueCurveAtTime')
   @DocsEditable
-  void setValueCurveAtTime(List<double> values, num time, num duration) native "AudioParam_setValueCurveAtTime_Callback";
+  void setValueCurveAtTime(Float32List values, num time, num duration) native "AudioParam_setValueCurveAtTime_Callback";
 
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
@@ -662,7 +662,7 @@
 
   @DomName('BiquadFilterNode.getFrequencyResponse')
   @DocsEditable
-  void getFrequencyResponse(List<double> frequencyHz, List<double> magResponse, List<double> phaseResponse) native "BiquadFilterNode_getFrequencyResponse_Callback";
+  void getFrequencyResponse(Float32List frequencyHz, Float32List magResponse, Float32List phaseResponse) native "BiquadFilterNode_getFrequencyResponse_Callback";
 
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
@@ -1105,11 +1105,11 @@
 
   @DomName('WaveShaperNode.curve')
   @DocsEditable
-  List<double> get curve native "WaveShaperNode_curve_Getter";
+  Float32List get curve native "WaveShaperNode_curve_Getter";
 
   @DomName('WaveShaperNode.curve')
   @DocsEditable
-  void set curve(List<double> value) native "WaveShaperNode_curve_Setter";
+  void set curve(Float32List value) native "WaveShaperNode_curve_Setter";
 
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
diff --git a/sdk/lib/web_gl/dartium/web_gl_dartium.dart b/sdk/lib/web_gl/dartium/web_gl_dartium.dart
index 72bf110..7287b8f 100644
--- a/sdk/lib/web_gl/dartium/web_gl_dartium.dart
+++ b/sdk/lib/web_gl/dartium/web_gl_dartium.dart
@@ -1922,7 +1922,7 @@
 
   @DomName('WebGLRenderingContext.uniform1fv')
   @DocsEditable
-  void uniform1fv(UniformLocation location, List<double> v) native "WebGLRenderingContext_uniform1fv_Callback";
+  void uniform1fv(UniformLocation location, Float32List v) native "WebGLRenderingContext_uniform1fv_Callback";
 
   @DomName('WebGLRenderingContext.uniform1i')
   @DocsEditable
@@ -1930,7 +1930,7 @@
 
   @DomName('WebGLRenderingContext.uniform1iv')
   @DocsEditable
-  void uniform1iv(UniformLocation location, List<int> v) native "WebGLRenderingContext_uniform1iv_Callback";
+  void uniform1iv(UniformLocation location, Int32List v) native "WebGLRenderingContext_uniform1iv_Callback";
 
   @DomName('WebGLRenderingContext.uniform2f')
   @DocsEditable
@@ -1938,7 +1938,7 @@
 
   @DomName('WebGLRenderingContext.uniform2fv')
   @DocsEditable
-  void uniform2fv(UniformLocation location, List<double> v) native "WebGLRenderingContext_uniform2fv_Callback";
+  void uniform2fv(UniformLocation location, Float32List v) native "WebGLRenderingContext_uniform2fv_Callback";
 
   @DomName('WebGLRenderingContext.uniform2i')
   @DocsEditable
@@ -1946,7 +1946,7 @@
 
   @DomName('WebGLRenderingContext.uniform2iv')
   @DocsEditable
-  void uniform2iv(UniformLocation location, List<int> v) native "WebGLRenderingContext_uniform2iv_Callback";
+  void uniform2iv(UniformLocation location, Int32List v) native "WebGLRenderingContext_uniform2iv_Callback";
 
   @DomName('WebGLRenderingContext.uniform3f')
   @DocsEditable
@@ -1954,7 +1954,7 @@
 
   @DomName('WebGLRenderingContext.uniform3fv')
   @DocsEditable
-  void uniform3fv(UniformLocation location, List<double> v) native "WebGLRenderingContext_uniform3fv_Callback";
+  void uniform3fv(UniformLocation location, Float32List v) native "WebGLRenderingContext_uniform3fv_Callback";
 
   @DomName('WebGLRenderingContext.uniform3i')
   @DocsEditable
@@ -1962,7 +1962,7 @@
 
   @DomName('WebGLRenderingContext.uniform3iv')
   @DocsEditable
-  void uniform3iv(UniformLocation location, List<int> v) native "WebGLRenderingContext_uniform3iv_Callback";
+  void uniform3iv(UniformLocation location, Int32List v) native "WebGLRenderingContext_uniform3iv_Callback";
 
   @DomName('WebGLRenderingContext.uniform4f')
   @DocsEditable
@@ -1970,7 +1970,7 @@
 
   @DomName('WebGLRenderingContext.uniform4fv')
   @DocsEditable
-  void uniform4fv(UniformLocation location, List<double> v) native "WebGLRenderingContext_uniform4fv_Callback";
+  void uniform4fv(UniformLocation location, Float32List v) native "WebGLRenderingContext_uniform4fv_Callback";
 
   @DomName('WebGLRenderingContext.uniform4i')
   @DocsEditable
@@ -1978,19 +1978,19 @@
 
   @DomName('WebGLRenderingContext.uniform4iv')
   @DocsEditable
-  void uniform4iv(UniformLocation location, List<int> v) native "WebGLRenderingContext_uniform4iv_Callback";
+  void uniform4iv(UniformLocation location, Int32List v) native "WebGLRenderingContext_uniform4iv_Callback";
 
   @DomName('WebGLRenderingContext.uniformMatrix2fv')
   @DocsEditable
-  void uniformMatrix2fv(UniformLocation location, bool transpose, List<double> array) native "WebGLRenderingContext_uniformMatrix2fv_Callback";
+  void uniformMatrix2fv(UniformLocation location, bool transpose, Float32List array) native "WebGLRenderingContext_uniformMatrix2fv_Callback";
 
   @DomName('WebGLRenderingContext.uniformMatrix3fv')
   @DocsEditable
-  void uniformMatrix3fv(UniformLocation location, bool transpose, List<double> array) native "WebGLRenderingContext_uniformMatrix3fv_Callback";
+  void uniformMatrix3fv(UniformLocation location, bool transpose, Float32List array) native "WebGLRenderingContext_uniformMatrix3fv_Callback";
 
   @DomName('WebGLRenderingContext.uniformMatrix4fv')
   @DocsEditable
-  void uniformMatrix4fv(UniformLocation location, bool transpose, List<double> array) native "WebGLRenderingContext_uniformMatrix4fv_Callback";
+  void uniformMatrix4fv(UniformLocation location, bool transpose, Float32List array) native "WebGLRenderingContext_uniformMatrix4fv_Callback";
 
   @DomName('WebGLRenderingContext.useProgram')
   @DocsEditable
@@ -2006,7 +2006,7 @@
 
   @DomName('WebGLRenderingContext.vertexAttrib1fv')
   @DocsEditable
-  void vertexAttrib1fv(int indx, List<double> values) native "WebGLRenderingContext_vertexAttrib1fv_Callback";
+  void vertexAttrib1fv(int indx, Float32List values) native "WebGLRenderingContext_vertexAttrib1fv_Callback";
 
   @DomName('WebGLRenderingContext.vertexAttrib2f')
   @DocsEditable
@@ -2014,7 +2014,7 @@
 
   @DomName('WebGLRenderingContext.vertexAttrib2fv')
   @DocsEditable
-  void vertexAttrib2fv(int indx, List<double> values) native "WebGLRenderingContext_vertexAttrib2fv_Callback";
+  void vertexAttrib2fv(int indx, Float32List values) native "WebGLRenderingContext_vertexAttrib2fv_Callback";
 
   @DomName('WebGLRenderingContext.vertexAttrib3f')
   @DocsEditable
@@ -2022,7 +2022,7 @@
 
   @DomName('WebGLRenderingContext.vertexAttrib3fv')
   @DocsEditable
-  void vertexAttrib3fv(int indx, List<double> values) native "WebGLRenderingContext_vertexAttrib3fv_Callback";
+  void vertexAttrib3fv(int indx, Float32List values) native "WebGLRenderingContext_vertexAttrib3fv_Callback";
 
   @DomName('WebGLRenderingContext.vertexAttrib4f')
   @DocsEditable
@@ -2030,7 +2030,7 @@
 
   @DomName('WebGLRenderingContext.vertexAttrib4fv')
   @DocsEditable
-  void vertexAttrib4fv(int indx, List<double> values) native "WebGLRenderingContext_vertexAttrib4fv_Callback";
+  void vertexAttrib4fv(int indx, Float32List values) native "WebGLRenderingContext_vertexAttrib4fv_Callback";
 
   @DomName('WebGLRenderingContext.vertexAttribPointer')
   @DocsEditable
diff --git a/sdk/lib/web_sql/dart2js/web_sql_dart2js.dart b/sdk/lib/web_sql/dart2js/web_sql_dart2js.dart
index cc7f88b..851be05 100644
--- a/sdk/lib/web_sql/dart2js/web_sql_dart2js.dart
+++ b/sdk/lib/web_sql/dart2js/web_sql_dart2js.dart
@@ -193,7 +193,7 @@
 
 @DocsEditable
 @DomName('SQLResultSetRowList')
-class SqlResultSetRowList implements JavaScriptIndexingBehavior, List<Map> native "SQLResultSetRowList" {
+class SqlResultSetRowList extends Object with ListMixin<Map>, ImmutableListMixin<Map> implements JavaScriptIndexingBehavior, List<Map> native "SQLResultSetRowList" {
 
   @DomName('SQLResultSetRowList.length')
   @DocsEditable
@@ -207,196 +207,11 @@
   // -- start List<Map> mixins.
   // Map is the element type.
 
-  // From Iterable<Map>:
 
-  Iterator<Map> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<Map>(this);
-  }
-
-  Map reduce(Map combine(Map value, Map element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, Map element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(Map element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(Map element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(Map element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<Map> where(bool f(Map element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(Map element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(Map element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(Map element)) => IterableMixinWorkaround.any(this, f);
-
-  List<Map> toList({ bool growable: true }) =>
-      new List<Map>.from(this, growable: growable);
-
-  Set<Map> toSet() => new Set<Map>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<Map> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<Map> takeWhile(bool test(Map value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<Map> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<Map> skipWhile(bool test(Map value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  Map firstWhere(bool test(Map value), { Map orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  Map lastWhere(bool test(Map value), {Map orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  Map singleWhere(bool test(Map value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  Map elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<Map>:
-
-  void add(Map value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<Map> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<Map>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<Map> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(Map a, Map b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(Map element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(Map element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  Map get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  Map get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  Map get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, Map element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<Map> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<Map> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Map removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  Map removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(Map element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(Map element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<Map> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<Map> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [Map fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<Map> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<Map> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <Map>[]);
-  }
-
-  Map<int, Map> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<Map> mixins.
 
   @DomName('SQLResultSetRowList.item')
diff --git a/sdk/lib/web_sql/dartium/web_sql_dartium.dart b/sdk/lib/web_sql/dartium/web_sql_dartium.dart
index 656c3a1..c7c3bc0 100644
--- a/sdk/lib/web_sql/dartium/web_sql_dartium.dart
+++ b/sdk/lib/web_sql/dartium/web_sql_dartium.dart
@@ -210,7 +210,7 @@
 
 @DocsEditable
 @DomName('SQLResultSetRowList')
-class SqlResultSetRowList extends NativeFieldWrapperClass1 implements List<Map> {
+class SqlResultSetRowList extends NativeFieldWrapperClass1 with ListMixin<Map>, ImmutableListMixin<Map> implements List<Map> {
   SqlResultSetRowList.internal();
 
   @DomName('SQLResultSetRowList.length')
@@ -225,196 +225,11 @@
   // -- start List<Map> mixins.
   // Map is the element type.
 
-  // From Iterable<Map>:
 
-  Iterator<Map> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<Map>(this);
-  }
-
-  Map reduce(Map combine(Map value, Map element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
-
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, Map element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-  bool contains(Map element) => IterableMixinWorkaround.contains(this, element);
-
-  void forEach(void f(Map element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f(Map element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<Map> where(bool f(Map element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f(Map element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f(Map element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f(Map element)) => IterableMixinWorkaround.any(this, f);
-
-  List<Map> toList({ bool growable: true }) =>
-      new List<Map>.from(this, growable: growable);
-
-  Set<Map> toSet() => new Set<Map>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<Map> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<Map> takeWhile(bool test(Map value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<Map> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<Map> skipWhile(bool test(Map value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  Map firstWhere(bool test(Map value), { Map orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  Map lastWhere(bool test(Map value), {Map orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  Map singleWhere(bool test(Map value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  Map elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<Map>:
-
-  void add(Map value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<Map> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<Map>:
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-
-  Iterable<Map> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare(Map a, Map b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf(Map element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf(Map element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  Map get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  Map get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  Map get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, Map element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<Map> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<Map> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Map removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  Map removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test(Map element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test(Map element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<Map> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<Map> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [Map fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<Map> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<Map> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <Map>[]);
-  }
-
-  Map<int, Map> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<Map> mixins.
 
   @DomName('SQLResultSetRowList.item')
diff --git a/tests/co19/co19-analyzer.status b/tests/co19/co19-analyzer.status
index 4941e73..d0e06b3 100644
--- a/tests/co19/co19-analyzer.status
+++ b/tests/co19/co19-analyzer.status
@@ -60,7 +60,6 @@
 Language/07_Classes/3_Setters_A03_t06: fail
 Language/07_Classes/3_Setters_A03_t08: fail
 Language/07_Classes/4_Abstract_Instance_Members_A04_t06: fail
-Language/07_Classes/6_Constructors/1_Generative_Constructors_A01_t02: fail
 Language/07_Classes/6_Constructors/1_Generative_Constructors_A01_t04: fail
 Language/07_Classes/6_Constructors/1_Generative_Constructors_A03_t03: fail
 Language/07_Classes/6_Constructors/1_Generative_Constructors_A03_t04: fail
@@ -69,18 +68,12 @@
 Language/07_Classes/6_Constructors/1_Generative_Constructors_A03_t07: fail
 Language/07_Classes/6_Constructors/1_Generative_Constructors_A03_t08: fail
 Language/07_Classes/6_Constructors/1_Generative_Constructors_A03_t11: fail
-Language/07_Classes/6_Constructors/1_Generative_Constructors_A04_t13: fail
 Language/07_Classes/6_Constructors/1_Generative_Constructors_A04_t15: fail
-Language/07_Classes/6_Constructors/1_Generative_Constructors_A05_t01: fail
-Language/07_Classes/6_Constructors/1_Generative_Constructors_A05_t02: fail
-Language/07_Classes/6_Constructors/1_Generative_Constructors_A05_t03: fail
 Language/07_Classes/6_Constructors/1_Generative_Constructors_A09_t01: fail
 Language/07_Classes/6_Constructors/1_Generative_Constructors_A11_t04: fail
-Language/07_Classes/6_Constructors/1_Generative_Constructors_A11_t05: fail
 Language/07_Classes/6_Constructors/1_Generative_Constructors_A11_t06: fail
 Language/07_Classes/6_Constructors/1_Generative_Constructors_A11_t07: fail
 Language/07_Classes/6_Constructors/1_Generative_Constructors_A11_t09: fail
-Language/07_Classes/6_Constructors/1_Generative_Constructors_A11_t12: fail
 Language/07_Classes/6_Constructors/1_Generative_Constructors_A14_t01: fail
 Language/07_Classes/6_Constructors/1_Generative_Constructors_A14_t02: fail
 Language/07_Classes/6_Constructors/1_Generative_Constructors_A14_t03: fail
@@ -92,8 +85,6 @@
 Language/07_Classes/6_Constructors/1_Generative_Constructors_A16_t02: fail
 Language/07_Classes/6_Constructors/1_Generative_Constructors_A16_t07: fail
 Language/07_Classes/6_Constructors/2_Factories_A01_t05: fail
-Language/07_Classes/6_Constructors/2_Factories_A02_t01: fail
-Language/07_Classes/6_Constructors/2_Factories_A02_t02: fail
 Language/07_Classes/6_Constructors/2_Factories_A02_t03: fail
 Language/07_Classes/6_Constructors/2_Factories_A02_t04: fail
 Language/07_Classes/6_Constructors/2_Factories_A02_t05: fail
@@ -104,7 +95,6 @@
 Language/07_Classes/6_Constructors/3_Constant_Constructors_A05_t01: fail
 Language/07_Classes/6_Constructors/3_Constant_Constructors_A05_t02: fail
 Language/07_Classes/6_Constructors/3_Constant_Constructors_A05_t03: fail
-Language/07_Classes/6_Constructors_A02_t01: fail
 Language/07_Classes/7_Static_Methods_A01_t01: fail
 Language/07_Classes/9_Superclasses_A03_t01: fail
 Language/07_Classes/9_Superclasses_A03_t02: fail
@@ -118,6 +108,7 @@
 Language/11_Expressions/01_Constants_A11_t03: fail
 Language/11_Expressions/01_Constants_A11_t04: fail
 Language/11_Expressions/01_Constants_A15_t07: fail
+Language/11_Expressions/01_Constants_A15_t08: fail
 Language/11_Expressions/01_Constants_A16_t01: fail
 Language/11_Expressions/01_Constants_A16_t02: fail
 Language/11_Expressions/01_Constants_A16_t03: fail
@@ -144,23 +135,13 @@
 Language/11_Expressions/06_Lists_A01_t03: fail
 Language/11_Expressions/06_Lists_A01_t04: fail
 Language/11_Expressions/06_Lists_A03_t01: fail
-Language/11_Expressions/07_Maps_A01_t01: fail
 Language/11_Expressions/07_Maps_A02_t02: fail
-Language/11_Expressions/07_Maps_A07_t03: fail
 Language/11_Expressions/08_Throw_A06_t01: fail
 Language/11_Expressions/08_Throw_A06_t02: fail
 Language/11_Expressions/08_Throw_A06_t03: fail
 Language/11_Expressions/08_Throw_A06_t04: fail
 Language/11_Expressions/08_Throw_A06_t05: fail
 Language/11_Expressions/08_Throw_A06_t06: fail
-Language/11_Expressions/10_This_A01_t05: fail
-Language/11_Expressions/10_This_A03_t01: fail
-Language/11_Expressions/10_This_A03_t03: fail
-Language/11_Expressions/10_This_A03_t04: fail
-Language/11_Expressions/10_This_A03_t05: fail
-Language/11_Expressions/10_This_A03_t06: fail
-Language/11_Expressions/10_This_A03_t07: fail
-Language/11_Expressions/10_This_A03_t08: fail
 Language/11_Expressions/11_Instance_Creation/1_New_A01_t04: fail
 Language/11_Expressions/11_Instance_Creation/2_Const_A02_t01: fail
 Language/11_Expressions/11_Instance_Creation/2_Const_A02_t02: fail
@@ -187,7 +168,6 @@
 Language/11_Expressions/15_Method_Invocation/4_Super_Invocation_A01_t05: fail
 Language/11_Expressions/15_Method_Invocation/4_Super_Invocation_A05_t01: fail
 Language/11_Expressions/15_Method_Invocation/4_Super_Invocation_A05_t02: fail
-Language/11_Expressions/15_Method_Invocation/4_Super_Invocation_A05_t03: fail
 Language/11_Expressions/15_Method_Invocation/4_Super_Invocation_A05_t04: fail
 Language/11_Expressions/15_Method_Invocation/4_Super_Invocation_A05_t05: fail
 Language/11_Expressions/15_Method_Invocation/4_Super_Invocation_A05_t06: fail
@@ -285,33 +265,12 @@
 Language/12_Statements/07_While_A01_t04: fail
 Language/12_Statements/08_Do_A01_t07: fail
 Language/12_Statements/09_Switch_A01_t06: fail
+Language/12_Statements/09_Switch_A04_t01: fail
 Language/12_Statements/15_Assert_A01_t03: fail
 Language/13_Libraries_and_Scripts/13_Libraries_and_Scripts_A03_t17: fail
 Language/13_Libraries_and_Scripts/1_Imports_A01_t46: fail
 Language/13_Libraries_and_Scripts/1_Imports_A02_t12: fail
 Language/13_Libraries_and_Scripts/1_Imports_A02_t15: fail
-Language/13_Libraries_and_Scripts/1_Imports_A03_t02: fail
-Language/13_Libraries_and_Scripts/1_Imports_A03_t05: fail
-Language/13_Libraries_and_Scripts/1_Imports_A03_t07: fail
-Language/13_Libraries_and_Scripts/1_Imports_A03_t08: fail
-Language/13_Libraries_and_Scripts/1_Imports_A03_t09: fail
-Language/13_Libraries_and_Scripts/1_Imports_A03_t10: fail
-Language/13_Libraries_and_Scripts/1_Imports_A03_t22: fail
-Language/13_Libraries_and_Scripts/1_Imports_A03_t25: fail
-Language/13_Libraries_and_Scripts/1_Imports_A03_t27: fail
-Language/13_Libraries_and_Scripts/1_Imports_A03_t28: fail
-Language/13_Libraries_and_Scripts/1_Imports_A03_t29: fail
-Language/13_Libraries_and_Scripts/1_Imports_A03_t30: fail
-Language/13_Libraries_and_Scripts/1_Imports_A03_t42: fail
-Language/13_Libraries_and_Scripts/1_Imports_A03_t45: fail
-Language/13_Libraries_and_Scripts/1_Imports_A03_t48: fail
-Language/13_Libraries_and_Scripts/1_Imports_A03_t49: fail
-Language/13_Libraries_and_Scripts/1_Imports_A03_t50: fail
-Language/13_Libraries_and_Scripts/1_Imports_A03_t62: fail
-Language/13_Libraries_and_Scripts/1_Imports_A03_t65: fail
-Language/13_Libraries_and_Scripts/1_Imports_A03_t68: fail
-Language/13_Libraries_and_Scripts/1_Imports_A03_t69: fail
-Language/13_Libraries_and_Scripts/1_Imports_A03_t70: fail
 Language/13_Libraries_and_Scripts/1_Imports_A04_t03: fail
 Language/13_Libraries_and_Scripts/1_Imports_A05_t01: fail
 Language/13_Libraries_and_Scripts/2_Exports_A04_t02: fail
diff --git a/tests/compiler/dart2js/analyze_api_test.dart b/tests/compiler/dart2js/analyze_api_test.dart
index a70de8b..582f8aa 100644
--- a/tests/compiler/dart2js/analyze_api_test.dart
+++ b/tests/compiler/dart2js/analyze_api_test.dart
@@ -26,7 +26,9 @@
  */
 // TODO(johnniwinther): Support canonical URIs as keys and message kinds as
 // values.
-const Map<String,List<String>> WHITE_LIST = const { };
+const Map<String,List<String>> WHITE_LIST = const {
+  'html_dart2js.dart': const ['Warning: Using "new Symbol"'], // Issue 10565.
+};
 
 class CollectingDiagnosticHandler extends FormattingDiagnosticHandler {
   bool hasWarnings = false;
diff --git a/tests/compiler/dart2js/dead_bailout_target_test.dart b/tests/compiler/dart2js/dead_bailout_target_test.dart
index 760da9c..5e7cb91 100644
--- a/tests/compiler/dart2js/dead_bailout_target_test.dart
+++ b/tests/compiler/dart2js/dead_bailout_target_test.dart
@@ -60,8 +60,7 @@
 
   // Check that the bailout method has a case 2 for the state, which
   // is the second bailout in foo.
-  RegExp state = new RegExp('case 2:');
-  checkNumberOfMatches(state.allMatches(generated).iterator, 1);
+  Expect.isTrue(generated.contains('case 2:'));
 
   // Finally, make sure that the reason foo does not contain
   // 'getInterceptor' is not because the compiler renamed it.
diff --git a/tests/html/binding_syntax_test.dart b/tests/html/binding_syntax_test.dart
new file mode 100644
index 0000000..ce1c847
--- /dev/null
+++ b/tests/html/binding_syntax_test.dart
@@ -0,0 +1,146 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library binding_syntax_test;
+
+import 'dart:async';
+import 'dart:html';
+import 'package:mdv_observe/mdv_observe.dart';
+import 'package:unittest/html_config.dart';
+import 'package:unittest/unittest.dart';
+import 'mdv_observe_utils.dart';
+
+// Note: this file ported from
+// https://github.com/toolkitchen/mdv/blob/master/tests/syntax.js
+
+main() {
+  useHtmlConfiguration();
+  group('Syntax', syntaxTests);
+}
+
+syntaxTests() {
+  var testDiv;
+
+  setUp(() {
+    document.body.nodes.add(testDiv = new DivElement());
+  });
+
+  tearDown(() {
+    testDiv.remove();
+    testDiv = null;
+  });
+
+  createTestHtml(s) {
+    var div = new DivElement();
+    div.innerHtml = s;
+    testDiv.nodes.add(div);
+
+    for (var node in div.queryAll('*')) {
+      if (node.isTemplate) TemplateElement.decorate(node);
+    }
+
+    return div;
+  }
+
+  recursivelySetTemplateModel(element, model) {
+    for (var node in element.queryAll('*')) {
+      if (node.isTemplate) node.model = model;
+    }
+  }
+
+  test('Registration', () {
+    var model = toSymbolMap({'foo': 'bar'});
+
+    var testSyntax = new TestBindingSyntax();
+    TemplateElement.syntax['Test'] = testSyntax;
+
+    var div = createTestHtml(
+        '<template bind syntax="Test">{{ foo }}' +
+        '<template bind>{{ foo }}</template></template>');
+    recursivelySetTemplateModel(div, model);
+    deliverChangeRecords();
+    expect(div.nodes.length, 4);
+    expect(div.nodes.last.text, 'bar');
+    expect(div.nodes[2].tagName, 'TEMPLATE');
+    expect(div.nodes[2].attributes['syntax'], 'Test');
+
+    expect(testSyntax.log, [
+      [model, 'foo', 'text', null],
+      [model, '', 'bind', 'TEMPLATE'],
+      [model, 'foo', 'text', null],
+    ]);
+
+    TemplateElement.syntax.remove('Test');
+  });
+
+  test('Basic', () {
+    var model = toSymbolMap({'foo': 2, 'bar': 4});
+
+    TemplateElement.syntax['2x'] = new TimesTwoSyntax();
+
+    var div = createTestHtml(
+        '<template bind syntax="2x">'
+        '{{ foo }} + {{ 2x: bar }} + {{ 4x: bar }}</template>');
+    recursivelySetTemplateModel(div, model);
+    deliverChangeRecords();
+    expect(div.nodes.length, 2);
+    expect(div.nodes.last.text, '2 + 8 + ');
+
+    model[const Symbol('foo')] = 4;
+    model[const Symbol('bar')] = 8;
+    deliverChangeRecords();
+    expect(div.nodes.last.text, '4 + 16 + ');
+
+    TemplateElement.syntax.remove('2x');
+  });
+
+  test('Different Sub-Template Syntax', () {
+    var model = toSymbolMap({'foo': 'bar'});
+
+    TemplateElement.syntax['Test'] = new TestBindingSyntax();
+    TemplateElement.syntax['Test2'] = new TestBindingSyntax();
+
+    var div = createTestHtml(
+        '<template bind syntax="Test">{{ foo }}'
+        '<template bind syntax="Test2">{{ foo }}</template></template>');
+    recursivelySetTemplateModel(div, model);
+    deliverChangeRecords();
+    expect(div.nodes.length, 4);
+    expect(div.nodes.last.text, 'bar');
+    expect(div.nodes[2].tagName, 'TEMPLATE');
+    expect(div.nodes[2].attributes['syntax'], 'Test2');
+
+    var testLog = TemplateElement.syntax['Test'].log;
+    var test2Log = TemplateElement.syntax['Test2'].log;
+
+    expect(testLog, [
+      [model, 'foo', 'text', null],
+      [model, '', 'bind', 'TEMPLATE']
+    ]);
+
+    expect(test2Log, [[model, 'foo', 'text', null]]);
+
+    TemplateElement.syntax.remove('Test');
+    TemplateElement.syntax.remove('Test2');
+  });
+}
+
+class TestBindingSyntax extends CustomBindingSyntax {
+  var log = [];
+
+  getBinding(model, String path, String name, Node node) {
+    log.add([model, path, name, node is Element ? node.tagName : null]);
+  }
+}
+
+class TimesTwoSyntax extends CustomBindingSyntax {
+  getBinding(model, path, name, node) {
+    path = path.trim();
+    if (!path.startsWith('2x:')) return null;
+
+    path = path.substring(3);
+    return new CompoundBinding((values) => values['value'] * 2)
+        ..bind('value', model, path);
+  }
+}
diff --git a/tests/html/element_bindings_test.dart b/tests/html/element_bindings_test.dart
new file mode 100644
index 0000000..e452e0b
--- /dev/null
+++ b/tests/html/element_bindings_test.dart
@@ -0,0 +1,344 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library node_bindings_test;
+
+import 'dart:async';
+import 'dart:html';
+import 'package:mdv_observe/mdv_observe.dart';
+import 'package:unittest/html_config.dart';
+import 'package:unittest/unittest.dart';
+import 'mdv_observe_utils.dart';
+
+// Note: this file ported from
+// https://github.com/toolkitchen/mdv/blob/master/tests/element_bindings.js
+
+main() {
+  useHtmlConfiguration();
+  group('Element Bindings', elementBindingTests);
+}
+
+sym(x) => new Symbol(x);
+
+observePath(obj, path) => new PathObserver(obj, path);
+
+elementBindingTests() {
+  var testDiv;
+
+  setUp(() {
+    document.body.nodes.add(testDiv = new DivElement());
+  });
+
+  tearDown(() {
+    testDiv.remove();
+    testDiv = null;
+  });
+
+  dispatchEvent(type, target) {
+    target.dispatchEvent(new Event(type, cancelable: false));
+  }
+
+  test('Text', () {
+    var template = new Element.html('<template bind>{{a}} and {{b}}');
+    testDiv.nodes.add(template);
+    var model = toSymbolMap({'a': 1, 'b': 2});
+    template.model = model;
+    deliverChangeRecords();
+    var text = testDiv.nodes[1];
+    expect(text.text, '1 and 2');
+
+    model[sym('a')] = 3;
+    deliverChangeRecords();
+    expect(text.text, '3 and 2');
+  });
+
+  test('SimpleBinding', () {
+    var el = new DivElement();
+    var model = toSymbolMap({'a': '1'});
+    el.bind('foo', model, 'a');
+    deliverChangeRecords();
+    expect(el.attributes['foo'], '1');
+
+    model[sym('a')] = '2';
+    deliverChangeRecords();
+    expect(el.attributes['foo'], '2');
+
+    model[sym('a')] = 232.2;
+    deliverChangeRecords();
+    expect(el.attributes['foo'], '232.2');
+
+    model[sym('a')] = 232;
+    deliverChangeRecords();
+    expect(el.attributes['foo'], '232');
+
+    model[sym('a')] = null;
+    deliverChangeRecords();
+    expect(el.attributes['foo'], '');
+  });
+
+  test('SimpleBindingWithDashes', () {
+    var el = new DivElement();
+    var model = toSymbolMap({'a': '1'});
+    el.bind('foo-bar', model, 'a');
+    deliverChangeRecords();
+    expect(el.attributes['foo-bar'], '1');
+
+    model[sym('a')] = '2';
+    deliverChangeRecords();
+    expect(el.attributes['foo-bar'], '2');
+  });
+
+  test('SimpleBindingWithComment', () {
+    var el = new DivElement();
+    el.innerHtml = '<!-- Comment -->';
+    var model = toSymbolMap({'a': '1'});
+    el.bind('foo-bar', model, 'a');
+    deliverChangeRecords();
+    expect(el.attributes['foo-bar'], '1');
+
+    model[sym('a')] = '2';
+    deliverChangeRecords();
+    expect(el.attributes['foo-bar'], '2');
+  });
+
+  test('PlaceHolderBindingText', () {
+    var model = toSymbolMap({
+      'adj': 'cruel',
+      'noun': 'world'
+    });
+
+    var el = new DivElement();
+    el.text = 'dummy';
+    el.nodes.first.text = 'Hello {{ adj }} {{noun}}!';
+    var template = new Element.html('<template bind>');
+    template.content.nodes.add(el);
+    testDiv.nodes.add(template);
+    template.model = model;
+
+    deliverChangeRecords();
+    el = testDiv.nodes[1].nodes.first;
+    expect(el.text, 'Hello cruel world!');
+
+    model[sym('adj')] = 'happy';
+    deliverChangeRecords();
+    expect(el.text, 'Hello happy world!');
+  });
+
+  test('InputElementTextBinding', () {
+    var model = toSymbolMap({'val': 'ping'});
+
+    var el = new InputElement();
+    el.bind('value', model, 'val');
+    deliverChangeRecords();
+    expect(el.value, 'ping');
+
+    el.value = 'pong';
+    dispatchEvent('input', el);
+    expect(model[sym('val')], 'pong');
+
+    // Try a deep path.
+    model = toSymbolMap({'a': {'b': {'c': 'ping'}}});
+
+    el.bind('value', model, 'a.b.c');
+    deliverChangeRecords();
+    expect(el.value, 'ping');
+
+    el.value = 'pong';
+    dispatchEvent('input', el);
+    expect(observePath(model, 'a.b.c').value, 'pong');
+
+    // Start with the model property being absent.
+    model[sym('a')][sym('b')].remove(sym('c'));
+    deliverChangeRecords();
+    expect(el.value, '');
+
+    el.value = 'pong';
+    dispatchEvent('input', el);
+    expect(observePath(model, 'a.b.c').value, 'pong');
+    deliverChangeRecords();
+
+    // Model property unreachable (and unsettable).
+    model[sym('a')].remove(sym('b'));
+    deliverChangeRecords();
+    expect(el.value, '');
+
+    el.value = 'pong';
+    dispatchEvent('input', el);
+    expect(observePath(model, 'a.b.c').value, null);
+  });
+
+  test('InputElementCheckbox', () {
+    var model = toSymbolMap({'val': true});
+
+    var el = new InputElement();
+    testDiv.nodes.add(el);
+    el.type = 'checkbox';
+    el.bind('checked', model, 'val');
+    deliverChangeRecords();
+    expect(el.checked, true);
+
+    model[sym('val')] = false;
+    deliverChangeRecords();
+    expect(el.checked, false);
+
+    el.click();
+    expect(model[sym('val')], true);
+
+    el.click();
+    expect(model[sym('val')], false);
+  });
+
+  test('InputElementRadio', () {
+    var model = toSymbolMap({'val1': true, 'val2': false, 'val3': false,
+        'val4': true});
+    var RADIO_GROUP_NAME = 'test';
+
+    var container = testDiv;
+
+    var el1 = new InputElement();
+    testDiv.nodes.add(el1);
+    el1.type = 'radio';
+    el1.name = RADIO_GROUP_NAME;
+    el1.bind('checked', model, 'val1');
+
+    var el2 = new InputElement();
+    testDiv.nodes.add(el2);
+    el2.type = 'radio';
+    el2.name = RADIO_GROUP_NAME;
+    el2.bind('checked', model, 'val2');
+
+    var el3 = new InputElement();
+    testDiv.nodes.add(el3);
+    el3.type = 'radio';
+    el3.name = RADIO_GROUP_NAME;
+    el3.bind('checked', model, 'val3');
+
+    var el4 = new InputElement();
+    testDiv.nodes.add(el4);
+    el4.type = 'radio';
+    el4.name = 'othergroup';
+    el4.bind('checked', model, 'val4');
+
+    deliverChangeRecords();
+    expect(el1.checked, true);
+    expect(el2.checked, false);
+    expect(el3.checked, false);
+    expect(el4.checked, true);
+
+    model[sym('val1')] = false;
+    model[sym('val2')] = true;
+    deliverChangeRecords();
+    expect(el1.checked, false);
+    expect(el2.checked, true);
+    expect(el3.checked, false);
+    expect(el4.checked, true);
+
+    el1.checked = true;
+    dispatchEvent('change', el1);
+    expect(model[sym('val1')], true);
+    expect(model[sym('val2')], false);
+    expect(model[sym('val3')], false);
+    expect(model[sym('val4')], true);
+
+    el3.checked = true;
+    dispatchEvent('change', el3);
+    expect(model[sym('val1')], false);
+    expect(model[sym('val2')], false);
+    expect(model[sym('val3')], true);
+    expect(model[sym('val4')], true);
+  });
+
+  test('InputElementRadioMultipleForms', () {
+    var model = toSymbolMap({'val1': true, 'val2': false, 'val3': false,
+        'val4': true});
+    var RADIO_GROUP_NAME = 'test';
+
+    var form1 = new FormElement();
+    testDiv.nodes.add(form1);
+    var form2 = new FormElement();
+    testDiv.nodes.add(form2);
+
+    var el1 = new InputElement();
+    form1.nodes.add(el1);
+    el1.type = 'radio';
+    el1.name = RADIO_GROUP_NAME;
+    el1.bind('checked', model, 'val1');
+
+    var el2 = new InputElement();
+    form1.nodes.add(el2);
+    el2.type = 'radio';
+    el2.name = RADIO_GROUP_NAME;
+    el2.bind('checked', model, 'val2');
+
+    var el3 = new InputElement();
+    form2.nodes.add(el3);
+    el3.type = 'radio';
+    el3.name = RADIO_GROUP_NAME;
+    el3.bind('checked', model, 'val3');
+
+    var el4 = new InputElement();
+    form2.nodes.add(el4);
+    el4.type = 'radio';
+    el4.name = RADIO_GROUP_NAME;
+    el4.bind('checked', model, 'val4');
+
+    deliverChangeRecords();
+    expect(el1.checked, true);
+    expect(el2.checked, false);
+    expect(el3.checked, false);
+    expect(el4.checked, true);
+
+    el2.checked = true;
+    dispatchEvent('change', el2);
+    expect(model[sym('val1')], false);
+    expect(model[sym('val2')], true);
+
+    // Radio buttons in form2 should be unaffected
+    expect(model[sym('val3')], false);
+    expect(model[sym('val4')], true);
+
+    el3.checked = true;
+    dispatchEvent('change', el3);
+    expect(model[sym('val3')], true);
+    expect(model[sym('val4')], false);
+
+    // Radio buttons in form1 should be unaffected
+    expect(model[sym('val1')], false);
+    expect(model[sym('val2')], true);
+  });
+
+  test('BindToChecked', () {
+    var div = new DivElement();
+    testDiv.nodes.add(div);
+    var child = new DivElement();
+    div.nodes.add(child);
+    var input = new InputElement();
+    child.nodes.add(input);
+    input.type = 'checkbox';
+
+    var model = toSymbolMap({'a': {'b': false}});
+    input.bind('checked', model, 'a.b');
+
+    input.click();
+    expect(model[sym('a')][sym('b')], true);
+
+    input.click();
+    expect(model[sym('a')][sym('b')], false);
+  });
+
+  test('MultipleReferences', () {
+    var el = new DivElement();
+    var template = new Element.html('<template bind>');
+    template.content.nodes.add(el);
+    testDiv.nodes.add(template);
+
+    var model = toSymbolMap({'foo': 'bar'});
+    el.attributes['foo'] = '{{foo}} {{foo}}';
+    template.model = model;
+
+    deliverChangeRecords();
+    el = testDiv.nodes[1];
+    expect(el.attributes['foo'], 'bar bar');
+  });
+}
diff --git a/tests/html/element_types_test.dart b/tests/html/element_types_test.dart
index 6dee82e..f10713b 100644
--- a/tests/html/element_types_test.dart
+++ b/tests/html/element_types_test.dart
@@ -70,6 +70,12 @@
     });
   });
 
+  group('supported_template', () {
+    test('supported', () {
+      expect(TemplateElement.supported, true);
+    });
+  });
+
   group('supported_track', () {
     test('supported', () {
       expect(TrackElement.supported, true);
@@ -259,6 +265,10 @@
     test('table', () {
       expect((new TableElement()) is TableElement, true);
     });
+    test('template', () {
+      expect((new TemplateElement()) is TemplateElement,
+          TemplateElement.supported);
+    });
     test('textarea', () {
       expect((new TextAreaElement()) is TextAreaElement, true);
     });
diff --git a/tests/html/html.status b/tests/html/html.status
index 8d10943..e539b1d 100644
--- a/tests/html/html.status
+++ b/tests/html/html.status
@@ -12,8 +12,6 @@
 [ $compiler == none && ($runtime == drt || $runtime == dartium) ]
 # postMessage in dartium always transfers the typed array buffer, never a view
 postmessage_structured_test/typed_arrays: Fail
-async_test: Timeout # http://dartbug.com/10470
-dom_isolates_test: Timeout # http://dartbug.com/10470
 
 [ $compiler == none && $runtime == drt && $system == windows ]
 worker_test/functional: Pass, Crash # Issue 9929.
@@ -40,15 +38,12 @@
 [ $runtime == chrome ]
 touchevent_test/supported: Fail
 
-[ $runtime == chrome && ($system == windows || $system == macos)]
-audiocontext_test: Skip # Issue 9566
+[ $runtime == chrome || $runtime == drt ]
+audiocontext_test: Skip # Issue 9322
 
 [$runtime == drt || $runtime == dartium || $runtime == chrome]
 webgl_1_test: Pass, Fail # Issue 8219
 
-[ $runtime == drt ]
-audiocontext_test: Skip # Issue 9322
-
 [ $compiler == none && ($runtime == drt || $runtime == dartium) ]
 request_animation_frame_test: Skip   # drt hangs; requestAnimationFrame not implemented
 
@@ -91,6 +86,7 @@
 element_types_test/supported_meter: Fail
 element_types_test/supported_output: Fail
 element_types_test/supported_shadow: Fail
+element_types_test/supported_template: Fail
 fileapi_test/supported: Fail
 history_test/supported_HashChangeEvent: Fail
 indexeddb_1_test/supportsDatabaseNames: Fail
@@ -154,6 +150,7 @@
 element_types_test/supported_output: Fail
 element_types_test/supported_progress: Fail
 element_types_test/supported_shadow: Fail
+element_types_test/supported_template: Fail
 element_types_test/supported_track: Fail
 fileapi_test/supported: Fail
 form_data_test/supported: Fail
@@ -242,6 +239,7 @@
 element_types_test/supported_content: Fail
 element_types_test/supported_datalist: Fail
 element_types_test/supported_shadow: Fail
+element_types_test/supported_template: Fail
 fileapi_test/supported: Fail
 indexeddb_1_test/supported: Fail
 indexeddb_1_test/supportsDatabaseNames: Fail
@@ -291,6 +289,7 @@
 crypto_test/supported: Fail
 css_test/supportsPointConversions: Fail
 document_test/supports_cssCanvasContext: Fail
+element_types_test/supported_template: Fail
 indexeddb_1_test/supported: Fail
 indexeddb_1_test/supportsDatabaseNames: Fail
 mutationobserver_test/supported: Fail
@@ -320,6 +319,7 @@
 element_types_test/supported_keygen: Fail
 element_types_test/supported_object: Fail
 element_types_test/supported_shadow: Fail
+element_types_test/supported_template: Fail
 element_types_test/supported_track: Fail
 fileapi_test/supported: Fail
 indexeddb_1_test/supportsDatabaseNames: Fail
diff --git a/tests/html/mdv_observe_utils.dart b/tests/html/mdv_observe_utils.dart
new file mode 100644
index 0000000..56c3ceb
--- /dev/null
+++ b/tests/html/mdv_observe_utils.dart
@@ -0,0 +1,16 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library mdv_observe_utils;
+
+import 'package:mdv_observe/mdv_observe.dart';
+
+toSymbolMap(Map map) {
+  var result = new ObservableMap.linked();
+  map.forEach((key, value) {
+    if (value is Map) value = toSymbolMap(value);
+    result[new Symbol(key)] = value;
+  });
+  return result;
+}
diff --git a/tests/html/node_bindings_test.dart b/tests/html/node_bindings_test.dart
new file mode 100644
index 0000000..6787ec0
--- /dev/null
+++ b/tests/html/node_bindings_test.dart
@@ -0,0 +1,156 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library node_bindings_test;
+
+import 'dart:async';
+import 'dart:html';
+import 'package:mdv_observe/mdv_observe.dart';
+import 'package:unittest/html_config.dart';
+import 'package:unittest/unittest.dart';
+import 'mdv_observe_utils.dart';
+
+// Note: this file ported from
+// https://github.com/toolkitchen/mdv/blob/master/tests/node_bindings.js
+
+main() {
+  useHtmlConfiguration();
+  group('Node Bindings', nodeBindingTests);
+}
+
+sym(x) => new Symbol(x);
+
+nodeBindingTests() {
+  var testDiv;
+
+  setUp(() {
+    document.body.nodes.add(testDiv = new DivElement());
+  });
+
+  tearDown(() {
+    testDiv.remove();
+    testDiv = null;
+  });
+
+  dispatchEvent(type, target) {
+    target.dispatchEvent(new Event(type, cancelable: false));
+  }
+
+  test('Text', () {
+    var text = new Text('hi');
+    var model = toSymbolMap({'a': 1});
+    text.bind('text', model, 'a');
+    expect(text.text, '1');
+
+    model[sym('a')] = 2;
+    deliverChangeRecords();
+    expect(text.text, '2');
+
+    text.unbind('text');
+    model[sym('a')] = 3;
+    deliverChangeRecords();
+    expect(text.text, '2');
+
+    // TODO(rafaelw): Throw on binding to unavailable property?
+  });
+
+  test('Element', () {
+    var element = new DivElement();
+    var model = toSymbolMap({'a': 1, 'b': 2});
+    element.bind('hidden?', model, 'a');
+    element.bind('id', model, 'b');
+
+    expect(element.attributes, contains('hidden'));
+    expect(element.attributes['hidden'], '');
+    expect(element.id, '2');
+
+    model[sym('a')] = null;
+    deliverChangeRecords();
+    expect(element.attributes, isNot(contains('hidden')),
+        reason: 'null is false-y');
+
+    model[sym('a')] = false;
+    deliverChangeRecords();
+    expect(element.attributes, isNot(contains('hidden')));
+
+    model[sym('a')] = 'foo';
+    model[sym('b')] = 'x';
+    deliverChangeRecords();
+    expect(element.attributes, contains('hidden'));
+    expect(element.attributes['hidden'], '');
+    expect(element.id, 'x');
+  });
+
+  test('Text Input', () {
+    var input = new InputElement();
+    var model = toSymbolMap({'x': 42});
+    input.bind('value', model, 'x');
+    expect(input.value, '42');
+
+    model[sym('x')] = 'Hi';
+    expect(input.value, '42', reason: 'changes delivered async');
+    deliverChangeRecords();
+    expect(input.value, 'Hi');
+
+    input.value = 'changed';
+    dispatchEvent('input', input);
+    expect(model[sym('x')], 'changed');
+
+    input.unbind('value');
+
+    input.value = 'changed again';
+    dispatchEvent('input', input);
+    expect(model[sym('x')], 'changed');
+
+    input.bind('value', model, 'x');
+    model[sym('x')] = null;
+    deliverChangeRecords();
+    expect(input.value, '');
+  });
+
+  test('Radio Input', () {
+    var input = new InputElement();
+    input.type = 'radio';
+    var model = toSymbolMap({'x': true});
+    input.bind('checked', model, 'x');
+    expect(input.checked, true);
+
+    model[sym('x')] = false;
+    expect(input.checked, true);
+    deliverChangeRecords();
+    expect(input.checked, false,reason: 'model change should update checked');
+
+    input.checked = true;
+    dispatchEvent('change', input);
+    expect(model[sym('x')], true, reason: 'input.checked should set model');
+
+    input.unbind('checked');
+
+    input.checked = false;
+    dispatchEvent('change', input);
+    expect(model[sym('x')], true,
+        reason: 'disconnected binding should not fire');
+  });
+
+  test('Checkbox Input', () {
+    var input = new InputElement();
+    testDiv.nodes.add(input);
+    input.type = 'checkbox';
+    var model = toSymbolMap({'x': true});
+    input.bind('checked', model, 'x');
+    expect(input.checked, true);
+
+    model[sym('x')] = false;
+    expect(input.checked, true, reason: 'changes delivered async');
+    deliverChangeRecords();
+    expect(input.checked, false);
+
+    input.click();
+    expect(model[sym('x')], true);
+    deliverChangeRecords();
+
+    input.click();
+    expect(model[sym('x')], false);
+  });
+}
diff --git a/tests/html/node_model_test.dart b/tests/html/node_model_test.dart
deleted file mode 100644
index 0e1b23a..0000000
--- a/tests/html/node_model_test.dart
+++ /dev/null
@@ -1,192 +0,0 @@
-// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-library node_model_test;
-import 'dart:async';
-import 'dart:html';
-import '../../pkg/unittest/lib/unittest.dart';
-import '../../pkg/unittest/lib/html_config.dart';
-
-var stepDuration;
-Future get nextStep {
-  return new Future.delayed(stepDuration);
-}
-
-class ModelTracker {
-  Element element = new DivElement();
-  List models = [];
-
-  ModelTracker() {
-    element.onModelChanged.listen((node) {
-      models.add(node.model);
-    });
-  }
-
-  void append(ModelTracker other) {
-    element.append(other.element);
-  }
-
-  get model => element.model;
-  void set model(value) {
-    element.model = value;
-  }
-
-  void clearModel() {
-    element.clearModel();
-  }
-
-  void remove() {
-    element.remove();
-  }
-}
-
-main() {
-  useHtmlConfiguration();
-
-  if (MutationObserver.supported) {
-    stepDuration = new Duration();
-  } else {
-    // Need to step after the tree update notifications, but on IE9 these may
-    // get polyfilled to use setTimeout(0). So use a longer timer to try to
-    // get a later callback.
-    stepDuration = new Duration(milliseconds: 15);
-  }
-
-  test('basic top down', () {
-    var a = new DivElement();
-    var b = new SpanElement();
-    var c = new DivElement();
-    var d = new DivElement();
-    var e = new DivElement();
-    var model = {};
-    var model2 = {};
-    var model3 = {};
-
-    document.body.append(a);
-    a.append(b);
-
-    a.model = model;
-    expect(a.model, model);
-    expect(b.model, model);
-
-    b.append(c);
-
-    return nextStep.then((_) {
-      expect(c.model, model);
-
-      d.append(e);
-      c.append(d);
-
-      return nextStep;
-    }).then((_) {
-      expect(e.model, model);
-
-      b.remove();
-      return nextStep;
-    }).then((_) {
-      expect(b.model, isNull);
-      expect(e.model, isNull);
-
-      return nextStep;
-    }).then((_) {
-      a.append(b);
-      a.model = model2;
-
-      return nextStep;
-    }).then((_) {
-      expect(e.model, model2);
-
-      c.model = model3;
-      expect(c.model, model3);
-      expect(b.model, model2);
-      expect(e.model, model3);
-
-      return nextStep;
-    }).then((_) {
-      d.remove();
-      c.append(d);
-
-      return nextStep;
-    }).then((_) {
-      expect(d.model, model3);
-
-      c.clearModel();
-      expect(d.model, model2);
-
-      a.remove();
-      return nextStep;
-    });
-  });
-
-  test('changes', () {
-    var a = new ModelTracker();
-    var b = new ModelTracker();
-    var c = new ModelTracker();
-    var d = new ModelTracker();
-
-    var model = {};
-    var cModel = {};
-
-    document.body.append(a.element);
-
-    a.append(b);
-
-    return nextStep.then((_) {
-      expect(a.models, []);
-      expect(b.models.length, 0);
-
-      a.model = model;
-
-      expect(a.models, [model]);
-      expect(b.models, [model]);
-
-      b.append(c);
-      return nextStep;
-    }).then((_) {
-      expect(c.models, [model]);
-
-      c.append(d);
-      return nextStep;
-    }).then((_) {
-      c.model = cModel;
-      expect(b.models, [model]);
-      expect(c.models, [model, cModel]);
-      expect(d.models, [model, cModel]);
-
-      c.clearModel();
-      expect(c.models, [model, cModel, model]);
-      expect(d.models, [model, cModel, model]);
-
-      a.remove();
-      return nextStep;
-    });
-  });
-
-  test('bottom up', () {
-    var a = new ModelTracker();
-    var b = new ModelTracker();
-    var c = new ModelTracker();
-    var d = new ModelTracker();
-
-    var aModel = {};
-    var cModel = {};
-
-    c.append(d);
-    c.model = cModel;
-    b.append(c);
-    a.append(b);
-    a.model = aModel;
-    document.body.append(a.element);
-
-    return nextStep.then((_) {
-      expect(a.models, [aModel]);
-      expect(b.models, [aModel]);
-      expect(c.models, [cModel]);
-      expect(d.models, [cModel]);
-
-      a.remove();
-      return nextStep;
-    });
-  });
-}
diff --git a/tests/html/path_observer_test.dart b/tests/html/path_observer_test.dart
new file mode 100644
index 0000000..d6db89b
--- /dev/null
+++ b/tests/html/path_observer_test.dart
@@ -0,0 +1,255 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:html' show PathObserver;
+import 'package:unittest/html_config.dart';
+import 'package:mdv_observe/mdv_observe.dart';
+import 'package:unittest/unittest.dart';
+
+// This file contains code ported from:
+// https://github.com/rafaelw/ChangeSummary/blob/master/tests/test.js
+
+main() {
+  useHtmlConfiguration();
+  group('PathObserver', observePathTests);
+}
+
+observePath(obj, path) => new PathObserver(obj, path);
+
+sym(x) => new Symbol(x);
+
+toSymbolMap(Map map) {
+  var result = new ObservableMap.linked();
+  map.forEach((key, value) {
+    if (value is Map) value = toSymbolMap(value);
+    result[new Symbol(key)] = value;
+  });
+  return result;
+}
+
+observePathTests() {
+
+  test('Degenerate Values', () {
+    expect(observePath(null, '').value, null);
+    expect(observePath(123, '').value, 123);
+    expect(observePath(123, 'foo.bar.baz').value, null);
+
+    // shouldn't throw:
+    observePath(123, '').values.listen((_) {}).cancel();
+    observePath(null, '').value = null;
+    observePath(123, '').value = 42;
+    observePath(123, 'foo.bar.baz').value = 42;
+
+    var foo = {};
+    expect(observePath(foo, '').value, foo);
+
+    foo = new Object();
+    expect(observePath(foo, '').value, foo);
+
+    expect(observePath(foo, 'a/3!').value, null);
+  });
+
+  test('get value at path ObservableBox', () {
+    var obj = new ObservableBox(new ObservableBox(new ObservableBox(1)));
+
+    expect(observePath(obj, '').value, obj);
+    expect(observePath(obj, 'value').value, obj.value);
+    expect(observePath(obj, 'value.value').value, obj.value.value);
+    expect(observePath(obj, 'value.value.value').value, 1);
+
+    obj.value.value.value = 2;
+    expect(observePath(obj, 'value.value.value').value, 2);
+
+    obj.value.value = new ObservableBox(3);
+    expect(observePath(obj, 'value.value.value').value, 3);
+
+    obj.value = new ObservableBox(4);
+    expect(observePath(obj, 'value.value.value').value, null);
+    expect(observePath(obj, 'value.value').value, 4);
+  });
+
+
+  test('get value at path ObservableMap', () {
+    var obj = toSymbolMap({'a': {'b': {'c': 1}}});
+
+    expect(observePath(obj, '').value, obj);
+    expect(observePath(obj, 'a').value, obj[sym('a')]);
+    expect(observePath(obj, 'a.b').value, obj[sym('a')][sym('b')]);
+    expect(observePath(obj, 'a.b.c').value, 1);
+
+    obj[sym('a')][sym('b')][sym('c')] = 2;
+    expect(observePath(obj, 'a.b.c').value, 2);
+
+    obj[sym('a')][sym('b')] = toSymbolMap({'c': 3});
+    expect(observePath(obj, 'a.b.c').value, 3);
+
+    obj[sym('a')] = toSymbolMap({'b': 4});
+    expect(observePath(obj, 'a.b.c').value, null);
+    expect(observePath(obj, 'a.b').value, 4);
+  });
+
+  test('set value at path', () {
+    var obj = toSymbolMap({});
+    observePath(obj, 'foo').value = 3;
+    expect(obj[sym('foo')], 3);
+
+    var bar = toSymbolMap({ 'baz': 3 });
+    observePath(obj, 'bar').value = bar;
+    expect(obj[sym('bar')], bar);
+
+    observePath(obj, 'bar.baz.bat').value = 'not here';
+    expect(observePath(obj, 'bar.baz.bat').value, null);
+  });
+
+  test('set value back to same', () {
+    var obj = toSymbolMap({});
+    var path = observePath(obj, 'foo');
+    var values = [];
+    path.values.listen((v) { values.add(v); });
+
+    path.value = 3;
+    expect(obj[sym('foo')], 3);
+    expect(path.value, 3);
+
+    observePath(obj, 'foo').value = 2;
+    deliverChangeRecords();
+    expect(path.value, 2);
+    expect(observePath(obj, 'foo').value, 2);
+
+    observePath(obj, 'foo').value = 3;
+    deliverChangeRecords();
+    expect(path.value, 3);
+
+    deliverChangeRecords();
+    expect(values, [2, 3]);
+  });
+
+  test('Observe and Unobserve - Paths', () {
+    var arr = toSymbolMap({});
+
+    arr[sym('foo')] = 'bar';
+    var fooValues = [];
+    var fooPath = observePath(arr, 'foo');
+    var fooSub = fooPath.values.listen((v) {
+      fooValues.add(v);
+    });
+    arr[sym('foo')] = 'baz';
+    arr[sym('bat')] = 'bag';
+    var batValues = [];
+    var batPath = observePath(arr, 'bat');
+    var batSub = batPath.values.listen((v) {
+      batValues.add(v);
+    });
+
+    deliverChangeRecords();
+    expect(fooValues, ['baz']);
+    expect(batValues, []);
+
+    arr[sym('foo')] = 'bar';
+    fooSub.cancel();
+    arr[sym('bat')] = 'boo';
+    batSub.cancel();
+    arr[sym('bat')] = 'boot';
+
+    deliverChangeRecords();
+    expect(fooValues, ['baz']);
+    expect(batValues, []);
+  });
+
+  test('Path Value With Indices', () {
+    var model = toObservable([]);
+    observePath(model, '0').values.listen(expectAsync1((v) {
+      expect(v, 123);
+    }));
+    model.add(123);
+  });
+
+  test('Path Observation', () {
+    var model = new TestModel(const Symbol('a'),
+        new TestModel(const Symbol('b'),
+            new TestModel(const Symbol('c'), 'hello, world')));
+
+    var path = observePath(model, 'a.b.c');
+    var lastValue = null;
+    var sub = path.values.listen((v) { lastValue = v; });
+
+    model.value.value.value = 'hello, mom';
+
+    expect(lastValue, null);
+    deliverChangeRecords();
+    expect(lastValue, 'hello, mom');
+
+    model.value.value = new TestModel(const Symbol('c'), 'hello, dad');
+    deliverChangeRecords();
+    expect(lastValue, 'hello, dad');
+
+    model.value = new TestModel(const Symbol('b'),
+        new TestModel(const Symbol('c'), 'hello, you'));
+    deliverChangeRecords();
+    expect(lastValue, 'hello, you');
+
+    model.value.value = 1;
+    deliverChangeRecords();
+    expect(lastValue, null);
+
+    // Stop observing
+    sub.cancel();
+
+    model.value.value = new TestModel(const Symbol('c'),
+        'hello, back again -- but not observing');
+    deliverChangeRecords();
+    expect(lastValue, null);
+
+    // Resume observing
+    sub = path.values.listen((v) { lastValue = v; });
+
+    model.value.value.value = 'hello. Back for reals';
+    deliverChangeRecords();
+    expect(lastValue, 'hello. Back for reals');
+  });
+
+  test('observe map', () {
+    var model = toSymbolMap({'a': 1});
+    var path = observePath(model, 'a');
+
+    var values = [path.value];
+    var sub = path.values.listen((v) { values.add(v); });
+    expect(values, [1]);
+
+    model[sym('a')] = 2;
+    deliverChangeRecords();
+    expect(values, [1, 2]);
+
+    sub.cancel();
+    model[sym('a')] = 3;
+    deliverChangeRecords();
+    expect(values, [1, 2]);
+  });
+}
+
+class TestModel extends ObservableBase {
+  final Symbol fieldName;
+  var _value;
+
+  TestModel(this.fieldName, [initialValue]) : _value = initialValue;
+
+  get value => _value;
+
+  void set value(newValue) {
+    _value = notifyPropertyChange(fieldName, _value, newValue);
+  }
+
+  getValueWorkaround(key) {
+    if (key == fieldName) return value;
+    return null;
+  }
+  void setValueWorkaround(key, newValue) {
+    if (key == fieldName) value = newValue;
+  }
+
+  toString() => '#<$runtimeType $fieldName: $_value>';
+}
+
+_record(key, oldValue, newValue, [kind = ChangeRecord.FIELD]) =>
+    new ChangeRecord(key, oldValue, newValue, kind: kind);
diff --git a/tests/html/template_element_test.dart b/tests/html/template_element_test.dart
new file mode 100644
index 0000000..c34aa76
--- /dev/null
+++ b/tests/html/template_element_test.dart
@@ -0,0 +1,1503 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library template_element_test;
+
+import 'dart:async';
+import 'dart:collection';
+import 'dart:html';
+import 'dart:math' as math;
+import 'package:mdv_observe/mdv_observe.dart';
+import 'package:unittest/html_config.dart';
+import 'package:unittest/unittest.dart';
+import 'mdv_observe_utils.dart';
+
+// Note: this file ported from
+// https://github.com/toolkitchen/mdv/blob/master/tests/template_element.js
+// TODO(jmesserly): submit a small cleanup patch to original. I fixed some
+// cases where "div" and "t" were unintentionally using the JS global scope;
+// look for "assertNodesAre".
+
+main() {
+  useHtmlConfiguration();
+  group('Template Element', templateElementTests);
+}
+
+templateElementTests() {
+  var testDiv;
+
+  setUp(() {
+    document.body.nodes.add(testDiv = new DivElement());
+  });
+
+  tearDown(() {
+    testDiv.remove();
+    testDiv = null;
+  });
+
+  createTestHtml(s) {
+    var div = new DivElement();
+    div.innerHtml = s;
+    testDiv.nodes.add(div);
+
+    for (var node in div.queryAll('*')) {
+      if (node.isTemplate) TemplateElement.decorate(node);
+    }
+
+    return div;
+  }
+
+  createShadowTestHtml(s) {
+    var div = new DivElement();
+    var root = div.createShadowRoot();
+    root.innerHtml = s;
+    testDiv.nodes.add(div);
+
+    for (var node in root.queryAll('*')) {
+      if (node.isTemplate) TemplateElement.decorate(node);
+    }
+
+    return root;
+  }
+
+  recursivelySetTemplateModel(element, model) {
+    for (var node in element.queryAll('*')) {
+      if (node.isTemplate) node.model = model;
+    }
+  }
+
+  dispatchEvent(type, target) {
+    target.dispatchEvent(new Event(type, cancelable: false));
+  }
+
+  test('Template', () {
+    var div = createTestHtml('<template bind={{}}>text</template>');
+    recursivelySetTemplateModel(div, null);
+    deliverChangeRecords();
+    expect(div.nodes.length, 2);
+    expect(div.nodes.last.text, 'text');
+  });
+
+  test('Template-Empty Bind', () {
+    var div = createTestHtml('<template bind>text</template>');
+    recursivelySetTemplateModel(div, null);
+    deliverChangeRecords();
+    expect(div.nodes.length, 2);
+    expect(div.nodes.last.text, 'text');
+  });
+
+  test('TextTemplateWithNullStringBinding', () {
+    var div = createTestHtml('<template bind={{}}>a{{b}}c</template>');
+    var model = toSymbolMap({'b': 'B'});
+    recursivelySetTemplateModel(div, model);
+
+    deliverChanges(model);
+    expect(div.nodes.length, 2);
+    expect(div.nodes.last.text, 'aBc');
+
+    model[sym('b')] = 'b';
+    deliverChanges(model);
+    expect(div.nodes.last.text, 'abc');
+
+    model[sym('b')] = null;
+    deliverChanges(model);
+    expect(div.nodes.last.text, 'ac');
+
+    model = null;
+    deliverChanges(model);
+    // setting model isn't observable.
+    expect(div.nodes.last.text, 'ac');
+  });
+
+  test('TextTemplateWithBindingPath', () {
+    var div = createTestHtml(
+        '<template bind="{{ data }}">a{{b}}c</template>');
+    var model = toSymbolMap({ 'data': {'b': 'B'} });
+    recursivelySetTemplateModel(div, model);
+
+    deliverChanges(model);
+    expect(div.nodes.length, 2);
+    expect(div.nodes.last.text, 'aBc');
+
+    model[sym('data')][sym('b')] = 'b';
+    deliverChanges(model);
+    expect(div.nodes.last.text, 'abc');
+
+    model[sym('data')] = toSymbols({'b': 'X'});
+    deliverChanges(model);
+    expect(div.nodes.last.text, 'aXc');
+
+    model[sym('data')] = null;
+    deliverChanges(model);
+    expect(div.nodes.last.text, 'ac');
+  });
+
+  test('TextTemplateWithBindingAndConditional', () {
+    var div = createTestHtml(
+        '<template bind="{{}}" if="{{ d }}">a{{b}}c</template>');
+    var model = toSymbolMap({'b': 'B', 'd': 1});
+    recursivelySetTemplateModel(div, model);
+
+    deliverChanges(model);
+    expect(div.nodes.length, 2);
+    expect(div.nodes.last.text, 'aBc');
+
+    model[sym('b')] = 'b';
+    deliverChanges(model);
+    expect(div.nodes.last.text, 'abc');
+
+    // TODO(jmesserly): MDV set this to empty string and relies on JS conversion
+    // rules. Is that intended?
+    // See https://github.com/toolkitchen/mdv/issues/59
+    model[sym('d')] = null;
+    deliverChanges(model);
+    expect(div.nodes.length, 1);
+
+    model[sym('d')] = 'here';
+    model[sym('b')] = 'd';
+
+    deliverChanges(model);
+    expect(div.nodes.length, 2);
+    expect(div.nodes.last.text, 'adc');
+  });
+
+  test('TemplateWithTextBinding2', () {
+    var div = createTestHtml(
+        '<template bind="{{ b }}">a{{value}}c</template>');
+    expect(div.nodes.length, 1);
+    var model = toSymbolMap({'b': {'value': 'B'}});
+    recursivelySetTemplateModel(div, model);
+
+    deliverChanges(model);
+    expect(div.nodes.length, 2);
+    expect(div.nodes.last.text, 'aBc');
+
+    model[sym('b')] = toSymbols({'value': 'b'});
+    deliverChanges(model);
+    expect(div.nodes.last.text, 'abc');
+  });
+
+  test('TemplateWithAttributeBinding', () {
+    var div = createTestHtml(
+        '<template bind="{{}}">'
+        '<div foo="a{{b}}c"></div>'
+        '</template>');
+    var model = toSymbolMap({'b': 'B'});
+    recursivelySetTemplateModel(div, model);
+
+    deliverChanges(model);
+    expect(div.nodes.length, 2);
+    expect(div.nodes.last.attributes['foo'], 'aBc');
+
+    model[sym('b')] = 'b';
+    deliverChanges(model);
+    expect(div.nodes.last.attributes['foo'], 'abc');
+
+    model[sym('b')] = 'X';
+    deliverChanges(model);
+    expect(div.nodes.last.attributes['foo'], 'aXc');
+  });
+
+  test('TemplateWithConditionalBinding', () {
+    var div = createTestHtml(
+        '<template bind="{{}}">'
+        '<div foo?="{{b}}"></div>'
+        '</template>');
+    var model = toSymbolMap({'b': 'b'});
+    recursivelySetTemplateModel(div, model);
+
+    deliverChanges(model);
+    expect(div.nodes.length, 2);
+    expect(div.nodes.last.attributes['foo'], '');
+    expect(div.nodes.last.attributes, isNot(contains('foo?')));
+
+    model[sym('b')] = null;
+    deliverChanges(model);
+    expect(div.nodes.last.attributes, isNot(contains('foo')));
+  });
+
+  test('Repeat', () {
+    var div = createTestHtml(
+        '<template repeat="{{}}"">text</template>');
+
+    var model = toSymbols([0, 1, 2]);
+    recursivelySetTemplateModel(div, model);
+
+    deliverChanges(model);
+    expect(div.nodes.length, 4);
+
+    model.length = 1;
+    deliverChanges(model);
+    expect(div.nodes.length, 2);
+
+    model.addAll(toSymbols([3, 4]));
+    deliverChanges(model);
+    expect(div.nodes.length, 4);
+
+    model.removeRange(1, 2);
+    deliverChanges(model);
+    expect(div.nodes.length, 3);
+  });
+
+  test('Repeat-Empty', () {
+    var div = createTestHtml(
+        '<template repeat>text</template>');
+
+    var model = toSymbols([0, 1, 2]);
+    recursivelySetTemplateModel(div, model);
+
+    deliverChanges(model);
+    expect(div.nodes.length, 4);
+
+    model.length = 1;
+    deliverChanges(model);
+    expect(div.nodes.length, 2);
+
+    model.addAll(toSymbols([3, 4]));
+    deliverChanges(model);
+    expect(div.nodes.length, 4);
+
+    model.removeRange(1, 2);
+    deliverChanges(model);
+    expect(div.nodes.length, 3);
+  });
+
+  test('Removal from iteration needs to unbind', () {
+    var div = createTestHtml(
+        '<template repeat="{{}}"><a>{{v}}</a></template>');
+    var model = toSymbols([{'v': 0}, {'v': 1}, {'v': 2}, {'v': 3}, {'v': 4}]);
+    recursivelySetTemplateModel(div, model);
+    deliverChanges(model);
+
+    var nodes = div.nodes.skip(1).toList();
+    var vs = model.toList();
+
+    for (var i = 0; i < 5; i++) {
+      expect(nodes[i].text, '$i');
+    }
+
+    model.length = 3;
+    deliverChanges(model);
+    for (var i = 0; i < 5; i++) {
+      expect(nodes[i].text, '$i');
+    }
+
+    vs[3][sym('v')] = 33;
+    vs[4][sym('v')] = 44;
+    deliverChanges(model);
+    for (var i = 0; i < 5; i++) {
+      expect(nodes[i].text, '$i');
+    }
+  });
+
+  test('DOM Stability on Iteration', () {
+    var div = createTestHtml(
+        '<template repeat="{{}}">{{}}</template>');
+    var model = toSymbols([1, 2, 3, 4, 5]);
+    recursivelySetTemplateModel(div, model);
+
+    deliverChanges(model);
+
+    // Note: the node at index 0 is the <template>.
+    var nodes = div.nodes.toList();
+    expect(nodes.length, 6, reason: 'list has 5 items');
+
+    model.removeAt(0);
+    model.removeLast();
+
+    deliverChanges(model);
+    expect(div.nodes.length, 4, reason: 'list has 3 items');
+    expect(identical(div.nodes[1], nodes[2]), true, reason: '2 not removed');
+    expect(identical(div.nodes[2], nodes[3]), true, reason: '3 not removed');
+    expect(identical(div.nodes[3], nodes[4]), true, reason: '4 not removed');
+
+    model.insert(0, 5);
+    model[2] = 6;
+    model.add(7);
+
+    deliverChanges(model);
+
+    expect(div.nodes.length, 6, reason: 'list has 5 items');
+    expect(nodes.contains(div.nodes[1]), false, reason: '5 is a new node');
+    expect(identical(div.nodes[2], nodes[2]), true);
+    expect(nodes.contains(div.nodes[3]), false, reason: '6 is a new node');
+    expect(identical(div.nodes[4], nodes[4]), true);
+    expect(nodes.contains(div.nodes[5]), false, reason: '7 is a new node');
+
+    nodes = div.nodes.toList();
+
+    model.insert(2, 8);
+
+    deliverChanges(model);
+
+    expect(div.nodes.length, 7, reason: 'list has 6 items');
+    expect(identical(div.nodes[1], nodes[1]), true);
+    expect(identical(div.nodes[2], nodes[2]), true);
+    expect(nodes.contains(div.nodes[3]), false, reason: '8 is a new node');
+    expect(identical(div.nodes[4], nodes[3]), true);
+    expect(identical(div.nodes[5], nodes[4]), true);
+    expect(identical(div.nodes[6], nodes[5]), true);
+  });
+
+  test('Repeat2', () {
+    var div = createTestHtml(
+        '<template repeat="{{}}">{{value}}</template>');
+    expect(div.nodes.length, 1);
+
+    var model = toSymbols([
+      {'value': 0},
+      {'value': 1},
+      {'value': 2}
+    ]);
+    recursivelySetTemplateModel(div, model);
+
+    deliverChanges(model);
+    expect(div.nodes.length, 4);
+    expect(div.nodes[1].text, '0');
+    expect(div.nodes[2].text, '1');
+    expect(div.nodes[3].text, '2');
+
+    model[1][sym('value')] = 'One';
+    deliverChanges(model);
+    expect(div.nodes.length, 4);
+    expect(div.nodes[1].text, '0');
+    expect(div.nodes[2].text, 'One');
+    expect(div.nodes[3].text, '2');
+
+    model.replaceRange(0, 1, toSymbols([{'value': 'Zero'}]));
+    deliverChanges(model);
+    expect(div.nodes.length, 4);
+    expect(div.nodes[1].text, 'Zero');
+    expect(div.nodes[2].text, 'One');
+    expect(div.nodes[3].text, '2');
+  });
+
+  test('TemplateWithInputValue', () {
+    var div = createTestHtml(
+        '<template bind="{{}}">'
+        '<input value="{{x}}">'
+        '</template>');
+    var model = toSymbolMap({'x': 'hi'});
+    recursivelySetTemplateModel(div, model);
+
+    deliverChanges(model);
+    expect(div.nodes.length, 2);
+    expect(div.nodes.last.value, 'hi');
+
+    model[sym('x')] = 'bye';
+    expect(div.nodes.last.value, 'hi');
+    deliverChanges(model);
+    expect(div.nodes.last.value, 'bye');
+
+    div.nodes.last.value = 'hello';
+    dispatchEvent('input', div.nodes.last);
+    expect(model[sym('x')], 'hello');
+    deliverChanges(model);
+    expect(div.nodes.last.value, 'hello');
+  });
+
+//////////////////////////////////////////////////////////////////////////////
+
+  test('Decorated', () {
+    var div = createTestHtml(
+        '<template bind="{{ XX }}" id="t1">'
+          '<p>Crew member: {{name}}, Job title: {{title}}</p>'
+        '</template>'
+        '<template bind="{{ XY }}" id="t2" ref="t1"></template>');
+
+    var model = toSymbolMap({
+      'XX': {'name': 'Leela', 'title': 'Captain'},
+      'XY': {'name': 'Fry', 'title': 'Delivery boy'},
+      'XZ': {'name': 'Zoidberg', 'title': 'Doctor'}
+    });
+    recursivelySetTemplateModel(div, model);
+
+    deliverChanges(model);
+
+    var t1 = document.getElementById('t1');
+    var instance = t1.nextElementSibling;
+    expect(instance.text, 'Crew member: Leela, Job title: Captain');
+
+    var t2 = document.getElementById('t2');
+    instance = t2.nextElementSibling;
+    expect(instance.text, 'Crew member: Fry, Job title: Delivery boy');
+
+    expect(div.children.length, 4);
+    expect(div.nodes.length, 4);
+
+    expect(div.nodes[1].tagName, 'P');
+    expect(div.nodes[3].tagName, 'P');
+  });
+
+  test('DefaultStyles', () {
+    var t = new Element.tag('template');
+    TemplateElement.decorate(t);
+
+    document.body.nodes.add(t);
+    expect(t.getComputedStyle().display, 'none');
+
+    t.remove();
+  });
+
+
+  test('Bind', () {
+    var div = createTestHtml('<template bind="{{}}">Hi {{ name }}</template>');
+    var model = toSymbolMap({'name': 'Leela'});
+    recursivelySetTemplateModel(div, model);
+
+    deliverChanges(model);
+    expect(div.nodes[1].text, 'Hi Leela');
+  });
+
+  test('BindImperative', () {
+    var div = createTestHtml(
+        '<template>'
+          'Hi {{ name }}'
+        '</template>');
+    var t = div.nodes.first;
+
+    var model = toSymbolMap({'name': 'Leela'});
+    t.bind('bind', model, '');
+
+    deliverChanges(model);
+    expect(div.nodes[1].text, 'Hi Leela');
+  });
+
+  test('BindPlaceHolderHasNewLine', () {
+    var div = createTestHtml('<template bind="{{}}">Hi {{\nname\n}}</template>');
+    var model = toSymbolMap({'name': 'Leela'});
+    recursivelySetTemplateModel(div, model);
+
+    deliverChanges(model);
+    expect(div.nodes[1].text, 'Hi Leela');
+  });
+
+  test('BindWithRef', () {
+    var id = 't${new math.Random().nextDouble()}';
+    var div = createTestHtml(
+        '<template id="$id">'
+          'Hi {{ name }}'
+        '</template>'
+        '<template ref="$id" bind="{{}}"></template>');
+
+    var t1 = div.nodes.first;
+    var t2 = div.nodes[1];
+
+    expect(t2.ref, t1);
+
+    var model = toSymbolMap({'name': 'Fry'});
+    recursivelySetTemplateModel(div, model);
+
+    deliverChanges(model);
+    expect(t2.nextNode.text, 'Hi Fry');
+  });
+
+  test('BindChanged', () {
+    var model = toSymbolMap({
+      'XX': {'name': 'Leela', 'title': 'Captain'},
+      'XY': {'name': 'Fry', 'title': 'Delivery boy'},
+      'XZ': {'name': 'Zoidberg', 'title': 'Doctor'}
+    });
+
+    var div = createTestHtml(
+        '<template bind="{{ XX }}">Hi {{ name }}</template>');
+
+    recursivelySetTemplateModel(div, model);
+
+    var t = div.nodes.first;
+    deliverChanges(model);
+
+    expect(div.nodes.length, 2);
+    expect(t.nextNode.text, 'Hi Leela');
+
+    t.bind('bind', model, 'XZ');
+    deliverChanges(model);
+
+    expect(div.nodes.length, 2);
+    expect(t.nextNode.text, 'Hi Zoidberg');
+  });
+
+  assertNodesAre(div, [arguments]) {
+    var expectedLength = arguments.length;
+    expect(div.nodes.length, expectedLength + 1);
+
+    for (var i = 0; i < arguments.length; i++) {
+      var targetNode = div.nodes[i + 1];
+      expect(targetNode.text, arguments[i]);
+    }
+  }
+
+  test('Repeat3', () {
+    var div = createTestHtml(
+        '<template repeat="{{ contacts }}">Hi {{ name }}</template>');
+    var t = div.nodes.first;
+
+    var m = toSymbols({
+      'contacts': [
+        {'name': 'Raf'},
+        {'name': 'Arv'},
+        {'name': 'Neal'}
+      ]
+    });
+
+    recursivelySetTemplateModel(div, m);
+    deliverChanges(m);
+
+    assertNodesAre(div, ['Hi Raf', 'Hi Arv', 'Hi Neal']);
+
+    m[sym('contacts')].add(toSymbols({'name': 'Alex'}));
+    deliverChanges(m);
+    assertNodesAre(div, ['Hi Raf', 'Hi Arv', 'Hi Neal', 'Hi Alex']);
+
+    m[sym('contacts')].replaceRange(0, 2,
+        toSymbols([{'name': 'Rafael'}, {'name': 'Erik'}]));
+    deliverChanges(m);
+    assertNodesAre(div, ['Hi Rafael', 'Hi Erik', 'Hi Neal', 'Hi Alex']);
+
+    m[sym('contacts')].removeRange(1, 3);
+    deliverChanges(m);
+    assertNodesAre(div, ['Hi Rafael', 'Hi Alex']);
+
+    m[sym('contacts')].insertAll(1,
+        toSymbols([{'name': 'Erik'}, {'name': 'Dimitri'}]));
+    deliverChanges(m);
+    assertNodesAre(div, ['Hi Rafael', 'Hi Erik', 'Hi Dimitri', 'Hi Alex']);
+
+    m[sym('contacts')].replaceRange(0, 1,
+        toSymbols([{'name': 'Tab'}, {'name': 'Neal'}]));
+    deliverChanges(m);
+    assertNodesAre(div, ['Hi Tab', 'Hi Neal', 'Hi Erik', 'Hi Dimitri', 'Hi Alex']);
+
+    m[sym('contacts')] = toSymbols([{'name': 'Alex'}]);
+    deliverChanges(m);
+    assertNodesAre(div, ['Hi Alex']);
+
+    m[sym('contacts')].length = 0;
+    deliverChanges(m);
+    assertNodesAre(div, []);
+  });
+
+  test('RepeatModelSet', () {
+    var div = createTestHtml(
+        '<template repeat="{{ contacts }}">'
+          'Hi {{ name }}'
+        '</template>');
+    var m = toSymbols({
+      'contacts': [
+        {'name': 'Raf'},
+        {'name': 'Arv'},
+        {'name': 'Neal'}
+      ]
+    });
+    recursivelySetTemplateModel(div, m);
+
+    deliverChanges(m);
+    var t = div.nodes.first;
+
+    assertNodesAre(div, ['Hi Raf', 'Hi Arv', 'Hi Neal']);
+  });
+
+  test('RepeatEmptyPath', () {
+    var div = createTestHtml('<template repeat="{{}}">Hi {{ name }}</template>');
+    var t = div.nodes.first;
+
+    var m = toSymbols([
+      {'name': 'Raf'},
+      {'name': 'Arv'},
+      {'name': 'Neal'}
+    ]);
+    recursivelySetTemplateModel(div, m);
+
+    deliverChanges(m);
+
+    assertNodesAre(div, ['Hi Raf', 'Hi Arv', 'Hi Neal']);
+
+    m.add(toSymbols({'name': 'Alex'}));
+    deliverChanges(m);
+    assertNodesAre(div, ['Hi Raf', 'Hi Arv', 'Hi Neal', 'Hi Alex']);
+
+    m.replaceRange(0, 2, toSymbols([{'name': 'Rafael'}, {'name': 'Erik'}]));
+    deliverChanges(m);
+    assertNodesAre(div, ['Hi Rafael', 'Hi Erik', 'Hi Neal', 'Hi Alex']);
+
+    m.removeRange(1, 3);
+    deliverChanges(m);
+    assertNodesAre(div, ['Hi Rafael', 'Hi Alex']);
+
+    m.insertAll(1, toSymbols([{'name': 'Erik'}, {'name': 'Dimitri'}]));
+    deliverChanges(m);
+    assertNodesAre(div, ['Hi Rafael', 'Hi Erik', 'Hi Dimitri', 'Hi Alex']);
+
+    m.replaceRange(0, 1, toSymbols([{'name': 'Tab'}, {'name': 'Neal'}]));
+    deliverChanges(m);
+    assertNodesAre(div, ['Hi Tab', 'Hi Neal', 'Hi Erik', 'Hi Dimitri', 'Hi Alex']);
+
+    m.length = 0;
+    m.add(toSymbols({'name': 'Alex'}));
+    deliverChanges(m);
+    assertNodesAre(div, ['Hi Alex']);
+  });
+
+  test('RepeatNullModel', () {
+    var div = createTestHtml('<template repeat="{{}}">Hi {{ name }}</template>');
+    var t = div.nodes.first;
+
+    var m = null;
+    recursivelySetTemplateModel(div, m);
+
+    expect(div.nodes.length, 1);
+
+    t.attributes['iterate'] = '';
+    m = toSymbols({});
+    recursivelySetTemplateModel(div, m);
+
+    deliverChanges(m);
+    expect(div.nodes.length, 1);
+  });
+
+  test('RepeatReuse', () {
+    var div = createTestHtml('<template repeat="{{}}">Hi {{ name }}</template>');
+    var t = div.nodes.first;
+
+    var m = toSymbols([
+      {'name': 'Raf'},
+      {'name': 'Arv'},
+      {'name': 'Neal'}
+    ]);
+    recursivelySetTemplateModel(div, m);
+    deliverChanges(m);
+
+    assertNodesAre(div, ['Hi Raf', 'Hi Arv', 'Hi Neal']);
+    var node1 = div.nodes[1];
+    var node2 = div.nodes[2];
+    var node3 = div.nodes[3];
+
+    m.replaceRange(1, 2, toSymbols([{'name': 'Erik'}]));
+    deliverChanges(m);
+    assertNodesAre(div, ['Hi Raf', 'Hi Erik', 'Hi Neal']);
+    expect(div.nodes[1], node1,
+        reason: 'model[0] did not change so the node should not have changed');
+    expect(div.nodes[2], isNot(equals(node2)),
+        reason: 'Should not reuse when replacing');
+    expect(div.nodes[3], node3,
+        reason: 'model[2] did not change so the node should not have changed');
+
+    node2 = div.nodes[2];
+    m.insert(0, toSymbols({'name': 'Alex'}));
+    deliverChanges(m);
+    assertNodesAre(div, ['Hi Alex', 'Hi Raf', 'Hi Erik', 'Hi Neal']);
+  });
+
+  test('TwoLevelsDeepBug', () {
+    var div = createTestHtml(
+      '<template bind="{{}}"><span><span>{{ foo }}</span></span></template>');
+
+    var model = toSymbolMap({'foo': 'bar'});
+    recursivelySetTemplateModel(div, model);
+    deliverChanges(model);
+
+    expect(div.nodes[1].nodes[0].nodes[0].text, 'bar');
+  });
+
+  test('Checked', () {
+    var div = createTestHtml(
+        '<template>'
+          '<input type="checkbox" checked="{{a}}">'
+        '</template>');
+    var t = div.nodes.first;
+    var m = toSymbols({
+      'a': true
+    });
+    t.bind('bind', m, '');
+    deliverChanges(m);
+
+    var instanceInput = t.nextNode;
+    expect(instanceInput.checked, true);
+
+    instanceInput.click();
+    expect(instanceInput.checked, false);
+
+    instanceInput.click();
+    expect(instanceInput.checked, true);
+  });
+
+  nestedHelper(s, start) {
+    var div = createTestHtml(s);
+
+    var m = toSymbols({
+      'a': {
+        'b': 1,
+        'c': {'d': 2}
+      },
+    });
+
+    recursivelySetTemplateModel(div, m);
+    deliverChanges(m);
+
+    var i = start;
+    expect(div.nodes[i++].text, '1');
+    expect(div.nodes[i++].tagName, 'TEMPLATE');
+    expect(div.nodes[i++].text, '2');
+
+    m[sym('a')][sym('b')] = 11;
+    deliverChanges(m);
+    expect(div.nodes[start].text, '11');
+
+    m[sym('a')][sym('c')] = toSymbols({'d': 22});
+    deliverChanges(m);
+    expect(div.nodes[start + 2].text, '22');
+  }
+
+  test('Nested', () {
+    nestedHelper(
+        '<template bind="{{a}}">'
+          '{{b}}'
+          '<template bind="{{c}}">'
+            '{{d}}'
+          '</template>'
+        '</template>', 1);
+  });
+
+  test('NestedWithRef', () {
+    nestedHelper(
+        '<template id="inner">{{d}}</template>'
+        '<template id="outer" bind="{{a}}">'
+          '{{b}}'
+          '<template ref="inner" bind="{{c}}"></template>'
+        '</template>', 2);
+  });
+
+  nestedIterateInstantiateHelper(s, start) {
+    var div = createTestHtml(s);
+
+    var m = toSymbols({
+      'a': [
+        {
+          'b': 1,
+          'c': {'d': 11}
+        },
+        {
+          'b': 2,
+          'c': {'d': 22}
+        }
+      ]
+    });
+
+    recursivelySetTemplateModel(div, m);
+    deliverChanges(m);
+
+    var i = start;
+    expect(div.nodes[i++].text, '1');
+    expect(div.nodes[i++].tagName, 'TEMPLATE');
+    expect(div.nodes[i++].text, '11');
+    expect(div.nodes[i++].text, '2');
+    expect(div.nodes[i++].tagName, 'TEMPLATE');
+    expect(div.nodes[i++].text, '22');
+
+    m[sym('a')][1] = toSymbols({
+      'b': 3,
+      'c': {'d': 33}
+    });
+
+    deliverChanges(m);
+    expect(div.nodes[start + 3].text, '3');
+    expect(div.nodes[start + 5].text, '33');
+  }
+
+  test('NestedRepeatBind', () {
+    nestedIterateInstantiateHelper(
+        '<template repeat="{{a}}">'
+          '{{b}}'
+          '<template bind="{{c}}">'
+            '{{d}}'
+          '</template>'
+        '</template>', 1);
+  });
+
+  test('NestedRepeatBindWithRef', () {
+    nestedIterateInstantiateHelper(
+        '<template id="inner">'
+          '{{d}}'
+        '</template>'
+        '<template repeat="{{a}}">'
+          '{{b}}'
+          '<template ref="inner" bind="{{c}}"></template>'
+        '</template>', 2);
+  });
+
+  nestedIterateIterateHelper(s, start) {
+    var div = createTestHtml(s);
+
+    var m = toSymbols({
+      'a': [
+        {
+          'b': 1,
+          'c': [{'d': 11}, {'d': 12}]
+        },
+        {
+          'b': 2,
+          'c': [{'d': 21}, {'d': 22}]
+        }
+      ]
+    });
+
+    recursivelySetTemplateModel(div, m);
+    deliverChanges(m);
+
+    var i = start;
+    expect(div.nodes[i++].text, '1');
+    expect(div.nodes[i++].tagName, 'TEMPLATE');
+    expect(div.nodes[i++].text, '11');
+    expect(div.nodes[i++].text, '12');
+    expect(div.nodes[i++].text, '2');
+    expect(div.nodes[i++].tagName, 'TEMPLATE');
+    expect(div.nodes[i++].text, '21');
+    expect(div.nodes[i++].text, '22');
+
+    m[sym('a')][1] = toSymbols({
+      'b': 3,
+      'c': [{'d': 31}, {'d': 32}, {'d': 33}]
+    });
+
+    i = start + 4;
+    deliverChanges(m);
+    expect(div.nodes[start + 4].text, '3');
+    expect(div.nodes[start + 6].text, '31');
+    expect(div.nodes[start + 7].text, '32');
+    expect(div.nodes[start + 8].text, '33');
+  }
+
+  test('NestedRepeatBind', () {
+    nestedIterateIterateHelper(
+        '<template repeat="{{a}}">'
+          '{{b}}'
+          '<template repeat="{{c}}">'
+            '{{d}}'
+          '</template>'
+        '</template>', 1);
+  });
+
+  test('NestedRepeatRepeatWithRef', () {
+    nestedIterateIterateHelper(
+        '<template id="inner">'
+          '{{d}}'
+        '</template>'
+        '<template repeat="{{a}}">'
+          '{{b}}'
+          '<template ref="inner" repeat="{{c}}"></template>'
+        '</template>', 2);
+  });
+
+  test('NestedRepeatSelfRef', () {
+    var div = createTestHtml(
+        '<template id="t" repeat="{{}}">'
+          '{{name}}'
+          '<template ref="t" repeat="{{items}}"></template>'
+        '</template>');
+
+    var m = toSymbols([
+      {
+        'name': 'Item 1',
+        'items': [
+          {
+            'name': 'Item 1.1',
+            'items': [
+              {
+                 'name': 'Item 1.1.1',
+                 'items': []
+              }
+            ]
+          },
+          {
+            'name': 'Item 1.2'
+          }
+        ]
+      },
+      {
+        'name': 'Item 2',
+        'items': []
+      },
+    ]);
+
+    recursivelySetTemplateModel(div, m);
+    deliverChanges(m);
+
+    var i = 1;
+    expect(div.nodes[i++].text, 'Item 1');
+    expect(div.nodes[i++].tagName, 'TEMPLATE');
+    expect(div.nodes[i++].text, 'Item 1.1');
+    expect(div.nodes[i++].tagName, 'TEMPLATE');
+    expect(div.nodes[i++].text, 'Item 1.1.1');
+    expect(div.nodes[i++].tagName, 'TEMPLATE');
+    expect(div.nodes[i++].text, 'Item 1.2');
+    expect(div.nodes[i++].tagName, 'TEMPLATE');
+    expect(div.nodes[i++].text, 'Item 2');
+
+    m[0] = toSymbols({'name': 'Item 1 changed'});
+
+    i = 1;
+    deliverChanges(m);
+    expect(div.nodes[i++].text, 'Item 1 changed');
+    expect(div.nodes[i++].tagName, 'TEMPLATE');
+    expect(div.nodes[i++].text, 'Item 2');
+  });
+
+  test('NestedIterateTableMixedSemanticNative', () {
+    if (!TemplateElement.supported) return;
+
+    var div = createTestHtml(
+        '<table><tbody>'
+          '<template repeat="{{}}">'
+            '<tr>'
+              '<td template repeat="{{}}" class="{{ val }}">{{ val }}</td>'
+            '</tr>'
+          '</template>'
+        '</tbody></table>');
+
+    var m = toSymbols([
+      [{ 'val': 0 }, { 'val': 1 }],
+      [{ 'val': 2 }, { 'val': 3 }]
+    ]);
+
+    recursivelySetTemplateModel(div, m);
+    deliverChanges(m);
+
+    var tbody = div.nodes[0].nodes[0];
+
+    // 1 for the <tr template>, 2 * (1 tr)
+    expect(tbody.nodes.length, 3);
+
+    // 1 for the <td template>, 2 * (1 td)
+    expect(tbody.nodes[1].nodes.length, 3);
+
+    expect(tbody.nodes[1].nodes[1].text, '0');
+    expect(tbody.nodes[1].nodes[2].text, '1');
+
+    // 1 for the <td template>, 2 * (1 td)
+    expect(tbody.nodes[2].nodes.length, 3);
+    expect(tbody.nodes[2].nodes[1].text, '2');
+    expect(tbody.nodes[2].nodes[2].text, '3');
+
+    // Asset the 'class' binding is retained on the semantic template (just
+    // check the last one).
+    expect(tbody.nodes[2].nodes[2].attributes["class"], '3');
+  });
+
+  test('NestedIterateTable', () {
+    var div = createTestHtml(
+        '<table><tbody>'
+          '<tr template repeat="{{}}">'
+            '<td template repeat="{{}}" class="{{ val }}">{{ val }}</td>'
+          '</tr>'
+        '</tbody></table>');
+
+    var m = toSymbols([
+      [{ 'val': 0 }, { 'val': 1 }],
+      [{ 'val': 2 }, { 'val': 3 }]
+    ]);
+
+    recursivelySetTemplateModel(div, m);
+    deliverChanges(m);
+
+    var i = 1;
+    var tbody = div.nodes[0].nodes[0];
+
+    // 1 for the <tr template>, 2 * (1 tr)
+    expect(tbody.nodes.length, 3);
+
+    // 1 for the <td template>, 2 * (1 td)
+    expect(tbody.nodes[1].nodes.length, 3);
+    expect(tbody.nodes[1].nodes[1].text, '0');
+    expect(tbody.nodes[1].nodes[2].text, '1');
+
+    // 1 for the <td template>, 2 * (1 td)
+    expect(tbody.nodes[2].nodes.length, 3);
+    expect(tbody.nodes[2].nodes[1].text, '2');
+    expect(tbody.nodes[2].nodes[2].text, '3');
+
+    // Asset the 'class' binding is retained on the semantic template (just check
+    // the last one).
+    expect(tbody.nodes[2].nodes[2].attributes['class'], '3');
+  });
+
+  test('NestedRepeatDeletionOfMultipleSubTemplates', () {
+    var div = createTestHtml(
+        '<ul>'
+          '<template repeat="{{}}" id=t1>'
+            '<li>{{name}}'
+              '<ul>'
+                '<template ref=t1 repaet="{{items}}"></template>'
+              '</ul>'
+            '</li>'
+          '</template>'
+        '</ul>');
+
+    var m = toSymbols([
+      {
+        'name': 'Item 1',
+        'items': [
+          {
+            'name': 'Item 1.1'
+          }
+        ]
+      }
+    ]);
+
+    recursivelySetTemplateModel(div, m);
+
+    deliverChanges(m);
+    m.removeAt(0);
+    deliverChanges(m);
+  });
+
+  test('DeepNested', () {
+    var div = createTestHtml(
+      '<template bind="{{a}}">'
+        '<p>'
+          '<template bind="{{b}}">'
+            '{{ c }}'
+          '</template>'
+        '</p>'
+      '</template>');
+
+    var m = toSymbols({
+      'a': {
+        'b': {
+          'c': 42
+        }
+      }
+    });
+    recursivelySetTemplateModel(div, m);
+    deliverChanges(m);
+
+    expect(div.nodes[1].tagName, 'P');
+    expect(div.nodes[1].nodes.first.tagName, 'TEMPLATE');
+    expect(div.nodes[1].nodes[1].text, '42');
+  });
+
+  test('TemplateContentRemoved', () {
+    var div = createTestHtml('<template bind="{{}}">{{ }}</template>');
+    var model = 42;
+
+    recursivelySetTemplateModel(div, model);
+    deliverChanges(model);
+    expect(div.nodes[1].text, '42');
+    expect(div.nodes[0].text, '');
+  });
+
+  test('TemplateContentRemovedEmptyArray', () {
+    var div = createTestHtml('<template iterate>Remove me</template>');
+    var model = toSymbols([]);
+
+    recursivelySetTemplateModel(div, model);
+    deliverChanges(model);
+    expect(div.nodes.length, 1);
+    expect(div.nodes[0].text, '');
+  });
+
+  test('TemplateContentRemovedNested', () {
+    var div = createTestHtml(
+        '<template bind="{{}}">'
+          '{{ a }}'
+          '<template bind="{{}}">'
+            '{{ b }}'
+          '</template>'
+        '</template>');
+
+    var model = toSymbolMap({
+      'a': 1,
+      'b': 2
+    });
+    recursivelySetTemplateModel(div, model);
+    deliverChanges(model);
+
+    expect(div.nodes[0].text, '');
+    expect(div.nodes[1].text, '1');
+    expect(div.nodes[2].text, '');
+    expect(div.nodes[3].text, '2');
+  });
+
+  test('BindWithUndefinedModel', () {
+    var div = createTestHtml(
+        '<template bind="{{}}" if="{{}}">{{ a }}</template>');
+
+    var model = toSymbolMap({'a': 42});
+    recursivelySetTemplateModel(div, model);
+    deliverChanges(model);
+    expect(div.nodes[1].text, '42');
+
+    model = null;
+    recursivelySetTemplateModel(div, model);
+    deliverChanges(model);
+    expect(div.nodes.length, 1);
+
+    model = toSymbols({'a': 42});
+    recursivelySetTemplateModel(div, model);
+    deliverChanges(model);
+    expect(div.nodes[1].text, '42');
+  });
+
+  test('BindNested', () {
+    var div = createTestHtml(
+        '<template bind="{{}}">'
+          'Name: {{ name }}'
+          '<template bind="{{wife}}" if="{{wife}}">'
+            'Wife: {{ name }}'
+          '</template>'
+          '<template bind="{{child}}" if="{{child}}">'
+            'Child: {{ name }}'
+          '</template>'
+        '</template>');
+
+    var m = toSymbols({
+      'name': 'Hermes',
+      'wife': {
+        'name': 'LaBarbara'
+      }
+    });
+    recursivelySetTemplateModel(div, m);
+    deliverChanges(m);
+
+    expect(div.nodes.length, 5);
+    expect(div.nodes[1].text, 'Name: Hermes');
+    expect(div.nodes[3].text, 'Wife: LaBarbara');
+
+    m[sym('child')] = toSymbols({'name': 'Dwight'});
+    deliverChanges(m);
+    expect(div.nodes.length, 6);
+    expect(div.nodes[5].text, 'Child: Dwight');
+
+    m.remove(sym('wife'));
+    deliverChanges(m);
+    expect(div.nodes.length, 5);
+    expect(div.nodes[4].text, 'Child: Dwight');
+  });
+
+  test('BindRecursive', () {
+    var div = createTestHtml(
+        '<template bind="{{}}" if="{{}}" id="t">'
+          'Name: {{ name }}'
+          '<template bind="{{friend}}" if="{{friend}}" ref="t"></template>'
+        '</template>');
+
+    var m = toSymbols({
+      'name': 'Fry',
+      'friend': {
+        'name': 'Bender'
+      }
+    });
+    recursivelySetTemplateModel(div, m);
+    deliverChanges(m);
+
+    expect(div.nodes.length, 5);
+    expect(div.nodes[1].text, 'Name: Fry');
+    expect(div.nodes[3].text, 'Name: Bender');
+
+    m[sym('friend')][sym('friend')] = toSymbols({'name': 'Leela'});
+    deliverChanges(m);
+    expect(div.nodes.length, 7);
+    expect(div.nodes[5].text, 'Name: Leela');
+
+    m[sym('friend')] = toSymbols({'name': 'Leela'});
+    deliverChanges(m);
+    expect(div.nodes.length, 5);
+    expect(div.nodes[3].text, 'Name: Leela');
+  });
+
+  test('ChangeFromBindToRepeat', () {
+    var div = createTestHtml(
+        '<template bind="{{a}}">'
+          '{{ length }}'
+        '</template>');
+    var template = div.nodes.first;
+
+    var m = toSymbols({
+      'a': [
+        {'length': 0},
+        {
+          'length': 1,
+          'b': {'length': 4}
+        },
+        {'length': 2}
+      ]
+    });
+    recursivelySetTemplateModel(div, m);
+    deliverChanges(m);
+
+    expect(div.nodes.length, 2);
+    expect(div.nodes[1].text, '3');
+
+    template.unbind('bind');
+    template.bind('repeat', m, 'a');
+    deliverChanges(m);
+    expect(div.nodes.length, 4);
+    expect(div.nodes[1].text, '0');
+    expect(div.nodes[2].text, '1');
+    expect(div.nodes[3].text, '2');
+
+    template.unbind('repeat');
+    template.bind('bind', m, 'a.1.b');
+
+    deliverChanges(m);
+    expect(div.nodes.length, 2);
+    expect(div.nodes[1].text, '4');
+  });
+
+  test('ChangeRefId', () {
+    var div = createTestHtml(
+        '<template id="a">a:{{ }}</template>'
+        '<template id="b">b:{{ }}</template>'
+        '<template repeat="{{}}">'
+          '<template ref="a" bind="{{}}"></template>'
+        '</template>');
+    var model = toSymbols([]);
+    recursivelySetTemplateModel(div, model);
+    deliverChanges(model);
+
+    expect(div.nodes.length, 3);
+
+    document.getElementById('a').id = 'old-a';
+    document.getElementById('b').id = 'a';
+
+    model..add(1)..add(2);
+    deliverChanges(model);
+
+    expect(div.nodes.length, 7);
+    expect(div.nodes[4].text, 'b:1');
+    expect(div.nodes[6].text, 'b:2');
+  });
+
+  test('Content', () {
+    var div = createTestHtml(
+        '<template><a></a></template>'
+        '<template><b></b></template>');
+    var templateA = div.nodes.first;
+    var templateB = div.nodes.last;
+    var contentA = templateA.content;
+    var contentB = templateB.content;
+    expect(contentA, isNotNull);
+
+    expect(templateA.document, isNot(equals(contentA.document)));
+    expect(templateB.document, isNot(equals(contentB.document)));
+
+    expect(templateB.document, templateA.document);
+    expect(contentB.document, contentA.document);
+
+    expect(templateA.document.window, window);
+    expect(templateB.document.window, window);
+
+    expect(contentA.document.window, null);
+    expect(contentB.document.window, null);
+
+    expect(contentA.nodes.last, contentA.nodes.first);
+    expect(contentA.nodes.first.tagName, 'A');
+
+    expect(contentB.nodes.last, contentB.nodes.first);
+    expect(contentB.nodes.first.tagName, 'B');
+  });
+
+  test('NestedContent', () {
+    var div = createTestHtml(
+        '<template>'
+        '<template></template>'
+        '</template>');
+    var templateA = div.nodes.first;
+    var templateB = templateA.content.nodes.first;
+
+    expect(templateB.document, templateA.content.document);
+    expect(templateB.content.document, templateA.content.document);
+  });
+
+  test('BindShadowDOM', () {
+    if (ShadowRoot.supported) {
+      var root = createShadowTestHtml(
+          '<template bind="{{}}">Hi {{ name }}</template>');
+      var model = toSymbolMap({'name': 'Leela'});
+      recursivelySetTemplateModel(root, model);
+      deliverChanges(model);
+      expect(root.nodes[1].text, 'Hi Leela');
+    }
+  });
+
+  // https://github.com/toolkitchen/mdv/issues/8
+  test('UnbindingInNestedBind', () {
+    var div = createTestHtml(
+      '<template bind="{{outer}}" if="{{outer}}" syntax="testHelper">'
+        '<template bind="{{inner}}" if="{{inner}}">'
+          '{{ age }}'
+        '</template>'
+      '</template>');
+
+    var syntax = new UnbindingInNestedBindSyntax();
+    TemplateElement.syntax['testHelper'] = syntax;
+    try {
+      var model = toSymbolMap({
+        'outer': {
+          'inner': {
+            'age': 42
+          }
+        }
+      });
+
+      recursivelySetTemplateModel(div, model);
+
+      deliverChanges(model);
+      expect(syntax.count, 1);
+
+      var inner = model[sym('outer')][sym('inner')];
+      model[sym('outer')] = null;
+
+      deliverChanges(model);
+      expect(syntax.count, 1);
+
+      model[sym('outer')] = toSymbols({'inner': {'age': 2}});
+      syntax.expectedAge = 2;
+
+      deliverChanges(model);
+      expect(syntax.count, 2);
+    } finally {
+      TemplateElement.syntax.remove('testHelper');
+    }
+  });
+
+  // https://github.com/toolkitchen/mdv/issues/8
+  test('DontCreateInstancesForAbandonedIterators', () {
+    var div = createTestHtml(
+      '<template bind="{{}} {{}}">'
+        '<template bind="{{}}">Foo'
+        '</template>'
+      '</template>');
+    recursivelySetTemplateModel(div, null);
+    // TODO(jmesserly): how to fix this test?
+    // Perhaps revive the original?
+    // https://github.com/toolkitchen/mdv/commit/8bc1e3466aeb6930150c0d3148f0e830184bf599#L3R1278
+    //expect(!!ChangeSummary._errorThrownDuringCallback, false);
+  });
+
+  test('CreateInstance', () {
+    var div = createTestHtml(
+      '<template bind="{{a}}">'
+        '<template bind="{{b}}">'
+          '{{text}}'
+        '</template>'
+      '</template>');
+    var outer = div.nodes.first;
+
+    var instance = outer.createInstance();
+    expect(outer.content.nodes.first, instance.nodes.first.ref);
+
+    var instance2 =  outer.createInstance();
+    expect(instance2.nodes.first.ref, instance.nodes.first.ref);
+  });
+
+  test('Bootstrap', () {
+    var div = new DivElement();
+    div.innerHtml =
+      '<template>'
+        '<div></div>'
+        '<template>'
+          'Hello'
+        '</template>'
+      '</template>';
+
+    TemplateElement.bootstrap(div);
+    var template = div.nodes.first;
+    expect(template.content.nodes.length, 2);
+    var template2 = template.content.nodes.first.nextNode;
+    expect(template2.content.nodes.length, 1);
+    expect(template2.content.nodes.first.text, 'Hello');
+
+    template = new Element.tag('template');
+    template.innerHtml =
+      '<template>'
+        '<div></div>'
+        '<template>'
+          'Hello'
+        '</template>'
+      '</template>';
+
+    TemplateElement.bootstrap(template);
+    template2 = template.content.nodes.first;
+    expect(template2.content.nodes.length, 2);
+    var template3 = template2.content.nodes.first.nextNode;
+    expect(template3.content.nodes.length, 1);
+    expect(template3.content.nodes.first.text, 'Hello');
+  });
+
+  test('instanceCreated hack', () {
+    var called = false;
+    var sub = TemplateElement.instanceCreated.listen((node) {
+      called = true;
+      expect(node.nodeType, Node.DOCUMENT_FRAGMENT_NODE);
+    });
+
+    var div = createTestHtml('<template bind="{{}}">Foo</template>');
+    expect(called, false);
+
+    recursivelySetTemplateModel(div, null);
+    deliverChangeRecords();
+    expect(called, true);
+
+    sub.cancel();
+  });
+}
+
+class UnbindingInNestedBindSyntax extends CustomBindingSyntax {
+  int expectedAge = 42;
+  int count = 0;
+
+  getBinding(model, path, name, node) {
+    if (name != 'text' || path != 'age')
+      return;
+
+    expect(model[sym('age')], expectedAge);
+    count++;
+  }
+}
+
+/** Verifies that the model is Observable, then calls [deliverChangeRecords]. */
+void deliverChanges(model) {
+  expectObservable(model);
+  deliverChangeRecords();
+}
+
+void expectObservable(model) {
+  if (model is! Observable) {
+    // This is here to eagerly catch a bug in the test; it means the test
+    // forgot a toSymbols somewhere.
+    expect(identical(toSymbols(model), model), true,
+        reason: 'model type "${model.runtimeType}" should be observable');
+    return;
+  }
+  if (model is ObservableList) {
+    for (var item in model) {
+      expectObservable(item);
+    }
+  } else if (model is ObservableMap) {
+    model.forEach((k, v) {
+      expectObservable(k);
+      expectObservable(v);
+    });
+  }
+}
+
+toSymbols(obj) => toObservable(_deepToSymbol(obj));
+
+sym(x) => new Symbol(x);
+
+_deepToSymbol(value) {
+  if (value is Map) {
+    var result = new LinkedHashMap();
+    value.forEach((k, v) {
+      k = k is String ? sym(k) : _deepToSymbol(k);
+      result[k] = _deepToSymbol(v);
+    });
+    return result;
+  }
+  if (value is Iterable) {
+    return value.map(_deepToSymbol).toList();
+  }
+  return value;
+}
+
diff --git a/tests/language/language.status b/tests/language/language.status
index 6e939ca..6808a65 100644
--- a/tests/language/language.status
+++ b/tests/language/language.status
@@ -565,7 +565,6 @@
 assign_static_type_test/05: fail
 assign_static_type_test/06: fail
 bad_initializer2_negative_test: fail
-bad_named_constructor_negative_test: fail
 bad_named_parameters2_test: fail
 bad_named_parameters_test: fail
 bad_override_test/01: fail
@@ -642,6 +641,8 @@
 compile_time_constant_checked_test/01: fail
 compile_time_constant_checked_test/02: fail
 compile_time_constant_checked_test/03: fail
+compile_time_constant_c_test/01: fail
+compile_time_constant_c_test/02: fail
 compile_time_constant_d_test: fail
 compile_time_constant_e_test: fail
 compile_time_constant_test/02: fail
@@ -652,9 +653,6 @@
 constructor2_negative_test: fail
 constructor3_negative_test: fail
 constructor_call_wrong_argument_count_negative_test: fail
-constructor_name_test/01: fail
-constructor_name_test/02: fail
-constructor_name_test/03: fail
 constructor_negative_test: fail
 constructor_redirect1_negative_test: fail
 constructor_redirect2_negative_test: fail
@@ -664,7 +662,6 @@
 constructor_redirect_test/01: fail
 constructor_return_with_arrow_negative_test: fail
 constructor_return_with_init_and_arrow_negative_test: fail
-constructor_setter_negative_test: fail
 cyclic_constructor_test/01: fail
 cyclic_type_variable_test/01: fail
 cyclic_type_variable_test/02: fail
@@ -678,13 +675,23 @@
 dynamic_field_test: fail
 f_bounded_quantification_test/01: fail
 f_bounded_quantification_test/02: fail
+factory1_test/00: skip
+factory1_test/01: skip
 factory2_negative_test: fail
 factory2_test: fail
 factory5_test/00: fail
+factory_implementation_test/00: skip
 factory_negative_test: fail
 factory_redirection2_test/01: fail
 factory_redirection_test/04: fail
 factory_redirection_test/07: fail
+factory_redirection_test/08: skip
+factory_redirection_test/09: skip
+factory_redirection_test/10: skip
+factory_redirection_test/11: skip
+factory_redirection_test/12: skip
+factory_redirection_test/13: skip
+factory_redirection_test/14: skip
 fauxverride_test/03: fail
 fauxverride_test/05: fail
 field_decl_missing_var_type_test/01: fail
@@ -692,6 +699,7 @@
 field_override_test/01: fail
 field_override_test/02: fail
 field_type_check_test/01: fail
+field_type_check2_test/01: skip
 final_for_in_variable_test/01: fail
 final_param_negative_test: fail
 final_var_negative_test: fail
@@ -705,13 +713,12 @@
 function_type_alias5_test/01: fail
 function_type_alias5_test/02: fail
 getter_no_setter2_test/00: fail
-getter_no_setter2_test/02: fail
 getter_no_setter2_test/03: fail
 getter_no_setter_test/00: fail
 getter_no_setter_test/01: fail
-getter_no_setter_test/02: fail
 getters_setters2_test/01: fail
 getters_setters2_test/02: fail
+getters_setters2_test/03: skip
 getters_setters_type_test/01: fail
 implicit_scope_test: fail
 implicit_this_test/01: fail
@@ -731,7 +738,6 @@
 internal_library_test/01: fail
 is_not_class2_negative_test: fail
 issue1363_test: fail
-library1_negative_test: fail
 library_juxtaposition_test: fail
 licm_test: fail
 list_literal1_negative_test: fail
@@ -740,76 +746,19 @@
 list_literal_syntax_test/03: fail
 list_literal_syntax_test/05: fail
 map_literal1_negative_test: fail
-map_literal2_test: fail
 map_literal3_test: fail
 method_override2_test/00: fail
 method_override2_test/01: fail
 method_override2_test/02: fail
 method_override2_test/03: fail
-mixin_cyclic_test/01: fail
-mixin_illegal_constructor_test/01: fail
-mixin_illegal_constructor_test/02: fail
-mixin_illegal_constructor_test/03: fail
-mixin_illegal_constructor_test/04: fail
-mixin_illegal_constructor_test/05: fail
-mixin_illegal_constructor_test/06: fail
-mixin_illegal_constructor_test/07: fail
-mixin_illegal_constructor_test/08: fail
-mixin_illegal_constructor_test/09: fail
-mixin_illegal_constructor_test/10: fail
-mixin_illegal_constructor_test/11: fail
-mixin_illegal_constructor_test/12: fail
 mixin_illegal_constructor_test/13: fail
 mixin_illegal_constructor_test/14: fail
 mixin_illegal_constructor_test/15: fail
 mixin_illegal_constructor_test/16: fail
-mixin_illegal_cycles_test/01: fail
 mixin_illegal_cycles_test/02: fail
 mixin_illegal_cycles_test/03: fail
 mixin_illegal_cycles_test/04: fail
-mixin_illegal_cycles_test/05: fail
 mixin_illegal_cycles_test/06: fail
-mixin_illegal_super_use_test/01: fail
-mixin_illegal_super_use_test/02: fail
-mixin_illegal_super_use_test/03: fail
-mixin_illegal_super_use_test/04: fail
-mixin_illegal_super_use_test/05: fail
-mixin_illegal_super_use_test/06: fail
-mixin_illegal_super_use_test/07: fail
-mixin_illegal_super_use_test/08: fail
-mixin_illegal_super_use_test/09: fail
-mixin_illegal_super_use_test/10: fail
-mixin_illegal_super_use_test/11: fail
-mixin_illegal_superclass_test/01: fail
-mixin_illegal_superclass_test/02: fail
-mixin_illegal_superclass_test/03: fail
-mixin_illegal_superclass_test/04: fail
-mixin_illegal_superclass_test/05: fail
-mixin_illegal_superclass_test/06: fail
-mixin_illegal_superclass_test/07: fail
-mixin_illegal_superclass_test/08: fail
-mixin_illegal_superclass_test/09: fail
-mixin_illegal_superclass_test/10: fail
-mixin_illegal_superclass_test/11: fail
-mixin_illegal_superclass_test/12: fail
-mixin_illegal_superclass_test/13: fail
-mixin_illegal_superclass_test/14: fail
-mixin_illegal_superclass_test/15: fail
-mixin_illegal_superclass_test/16: fail
-mixin_illegal_superclass_test/17: fail
-mixin_illegal_superclass_test/18: fail
-mixin_illegal_superclass_test/19: fail
-mixin_illegal_superclass_test/20: fail
-mixin_illegal_superclass_test/21: fail
-mixin_illegal_superclass_test/22: fail
-mixin_illegal_superclass_test/23: fail
-mixin_illegal_superclass_test/24: fail
-mixin_illegal_superclass_test/25: fail
-mixin_illegal_superclass_test/26: fail
-mixin_illegal_superclass_test/27: fail
-mixin_illegal_superclass_test/28: fail
-mixin_illegal_superclass_test/29: fail
-mixin_illegal_superclass_test/30: fail
 mixin_illegal_syntax_test/13: fail
 mixin_type_parameters_errors_test/01: fail
 mixin_type_parameters_errors_test/02: fail
@@ -846,7 +795,6 @@
 prefix22_test: fail
 prefix23_test: fail
 prefix2_negative_test: fail
-prefix3_negative_test: fail
 prefix4_negative_test: fail
 prefix5_negative_test: fail
 prefix6_negative_test: fail
@@ -862,8 +810,6 @@
 scope_negative_test: fail
 setter3_test/01: fail
 setter3_test/02: fail
-setter_declaration2_negative_test: fail
-setter_declaration_negative_test: fail
 static_call_wrong_argument_count_negative_test: fail
 static_field3_test/01: fail
 static_field3_test/02: fail
@@ -935,7 +881,9 @@
 type_variable_bounds2_test/02: fail
 type_variable_bounds2_test/03: fail
 type_variable_bounds2_test/04: fail
+type_variable_bounds2_test/05: skip
 type_variable_bounds2_test/06: fail
+type_variable_bounds3_test/00: skip
 type_variable_bounds_test/00: fail
 type_variable_bounds_test/01: fail
 type_variable_bounds_test/02: fail
@@ -947,6 +895,12 @@
 type_variable_bounds_test/09: fail
 type_variable_bounds_test/10: fail
 type_variable_identifier_expression_negative_test: fail
+type_variable_scope_test/00: skip
+type_variable_scope_test/01: skip
+type_variable_scope_test/02: skip
+type_variable_scope_test/03: skip
+type_variable_scope_test/04: skip
+type_variable_scope_test/05: skip
 type_variable_static_context_negative_test: fail
 typed_equality_test: fail
 unary2_test: fail
diff --git a/tests/language/licm2_test.dart b/tests/language/licm2_test.dart
new file mode 100644
index 0000000..9e82949
--- /dev/null
+++ b/tests/language/licm2_test.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Test that a loop invariant code motion optimization does not try to
+// hoist instructions that may throw.
+
+import "package:expect/expect.dart";
+
+var a = 42;
+var b;
+
+main() {
+  Expect.throws(() {
+    while (true) {
+      a = 54;
+      b.length;
+    }
+  });
+  b = [];
+  Expect.equals(54, a);
+}
diff --git a/tests/language/throw_expr_test.dart b/tests/language/throw_expr_test.dart
index f844771..253e7cc 100644
--- a/tests/language/throw_expr_test.dart
+++ b/tests/language/throw_expr_test.dart
@@ -48,6 +48,7 @@
 class Q {
   var qqq;
   f(x) { qqq = x; }
+  Q get nono => throw "nono";
 }
 
 void test3() {
@@ -91,8 +92,14 @@
   }
 }
 
+void test4() {
+  var q = new Q();
+  Expect.throws(() => q.nono, (e) => e == "nono");
+}
+
 main() {
   test1();
   test2();
   test3();
+  test4();
 }
diff --git a/tests/standalone/io/http_server_early_client_close2_test.dart b/tests/standalone/io/http_server_early_client_close2_test.dart
index bc8b6f6..5f695de 100644
--- a/tests/standalone/io/http_server_early_client_close2_test.dart
+++ b/tests/standalone/io/http_server_early_client_close2_test.dart
@@ -1,6 +1,11 @@
 // Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
+//
+// VMOptions=
+// VMOptions=--short_socket_read
+// VMOptions=--short_socket_write
+// VMOptions=--short_socket_read --short_socket_write
 
 import "package:expect/expect.dart";
 import "dart:async";
diff --git a/tests/standalone/io/http_server_early_client_close_test.dart b/tests/standalone/io/http_server_early_client_close_test.dart
index d35aaf4..57a922b 100644
--- a/tests/standalone/io/http_server_early_client_close_test.dart
+++ b/tests/standalone/io/http_server_early_client_close_test.dart
@@ -1,6 +1,11 @@
 // Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
+//
+// VMOptions=
+// VMOptions=--short_socket_read
+// VMOptions=--short_socket_write
+// VMOptions=--short_socket_read --short_socket_write
 
 import "package:expect/expect.dart";
 import "dart:async";
diff --git a/tests/standalone/io/string_decoder_test.dart b/tests/standalone/io/string_decoder_test.dart
index d5c3da1..975e1cc 100644
--- a/tests/standalone/io/string_decoder_test.dart
+++ b/tests/standalone/io/string_decoder_test.dart
@@ -26,7 +26,7 @@
       })
       .then((b) => b.toString())
       .then((decoded) {
-        Expect.equals(7, decoded.length);
+        Expect.equals(8, decoded.length);
 
         var replacementChar = '?'.codeUnitAt(0);
         Expect.equals(0xd800, decoded.codeUnitAt(0));
@@ -36,6 +36,7 @@
         Expect.equals(replacementChar, decoded.codeUnitAt(4));
         Expect.equals(replacementChar, decoded.codeUnitAt(5));
         Expect.equals(replacementChar, decoded.codeUnitAt(6));
+        Expect.equals(replacementChar, decoded.codeUnitAt(7));
       });
 }
 
diff --git a/tests/standalone/io/web_socket_protocol_processor_test.dart b/tests/standalone/io/web_socket_protocol_processor_test.dart
index 39f9d91..b655b13 100644
--- a/tests/standalone/io/web_socket_protocol_processor_test.dart
+++ b/tests/standalone/io/web_socket_protocol_processor_test.dart
@@ -28,10 +28,11 @@
   List<int> expectedMessage;
 
   int messageCount = 0;
-  int closeCount = 0;
 
   var data;
 
+  Function onClosed;
+
   WebSocketMessageCollector(Stream stream,
                             [List<int> this.expectedMessage = null]) {
     stream.listen(onMessageData, onDone: onClosed, onError: onError);
@@ -46,10 +47,6 @@
     data = buffer;
   }
 
-  void onClosed() {
-    closeCount++;
-  }
-
   void onError(e) {
     String msg = "Unexpected error $e";
     var trace = getAttachedStackTrace(e);
@@ -78,7 +75,7 @@
   frameSize += count;
   // No masking.
   assert(maskingKey == null);
-  List<int> frame = new List<int>(frameSize);
+  List<int> frame = new Uint8List(frameSize);
   int frameIndex = 0;
   frame[frameIndex++] = (fin ? 0x80 : 0x00) | opcode;
   if (count < 126) {
@@ -115,33 +112,35 @@
     // Update the transformer with one big chunk.
     messageCount++;
     controller.add(frame);
-    Expect.isNotNull(mc.data);
-    Expect.equals(0, transformer._state);
-
-    mc.data = null;
-
-    // Only run this part on small messages.
-    if (message.length < 1000) {
-      // Update the transformer one byte at the time.
-      messageCount++;
-      for (int i = 0; i < frame.length; i++) {
-        controller.add(<int>[frame[i]]);
-      }
-      Expect.equals(0, transformer._state);
+    mc.onClosed = () {
       Expect.isNotNull(mc.data);
+      Expect.equals(0, transformer._state);
+
       mc.data = null;
 
-      // Update the transformer two bytes at the time.
-      messageCount++;
-      for (int i = 0; i < frame.length; i += 2) {
-        controller.add(frame.sublist(i, min(i + 2, frame.length)));
+      // Only run this part on small messages.
+      if (message.length < 1000) {
+        // Update the transformer one byte at the time.
+        messageCount++;
+        for (int i = 0; i < frame.length; i++) {
+          controller.add(<int>[frame[i]]);
+        }
+        Expect.equals(0, transformer._state);
+        Expect.isNotNull(mc.data);
+        mc.data = null;
+
+        // Update the transformer two bytes at the time.
+        messageCount++;
+        for (int i = 0; i < frame.length; i += 2) {
+          controller.add(frame.sublist(i, min(i + 2, frame.length)));
+        }
+        Expect.equals(0, transformer._state);
+        Expect.isNotNull(mc.data);
       }
-      Expect.equals(0, transformer._state);
-      Expect.isNotNull(mc.data);
-    }
-    Expect.equals(messageCount, mc.messageCount);
-    Expect.equals(0, mc.closeCount);
-    print("Messages test, messages $messageCount");
+      Expect.equals(messageCount, mc.messageCount);
+      print("Messages test, messages $messageCount");
+    };
+    controller.close();
   }
 
   void runTest(int from, int to, int step) {
@@ -227,7 +226,6 @@
   runTest(65534, 65537, 1);
   print("Fragment messages test, messages $messageCount, frames $frameCount");
   Expect.equals(messageCount, mc.messageCount);
-  Expect.equals(0, mc.closeCount);
 }
 
 void testUnmaskedMessage() {
diff --git a/tests/standalone/standalone.status b/tests/standalone/standalone.status
index 78a9864..dd2b0b7 100644
--- a/tests/standalone/standalone.status
+++ b/tests/standalone/standalone.status
@@ -17,8 +17,6 @@
 [ $runtime == vm ]
 package/package_isolate_test: Fail # http://dartbug.com/7520.
 io/raw_server_socket_cancel_test: Pass, Fail, Timeout # Issue 8675
-io/http_server_early_client_close_test: Pass, Fail # http://dartbug.com/10408
-io/http_server_early_client_close2_test: Pass, Fail # http://dartbug.com/10408
 
 [ $runtime == vm && $checked ]
 # These tests have type errors on purpose.
@@ -38,8 +36,6 @@
 # of allowed open files ('ulimit -n' says something like 256).
 io/socket_many_connections_test: Skip
 
-debugger/basic_debugger_test: Pass, Crash # Issue 10488
-
 # These tests pass on MacOS 10.8.2 but fails on the buildbot machines
 # that are running an earlier version of MacOS. The issue is that the
 # old version of MacOS does not expand the precomposed utf-8 it gets
diff --git a/tools/VERSION b/tools/VERSION
index 49be911..ee428f2 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -1,4 +1,4 @@
 MAJOR 0
 MINOR 5
-BUILD 6
+BUILD 7
 PATCH 0
diff --git a/tools/create_sdk.py b/tools/create_sdk.py
index 56d903e..c2c5cd0 100755
--- a/tools/create_sdk.py
+++ b/tools/create_sdk.py
@@ -36,6 +36,7 @@
 # ......isolate/
 # ......json/
 # ......math/
+# ......mdv_observe_impl/
 # ......mirrors/
 # ......uri/
 # ......utf/
@@ -218,7 +219,7 @@
                   join('html', 'dart2js'), join('html', 'dartium'),
                   join('html', 'html_common'),
                   join('indexed_db', 'dart2js'), join('indexed_db', 'dartium'),
-                  'json', 'math', 'mirrors', 'typed_data',
+                  'json', 'math', 'mdv_observe_impl', 'mirrors', 'typed_data',
                   join('svg', 'dart2js'), join('svg', 'dartium'),
                   'uri', 'utf',
                   join('web_audio', 'dart2js'), join('web_audio', 'dartium'),
@@ -273,12 +274,12 @@
   DARTANALYZER_SRC = join(HOME, build_dir, 'dartanalyzer')
   DARTANALYZER_DEST = join(UTIL, 'dartanalyzer')
   os.makedirs(DARTANALYZER_DEST)
-  
+
   jarFiles = glob.glob(join(DARTANALYZER_SRC, '*.jar'))
-  
+
   for jarFile in jarFiles:
     copyfile(jarFile, join(DARTANALYZER_DEST, os.path.basename(jarFile)))
-  
+
   # Copy in 7zip for Windows.
   if HOST_OS == 'win32':
     copytree(join(HOME, 'third_party', '7zip'),
diff --git a/tools/dom/dom.json b/tools/dom/dom.json
index c8d10bd..12e8200 100644
--- a/tools/dom/dom.json
+++ b/tools/dom/dom.json
@@ -4663,6 +4663,7 @@
   "HTMLTemplateElement": {
     "comment": "https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html#template-element",
     "members": {
+      "HTMLTemplateElement": {},
       "content": {}
     },
     "support_level": "experimental"
diff --git a/tools/dom/dom.py b/tools/dom/dom.py
index 15697b3..793764c 100755
--- a/tools/dom/dom.py
+++ b/tools/dom/dom.py
@@ -102,8 +102,10 @@
 
 def gen():
   os.chdir(os.path.join('tools', 'dom', 'scripts'))
-  return call([os.path.join(os.getcwd(), 'dartdomgenerator.py'),
+  result = call([os.path.join(os.getcwd(), 'dartdomgenerator.py'),
       '--rebuild', '--parallel', '--systems=htmldart2js,htmldartium'])
+  os.chdir(os.path.join('..', '..', '..'))
+  return result
 
 def http_server():
   print('Browse tests at '
diff --git a/tools/dom/idl/dart/dart.idl b/tools/dom/idl/dart/dart.idl
index 39034c3..b307156 100644
--- a/tools/dom/idl/dart/dart.idl
+++ b/tools/dom/idl/dart/dart.idl
@@ -159,11 +159,6 @@
   [Custom] any getVertexAttrib(unsigned long index, unsigned long pname);
   [Suppressed, StrictTypeChecking, Custom] void getVertexAttrib();
 };
-[Supplemental]
-interface CSSStyleDeclaration {
-  void setProperty(DOMString propertyName, DOMString value, [ForceOptional] optional DOMString priority);
-  [DartName=_getPropertyValue] DOMString getPropertyValue(DOMString propertyName);
-};
 
 // TODO(vsm): Define new names for these (see b/4436830).
 [Supplemental]
@@ -181,14 +176,6 @@
     [CallWith=ScriptExecutionContext, RaisesException] IDBRequest count([ForceOptional] optional IDBKeyRange? range);
     [CallWith=ScriptExecutionContext, RaisesException] IDBRequest count(any key);
 };
-[Supplemental]
-interface IDBDatabase {
-  // These variants are slated for removal from WebKit.  Suppress to bring our
-  // API in line with the most recent spec.
-  [Suppressed, CallWith=ScriptExecutionContext] IDBTransaction transaction(DOMStringList storeNames, unsigned short mode);
-  [Suppressed, CallWith=ScriptExecutionContext] IDBTransaction transaction(DOMString[] storeNames, unsigned short mode);
-  [Suppressed, CallWith=ScriptExecutionContext] IDBTransaction transaction(DOMString storeName, unsigned short mode);
-};
 
 [Supplemental]
 interface IDBKeyRange {
@@ -247,20 +234,17 @@
   // Provide missing constructor signature.
   Constructor(MutationCallback callback)]
 interface MutationObserver {
-  // Rename 'observe' so we can define a new 'observe' API that calls the
-  // original.
-  [DartName=_observe] void observe(Node target, Dictionary options);
 };
 
-  [Supplemental,
-    CustomConstructor,
-    // Provide missing constructor signature.
-    Constructor(optional HTMLFormElement form)]
-  interface DOMFormData {
-    [Suppressed] void append(DOMString name, DOMString value, DOMString filename);
-    [Custom] void append(DOMString name, DOMString value);
-    [Custom] void append(DOMString name, Blob value, optional DOMString filename);
-  };
+[Supplemental,
+  CustomConstructor,
+  // Provide missing constructor signature.
+  Constructor(optional HTMLFormElement form)]
+interface DOMFormData {
+  [Suppressed] void append(DOMString name, DOMString value, DOMString filename);
+  [Custom] void append(DOMString name, DOMString value);
+  [Custom] void append(DOMString name, Blob value, optional DOMString filename);
+};
 
 [Supplemental]
 interface SQLResultSetRowList {
diff --git a/tools/dom/scripts/dartmetadata.py b/tools/dom/scripts/dartmetadata.py
index 3a10a55..ce2f9f8 100644
--- a/tools/dom/scripts/dartmetadata.py
+++ b/tools/dom/scripts/dartmetadata.py
@@ -56,6 +56,14 @@
       "@Returns('String|CanvasGradient|CanvasPattern')",
     ],
 
+    # Normally DOMWindow is nevernull, but starting from a <template> element in
+    # JavaScript, this will be null:
+    #     template.content.ownerDocument.defaultView
+    'Document.window': [
+      "@Creates('Window|=Object|Null')",
+      "@Returns('Window|=Object|Null')",
+    ],
+
     # Methods returning Window can return a local window, or a cross-frame
     # window (=Object) that needs wrapping.
     'DOMWindow': [
@@ -171,6 +179,11 @@
     ],
 }, dart2jsOnly=True)
 
+_blink_experimental_annotations = [
+  "@SupportedBrowser(SupportedBrowser.CHROME)",
+  "@Experimental",
+]
+
 _indexed_db_annotations = [
   "@SupportedBrowser(SupportedBrowser.CHROME)",
   "@SupportedBrowser(SupportedBrowser.FIREFOX, '15')",
@@ -257,6 +270,7 @@
   ],
   'DOMFileSystem': _file_system_annotations,
   'DOMFileSystemSync': _file_system_annotations,
+  'DOMPoint': _webkit_experimental_annotations,
   'DOMWindow.webkitConvertPointFromNodeToPage': _webkit_experimental_annotations,
   'DOMWindow.webkitConvertPointFromPageToNode': _webkit_experimental_annotations,
   'DOMWindow.indexedDB': _indexed_db_annotations,
@@ -274,7 +288,7 @@
     "@Experimental",
   ],
   'Event.clipboardData': _webkit_experimental_annotations,
-  'FormData': _all_but_ie9_annotations,
+  'DOMFormData': _all_but_ie9_annotations,
   'HashChangeEvent': [
     "@SupportedBrowser(SupportedBrowser.CHROME)",
     "@SupportedBrowser(SupportedBrowser.FIREFOX)",
@@ -300,6 +314,7 @@
   'HTMLOutputElement': _no_ie_annotations,
   'HTMLProgressElement': _all_but_ie9_annotations,
   'HTMLShadowElement': _shadow_dom_annotations,
+  'HTMLTemplateElement': _blink_experimental_annotations,
   'HTMLTrackElement': [
     "@SupportedBrowser(SupportedBrowser.CHROME)",
     "@SupportedBrowser(SupportedBrowser.IE, '10')",
@@ -364,7 +379,6 @@
   'SQLTransactionSync': _web_sql_annotations,
   'WebGLRenderingContext': _webgl_annotations,
   'WebKitCSSMatrix': _webkit_experimental_annotations,
-  'WebKitPoint': _webkit_experimental_annotations,
   'WebSocket': _all_but_ie9_annotations,
   'Worker': _all_but_ie9_annotations,
   'XMLHttpRequest.onloadend': _all_but_ie9_annotations,
@@ -390,15 +404,15 @@
     self._doc_comments = json.load(comments_file)
     comments_file.close()
 
-  def GetFormattedMetadata(self, library_name, interface_name, member_id=None,
+  def GetFormattedMetadata(self, library_name, interface, member_id=None,
       indentation=''):
     """ Gets all comments and annotations for an interface or member.
     """
     return self.FormatMetadata(
-        self.GetMetadata(library_name, interface_name, member_id),
+        self.GetMetadata(library_name, interface, member_id),
         indentation)
 
-  def GetMetadata(self, library_name, interface_name,
+  def GetMetadata(self, library_name, interface,
         member_name=None, source_member_name=None):
     """ Gets all comments and annotations for an interface or member.
 
@@ -407,20 +421,20 @@
         then this is used to apply the support annotations from the other
         member.
     """
-    annotations = self._GetComments(library_name, interface_name, member_name)
+    annotations = self._GetComments(library_name, interface, member_name)
     annotations = annotations + self._GetCommonAnnotations(
-        interface_name, member_name, source_member_name)
+        interface, member_name, source_member_name)
 
     return annotations
 
   def GetDart2JSMetadata(self, idl_type, library_name,
-      interface_name, member_name,):
+      interface, member_name,):
     """ Gets all annotations for Dart2JS members- including annotations for
     both dart2js and dartium.
     """
-    annotations = self.GetMetadata(library_name, interface_name, member_name)
+    annotations = self.GetMetadata(library_name, interface, member_name)
 
-    ann2 = self._GetDart2JSSpecificAnnotations(idl_type, interface_name, member_name)
+    ann2 = self._GetDart2JSSpecificAnnotations(idl_type, interface.id, member_name)
     if ann2:
       if annotations:
         annotations.extend(ann2)
@@ -428,14 +442,16 @@
         annotations = ann2
     return annotations
 
-  def _GetCommonAnnotations(self, interface_name, member_name=None,
+  def _GetCommonAnnotations(self, interface, member_name=None,
       source_member_name=None):
     if member_name:
-      key = '%s.%s' % (interface_name, member_name)
+      key = '%s.%s' % (interface.id, member_name)
+      dom_name = '%s.%s' % (interface.javascript_binding_name, member_name)
     else:
-      key = interface_name
+      key = interface.id
+      dom_name = interface.javascript_binding_name
 
-    annotations = ["@DomName('" + key + "')"]
+    annotations = ["@DomName('" + dom_name + "')"]
 
     # Only add this for members, so we don't add DocsEditable to templated
     # classes (they get it from the default class template)
@@ -445,8 +461,9 @@
     if key in _annotations:
       annotations.extend(_annotations[key])
 
-    if (not member_name and interface_name.startswith('WebKit') and
-        interface_name not in html_interface_renames):
+    if (not member_name and
+        interface.javascript_binding_name.startswith('WebKit') and
+        interface.id not in html_interface_renames):
       annotations.extend(_webkit_experimental_annotations)
 
     if (member_name and member_name.startswith('webkit') and
@@ -457,11 +474,11 @@
       member_name = source_member_name
 
     # TODO(blois): Emit support level annotations
-    self._GetSupportLevelAnnotation(interface_name, member_name)
+    self._GetSupportLevelAnnotation(interface.id, member_name)
 
     return annotations
 
-  def _GetComments(self, library_name, interface_name, member_name=None):
+  def _GetComments(self, library_name, interface, member_name=None):
     """ Gets all comments for the interface or member and returns a list. """
 
     # Add documentation from JSON.
@@ -469,8 +486,8 @@
     library_name = 'dart.dom.%s' % library_name
     if library_name in self._doc_comments:
       library_info = self._doc_comments[library_name]
-      if interface_name in library_info:
-        interface_info = library_info[interface_name]
+      if interface.id in library_info:
+        interface_info = library_info[interface.id]
         if member_name:
           if 'members' in interface_info and member_name in interface_info['members']:
             comments = interface_info['members'][member_name]
diff --git a/tools/dom/scripts/databasebuilder.py b/tools/dom/scripts/databasebuilder.py
index f61f33d..6df1136 100755
--- a/tools/dom/scripts/databasebuilder.py
+++ b/tools/dom/scripts/databasebuilder.py
@@ -406,7 +406,7 @@
           old_interface = self._database.GetInterface(target)
           self._merge_interfaces(old_interface, interface, import_options)
         else:
-          raise Exception("Supplemental target '%s' not found", target)
+          _logger.warning("Supplemental target '%s' not found", target)
 
     # Step 4: Resolve 'implements' statements
     for impl_stmt, import_options in self._impl_stmts:
diff --git a/tools/dom/scripts/fremontcutbuilder.py b/tools/dom/scripts/fremontcutbuilder.py
index 8f440da..3b172b2 100755
--- a/tools/dom/scripts/fremontcutbuilder.py
+++ b/tools/dom/scripts/fremontcutbuilder.py
@@ -10,6 +10,8 @@
 import os.path
 import sys
 
+_logger = logging.getLogger('fremontcutbuilder')
+
 FEATURE_DISABLED = [
     'ENABLE_BATTERY_STATUS',
     'ENABLE_CSS3_CONDITIONAL_RULES',
@@ -107,12 +109,12 @@
 
   unused_conditionals = known_conditionals - conditionals_met
   if unused_conditionals:
-    raise Exception('There are some unused conditionals %s' %
+    _logger.warning('There are some unused conditionals %s' %
         sorted(unused_conditionals))
 
   unknown_conditionals = conditionals_met - known_conditionals
   if unknown_conditionals:
-    raise Exception('There are some unknown conditionals %s' %
+    _logger.warning('There are some unknown conditionals %s' %
         sorted(unknown_conditionals))
 
   db.Save()
diff --git a/tools/dom/scripts/generator.py b/tools/dom/scripts/generator.py
index c36ec9c..17db760 100644
--- a/tools/dom/scripts/generator.py
+++ b/tools/dom/scripts/generator.py
@@ -899,10 +899,7 @@
 
 
 def TypedListTypeData(item_type):
-  return TypeData(
-      clazz='TypedList',
-      dart_type='List<%s>' % item_type, # TODO(antonm): proper typed_data interfaces.
-      item_type=item_type)
+  return TypeData(clazz='TypedList', item_type=item_type)
 
 
 _idl_type_registry = monitored.Dict('generator._idl_type_registry', {
diff --git a/tools/dom/scripts/htmldartgenerator.py b/tools/dom/scripts/htmldartgenerator.py
index fb116d1..d22b6cd 100644
--- a/tools/dom/scripts/htmldartgenerator.py
+++ b/tools/dom/scripts/htmldartgenerator.py
@@ -326,6 +326,17 @@
       implements.append('List<%s>' % item_type_info.dart_type())
     return implements
 
+  def Mixins(self):
+    mixins = []
+    if self._interface_type_info.list_item_type():
+      item_type = self._type_registry.TypeInfo(
+          self._interface_type_info.list_item_type()).dart_type()
+      mixins.append('ListMixin<%s>' % item_type)
+      mixins.append('ImmutableListMixin<%s>' % item_type)
+
+    return mixins
+
+
   def AddConstructors(self,
       constructors, factory_name, factory_constructor_name):
     """ Adds all of the constructors.
@@ -346,7 +357,7 @@
       return
 
     metadata = self._metadata.GetFormattedMetadata(
-        self._library_name, self._interface.id, self._interface.id, '  ')
+        self._library_name, self._interface, self._interface.id, '  ')
 
     if not factory_constructor_name:
       factory_constructor_name = '_create'
@@ -524,8 +535,6 @@
     # TODO(sra): Use separate mixins for mutable implementations of List<T>.
     # TODO(sra): Use separate mixins for typed array implementations of List<T>.
     template_file = 'immutable_list_mixin.darttemplate'
-    has_contains = any(op.id == 'contains' for op in self._interface.operations)
-    has_clear = any(op.id == 'clear' for op in self._interface.operations)
     has_length = False
     has_length_setter = False
 
@@ -540,8 +549,6 @@
     template = self._template_loader.Load(
         template_file,
         {
-          'DEFINE_CONTAINS': not has_contains,
-          'DEFINE_CLEAR': not has_clear,
           'DEFINE_LENGTH_AS_NUM_ITEMS': not has_length and has_num_items,
           'DEFINE_LENGTH_SETTER': not has_length_setter,
         })
diff --git a/tools/dom/scripts/htmleventgenerator.py b/tools/dom/scripts/htmleventgenerator.py
index 4c0fac0..cdbade3 100644
--- a/tools/dom/scripts/htmleventgenerator.py
+++ b/tools/dom/scripts/htmleventgenerator.py
@@ -236,7 +236,7 @@
         continue
 
       annotations = self._metadata.FormatMetadata(
-          self._metadata.GetMetadata(library_name, interface.id,
+          self._metadata.GetMetadata(library_name, interface,
               annotation_name, 'on' + dom_name), '  ')
 
       members_emitter.Emit(
@@ -267,7 +267,7 @@
         provider = html_name + 'Event'
 
       annotations = self._metadata.GetFormattedMetadata(
-          library_name, interface.id, annotation_name, '  ')
+          library_name, interface, annotation_name, '  ')
 
       members_emitter.Emit(
           "\n"
diff --git a/tools/dom/scripts/htmlrenamer.py b/tools/dom/scripts/htmlrenamer.py
index 9073f98..25e66d6 100644
--- a/tools/dom/scripts/htmlrenamer.py
+++ b/tools/dom/scripts/htmlrenamer.py
@@ -235,6 +235,7 @@
   'HTMLTableRowElement.insertCell',
   'HTMLTableSectionElement.insertRow',
   'HTMLTableSectionElement.rows',
+  'HTMLTemplateElement.content',
   'IDBCursor.delete',
   'IDBCursor.update',
   'IDBDatabase.createObjectStore',
@@ -309,6 +310,7 @@
 # Members from the standard dom that exist in the dart:html library with
 # identical functionality but with cleaner names.
 renamed_html_members = monitored.Dict('htmlrenamer.renamed_html_members', {
+    'CSSStyleDeclaration.getPropertyValue': '_getPropertyValue',
     'DirectoryEntry.getDirectory': '_getDirectory',
     'DirectoryEntry.getFile': '_getFile',
     'Document.createCDATASection': 'createCDataSection',
@@ -328,6 +330,7 @@
     'Element.querySelector': 'query',
     'Element.webkitCreateShadowRoot': 'createShadowRoot',
     'Element.webkitMatchesSelector' : 'matches',
+    'MutationObserver.observe': '_observe',
     'Navigator.webkitGetUserMedia': '_getUserMedia',
     'Node.appendChild': 'append',
     'Node.cloneNode': 'clone',
diff --git a/tools/dom/scripts/systemhtml.py b/tools/dom/scripts/systemhtml.py
index 5473635..6adefad 100644
--- a/tools/dom/scripts/systemhtml.py
+++ b/tools/dom/scripts/systemhtml.py
@@ -184,6 +184,7 @@
   'HTMLTableElement': 'table',
   'HTMLTableRowElement': 'tr',
   #'HTMLTableSectionElement'  <thead> <tbody> <tfoot>
+  'HTMLTemplateElement': 'template',
   'HTMLTextAreaElement': 'textarea',
   'HTMLTitleElement': 'title',
   'HTMLTrackElement': 'track',
@@ -332,6 +333,7 @@
   'HTMLObjectElement',
   'HTMLOutputElement',
   'HTMLProgressElement',
+  'HTMLTemplateElement',
   'HTMLTrackElement',
 ]
 
@@ -382,6 +384,7 @@
     'FormData': "JS('bool', '!!(window.FormData)')",
     'HashChangeEvent': "Device.isEventTypeSupported('HashChangeEvent')",
     'HTMLShadowElement': ElemSupportStr('shadow'),
+    'HTMLTemplateElement': ElemSupportStr('template'),
     'MediaStreamEvent': "Device.isEventTypeSupported('MediaStreamEvent')",
     'MediaStreamTrackEvent': "Device.isEventTypeSupported('MediaStreamTrackEvent')",
     'NotificationCenter': "JS('bool', '!!(window.webkitNotifications)')",
@@ -520,8 +523,15 @@
     if implements:
       implements_str = ' implements ' + ', '.join(set(implements))
 
+    mixins = self._backend.Mixins()
+    mixins_str = ''
+    if mixins:
+      mixins_str = ' with ' + ', '.join(mixins)
+      if not base_class:
+        base_class = 'Object'
+
     annotations = self._metadata.GetFormattedMetadata(
-        self._library_name, self._interface.doc_js_name, '')
+        self._library_name, self._interface, None, '')
 
     class_modifiers = ''
     if self._renamer.ShouldSuppressInterface(self._interface):
@@ -535,6 +545,7 @@
         CLASSNAME=self._interface_type_info.implementation_name(),
         EXTENDS=' extends %s' % base_class if base_class else '',
         IMPLEMENTS=implements_str,
+        MIXINS=mixins_str,
         DOMNAME=self._interface.doc_js_name,
         NATIVESPEC=self._backend.NativeSpec())
     self._backend.StartInterface(self._implementation_members_emitter)
@@ -1036,7 +1047,7 @@
 
   def _Metadata(self, idl_type, idl_member_name, indent='  '):
     anns = self._metadata.GetDart2JSMetadata(
-        idl_type, self._library_name, self._interface.id, idl_member_name)
+        idl_type, self._library_name, self._interface, idl_member_name)
 
     if not self._metadata.AnyConversionAnnotations(
         idl_type, self._interface.id, idl_member_name):
diff --git a/tools/dom/scripts/systemnative.py b/tools/dom/scripts/systemnative.py
index bbd9be3..4c61854 100644
--- a/tools/dom/scripts/systemnative.py
+++ b/tools/dom/scripts/systemnative.py
@@ -826,7 +826,7 @@
     if emit_metadata:
       metadata = self._metadata.GetFormattedMetadata(
           self._renamer.GetLibraryName(self._interface),
-          self._interface.id, idl_name, '  ')
+          self._interface, idl_name, '  ')
 
     native_binding = '%s_%s_%s' % (self._interface.id, idl_name, native_suffix)
     self._members_emitter.Emit(
diff --git a/tools/dom/src/ImmutableListMixin.dart b/tools/dom/src/ImmutableListMixin.dart
new file mode 100644
index 0000000..df14b40
--- /dev/null
+++ b/tools/dom/src/ImmutableListMixin.dart
@@ -0,0 +1,77 @@
+// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of dart.dom.html;
+
+abstract class ImmutableListMixin<E> implements List<E> {
+  // From Iterable<$E>:
+  Iterator<E> get iterator {
+    // Note: NodeLists are not fixed size. And most probably length shouldn't
+    // be cached in both iterator _and_ forEach method. For now caching it
+    // for consistency.
+    return new FixedSizeListIterator<E>(this);
+  }
+
+  // From Collection<E>:
+  void add(E value) {
+    throw new UnsupportedError("Cannot add to immutable List.");
+  }
+
+  void addAll(Iterable<E> iterable) {
+    throw new UnsupportedError("Cannot add to immutable List.");
+  }
+
+  // From List<E>:
+  void sort([int compare(E a, E b)]) {
+    throw new UnsupportedError("Cannot sort immutable List.");
+  }
+
+  void insert(int index, E element) {
+    throw new UnsupportedError("Cannot add to immutable List.");
+  }
+
+  void insertAll(int index, Iterable<E> iterable) {
+    throw new UnsupportedError("Cannot add to immutable List.");
+  }
+
+  void setAll(int index, Iterable<E> iterable) {
+    throw new UnsupportedError("Cannot modify an immutable List.");
+  }
+
+  E removeAt(int pos) {
+    throw new UnsupportedError("Cannot remove from immutable List.");
+  }
+
+  E removeLast() {
+    throw new UnsupportedError("Cannot remove from immutable List.");
+  }
+
+  void remove(Object object) {
+    throw new UnsupportedError("Cannot remove from immutable List.");
+  }
+
+  void removeWhere(bool test(E element)) {
+    throw new UnsupportedError("Cannot remove from immutable List.");
+  }
+
+  void retainWhere(bool test(E element)) {
+    throw new UnsupportedError("Cannot remove from immutable List.");
+  }
+
+  void setRange(int start, int end, Iterable<E> iterable, [int skipCount]) {
+    throw new UnsupportedError("Cannot setRange on immutable List.");
+  }
+
+  void removeRange(int start, int end) {
+    throw new UnsupportedError("Cannot removeRange on immutable List.");
+  }
+
+  void replaceRange(int start, int end, Iterable<E> iterable) {
+    throw new UnsupportedError("Cannot modify an immutable List.");
+  }
+
+  void fillRange(int start, int end, [E fillValue]) {
+    throw new UnsupportedError("Cannot modify an immutable List.");
+  }
+}
diff --git a/tools/dom/src/ModelTreeObserver.dart b/tools/dom/src/ModelTreeObserver.dart
deleted file mode 100644
index 8e0bb41a..0000000
--- a/tools/dom/src/ModelTreeObserver.dart
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-part of html;
-
-class _ModelTreeObserver {
-  static bool _initialized = false;
-
-  /**
-   * Start an observer watching the document for tree changes to automatically
-   * propagate model changes.
-   *
-   * Currently this does not support propagation through Shadow DOMs.
-   */
-  static void initialize() {
-    if (!_initialized) {
-      _initialized = true;
-
-      if (MutationObserver.supported) {
-        var observer = new MutationObserver(_processTreeChange);
-        observer.observe(document, childList: true, subtree: true);
-      } else {
-        document.on['DOMNodeInserted'].listen(_handleNodeInserted);
-        document.on['DOMNodeRemoved'].listen(_handleNodeRemoved);
-      }
-    }
-  }
-
-  static void _processTreeChange(List<MutationRecord> mutations,
-      MutationObserver observer) {
-    for (var record in mutations) {
-      for (var node in record.addedNodes) {
-        // When nodes enter the document we need to make sure that all of the
-        // models are properly propagated through the entire sub-tree.
-        propagateModel(node, _calculatedModel(node), true);
-      }
-      for (var node in record.removedNodes) {
-        propagateModel(node, _calculatedModel(node), false);
-      }
-    }
-  }
-
-  static void _handleNodeInserted(MutationEvent e) {
-    var node = e.target;
-    window.setImmediate(() {
-      propagateModel(node, _calculatedModel(node), true);
-    });
-  }
-
-  static void _handleNodeRemoved(MutationEvent e) {
-    var node = e.target;
-    window.setImmediate(() {
-      propagateModel(node, _calculatedModel(node), false);
-    });
-  }
-
-  /**
-   * Figures out what the model should be for a node, avoiding any cached
-   * model values.
-   */
-  static _calculatedModel(node) {
-    if (node._hasLocalModel == true) {
-      return node._model;
-    } else if (node.parentNode != null) {
-      return node.parentNode._model;
-    }
-    return null;
-  }
-
-  /**
-   * Pushes model changes down through the tree.
-   *
-   * Set fullTree to true if the state of the tree is unknown and model changes
-   * should be propagated through the entire tree.
-   */
-  static void propagateModel(Node node, model, bool fullTree) {
-    // Calling into user code with the != call could generate exceptions.
-    // Catch and report them a global exceptions.
-    try {
-      if (node._hasLocalModel != true && node._model != model &&
-          node._modelChangedStreams != null &&
-          !node._modelChangedStreams.isEmpty) {
-        node._model = model;
-        node._modelChangedStreams.toList()
-          .forEach((controller) => controller.add(node));
-      }
-    } catch (e, s) {
-      new Future.error(e, s);
-    }
-    for (var child = node.$dom_firstChild; child != null;
-        child = child.nextNode) {
-      if (child._hasLocalModel != true) {
-        propagateModel(child, model, fullTree);
-      } else if (fullTree) {
-        propagateModel(child, child._model, true);
-      }
-    }
-  }
-}
diff --git a/tools/dom/src/PathObserver.dart b/tools/dom/src/PathObserver.dart
new file mode 100644
index 0000000..bf687cb
--- /dev/null
+++ b/tools/dom/src/PathObserver.dart
@@ -0,0 +1,286 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of html;
+
+// This code is inspired by ChangeSummary:
+// https://github.com/rafaelw/ChangeSummary/blob/master/change_summary.js
+// ...which underlies MDV. Since we don't need the functionality of
+// ChangeSummary, we just implement what we need for data bindings.
+// This allows our implementation to be much simpler.
+
+// TODO(jmesserly): should we make these types stronger, and require
+// Observable objects? Currently, it is fine to say something like:
+//     var path = new PathObserver(123, '');
+//     print(path.value); // "123"
+//
+// Furthermore this degenerate case is allowed:
+//     var path = new PathObserver(123, 'foo.bar.baz.qux');
+//     print(path.value); // "null"
+//
+// Here we see that any invalid (i.e. not Observable) value will break the
+// path chain without producing an error or exception.
+//
+// Now the real question: should we do this? For the former case, the behavior
+// is correct but we could chose to handle it in the dart:html bindings layer.
+// For the latter case, it might be better to throw an error so users can find
+// the problem.
+
+
+/**
+ * A data-bound path starting from a view-model or model object, for example
+ * `foo.bar.baz`.
+ *
+ * When the [values] stream is being listened to, this will observe changes to
+ * the object and any intermediate object along the path, and send [values]
+ * accordingly. When all listeners are unregistered it will stop observing
+ * the objects.
+ *
+ * This class is used to implement [Node.bind] and similar functionality.
+ */
+// TODO(jmesserly): find a better home for this type.
+@Experimental
+class PathObserver {
+  /** The object being observed. */
+  final object;
+
+  /** The path string. */
+  final String path;
+
+  /** True if the path is valid, otherwise false. */
+  final bool _isValid;
+
+  // TODO(jmesserly): same issue here as ObservableMixin: is there an easier
+  // way to get a broadcast stream?
+  StreamController _values;
+  Stream _valueStream;
+
+  _PropertyObserver _observer, _lastObserver;
+
+  Object _lastValue;
+  bool _scheduled = false;
+
+  /**
+   * Observes [path] on [object] for changes. This returns an object that can be
+   * used to get the changes and get/set the value at this path.
+   * See [PathObserver.values] and [PathObserver.value].
+   */
+  PathObserver(this.object, String path)
+    : path = path, _isValid = _isPathValid(path) {
+
+    // TODO(jmesserly): if the path is empty, or the object is! Observable, we
+    // can optimize the PathObserver to be more lightweight.
+
+    _values = new StreamController(onListen: _observe, onCancel: _unobserve);
+
+    if (_isValid) {
+      var segments = [];
+      for (var segment in path.trim().split('.')) {
+        if (segment == '') continue;
+        var index = int.parse(segment, onError: (_) {});
+        segments.add(index != null ? index : new Symbol(segment));
+      }
+
+      // Create the property observer linked list.
+      // Note that the structure of a path can't change after it is initially
+      // constructed, even though the objects along the path can change.
+      for (int i = segments.length - 1; i >= 0; i--) {
+        _observer = new _PropertyObserver(this, segments[i], _observer);
+        if (_lastObserver == null) _lastObserver = _observer;
+      }
+    }
+  }
+
+  // TODO(jmesserly): we could try adding the first value to the stream, but
+  // that delivers the first record async.
+  /**
+   * Listens to the stream, and invokes the [callback] immediately with the
+   * current [value]. This is useful for bindings, which want to be up-to-date
+   * immediately.
+   */
+  StreamSubscription bindSync(void callback(value)) {
+    var result = values.listen(callback);
+    callback(value);
+    return result;
+  }
+
+  // TODO(jmesserly): should this be a change record with the old value?
+  // TODO(jmesserly): should this be a broadcast stream? We only need
+  // single-subscription in the bindings system, so single sub saves overhead.
+  /**
+   * Gets the stream of values that were observed at this path.
+   * This returns a single-subscription stream.
+   */
+  Stream get values => _values.stream;
+
+  /** Force synchronous delivery of [values]. */
+  void _deliverValues() {
+    _scheduled = false;
+
+    var newValue = value;
+    if (!identical(_lastValue, newValue)) {
+      _values.add(newValue);
+      _lastValue = newValue;
+    }
+  }
+
+  void _observe() {
+    if (_observer != null) {
+      _lastValue = value;
+      _observer.observe();
+    }
+  }
+
+  void _unobserve() {
+    if (_observer != null) _observer.unobserve();
+  }
+
+  void _notifyChange() {
+    if (_scheduled) return;
+    _scheduled = true;
+
+    // TODO(jmesserly): should we have a guarenteed order with respect to other
+    // paths? If so, we could implement this fairly easily by sorting instances
+    // of this class by birth order before delivery.
+    queueChangeRecords(_deliverValues);
+  }
+
+  /** Gets the last reported value at this path. */
+  get value {
+    if (!_isValid) return null;
+    if (_observer == null) return object;
+    _observer.ensureValue(object);
+    return _lastObserver.value;
+  }
+
+  /** Sets the value at this path. */
+  void set value(Object value) {
+    // TODO(jmesserly): throw if property cannot be set?
+    // MDV seems tolerant of these error.
+    if (_observer == null || !_isValid) return;
+    _observer.ensureValue(object);
+    var last = _lastObserver;
+    if (_setObjectProperty(last._object, last._property, value)) {
+      // Technically, this would get updated asynchronously via a change record.
+      // However, it is nice if calling the getter will yield the same value
+      // that was just set. So we use this opportunity to update our cache.
+      last.value = value;
+    }
+  }
+}
+
+// TODO(jmesserly): these should go away in favor of mirrors!
+_getObjectProperty(object, property) {
+  if (object is List && property is int) {
+    if (property >= 0 && property < object.length) {
+      return object[property];
+    } else {
+      return null;
+    }
+  }
+
+  // TODO(jmesserly): what about length?
+  if (object is Map) return object[property];
+
+  if (object is Observable) return object.getValueWorkaround(property);
+
+  return null;
+}
+
+bool _setObjectProperty(object, property, value) {
+  if (object is List && property is int) {
+    object[property] = value;
+  } else if (object is Map) {
+    object[property] = value;
+  } else if (object is Observable) {
+    (object as Observable).setValueWorkaround(property, value);
+  } else {
+    return false;
+  }
+  return true;
+}
+
+
+class _PropertyObserver {
+  final PathObserver _path;
+  final _property;
+  final _PropertyObserver _next;
+
+  // TODO(jmesserly): would be nice not to store both of these.
+  Object _object;
+  Object _value;
+  StreamSubscription _sub;
+
+  _PropertyObserver(this._path, this._property, this._next);
+
+  get value => _value;
+
+  void set value(Object newValue) {
+    _value = newValue;
+    if (_next != null) {
+      if (_sub != null) _next.unobserve();
+      _next.ensureValue(_value);
+      if (_sub != null) _next.observe();
+    }
+  }
+
+  void ensureValue(object) {
+    // If we're observing, values should be up to date already.
+    if (_sub != null) return;
+
+    _object = object;
+    value = _getObjectProperty(object, _property);
+  }
+
+  void observe() {
+    if (_object is Observable) {
+      assert(_sub == null);
+      _sub = (_object as Observable).changes.listen(_onChange);
+    }
+    if (_next != null) _next.observe();
+  }
+
+  void unobserve() {
+    if (_sub == null) return;
+
+    _sub.cancel();
+    _sub = null;
+    if (_next != null) _next.unobserve();
+  }
+
+  void _onChange(List<ChangeRecord> changes) {
+    for (var change in changes) {
+      // TODO(jmesserly): what to do about "new Symbol" here?
+      // Ideally this would only preserve names if the user has opted in to
+      // them being preserved.
+      // TODO(jmesserly): should we drop observable maps with String keys?
+      // If so then we only need one check here.
+      if (change.changes(_property)) {
+        value = _getObjectProperty(_object, _property);
+        _path._notifyChange();
+        return;
+      }
+    }
+  }
+}
+
+// From: https://github.com/rafaelw/ChangeSummary/blob/master/change_summary.js
+
+const _pathIndentPart = r'[$a-z0-9_]+[$a-z0-9_\d]*';
+final _pathRegExp = new RegExp('^'
+    '(?:#?' + _pathIndentPart + ')?'
+    '(?:'
+      '(?:\\.' + _pathIndentPart + ')'
+    ')*'
+    r'$', caseSensitive: false);
+
+final _spacesRegExp = new RegExp(r'\s');
+
+bool _isPathValid(String s) {
+  s = s.replaceAll(_spacesRegExp, '');
+
+  if (s == '') return true;
+  if (s[0] == '.') return false;
+  return _pathRegExp.hasMatch(s);
+}
diff --git a/tools/dom/src/TemplateBindings.dart b/tools/dom/src/TemplateBindings.dart
new file mode 100644
index 0000000..f0a57e9
--- /dev/null
+++ b/tools/dom/src/TemplateBindings.dart
@@ -0,0 +1,776 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of html;
+
+// This code is a port of Model-Driven-Views:
+// https://github.com/toolkitchen/mdv
+// The code mostly comes from src/template_element.js
+
+typedef void _ChangeHandler(value);
+
+/**
+ * Model-Driven Views (MDV)'s native features enables a wide-range of use cases,
+ * but (by design) don't attempt to implement a wide array of specialized
+ * behaviors.
+ *
+ * Enabling these features in MDV is a matter of implementing and registering an
+ * MDV Custom Syntax. A Custom Syntax is an object which contains one or more
+ * delegation functions which implement specialized behavior. This object is
+ * registered with MDV via [TemplateElement.syntax]:
+ *
+ *
+ * HTML:
+ *     <template bind syntax="MySyntax">
+ *       {{ What!Ever('crazy')->thing^^^I+Want(data) }}
+ *     </template>
+ *
+ * Dart:
+ *     class MySyntax extends CustomBindingSyntax {
+ *       getBinding(model, path, name, node) {
+ *         // The magic happens here!
+ *       }
+ *     }
+ *
+ *     ...
+ *
+ *     TemplateElement.syntax['MySyntax'] = new MySyntax();
+ *
+ * See <https://github.com/toolkitchen/mdv/blob/master/docs/syntax.md> for more
+ * information about Custom Syntax.
+ */
+// TODO(jmesserly): if this is just one method, a function type would make it
+// more Dart-friendly.
+@Experimental
+abstract class CustomBindingSyntax {
+  // TODO(jmesserly): I had to remove type annotations from "name" and "node"
+  // Normally they are String and Node respectively. But sometimes it will pass
+  // (int name, CompoundBinding node). That seems very confusing; we may want
+  // to change this API.
+  getBinding(model, String path, name, node);
+}
+
+/** The callback used in the [CompoundBinding.combinator] field. */
+@Experimental
+typedef Object CompoundBindingCombinator(Map objects);
+
+/** Information about the instantiated template. */
+@Experimental
+class TemplateInstance {
+  // TODO(rafaelw): firstNode & lastNode should be read-synchronous
+  // in cases where script has modified the template instance boundary.
+
+  /** The first node of this template instantiation. */
+  final Node firstNode;
+
+  /**
+   * The last node of this template instantiation.
+   * This could be identical to [firstNode] if the template only expanded to a
+   * single node.
+   */
+  final Node lastNode;
+
+  /** The model used to instantiate the template. */
+  final model;
+
+  TemplateInstance(this.firstNode, this.lastNode, this.model);
+}
+
+/**
+ * Model-Driven Views contains a helper object which is useful for the
+ * implementation of a Custom Syntax.
+ *
+ *     var binding = new CompoundBinding((values) {
+ *       var combinedValue;
+ *       // compute combinedValue based on the current values which are provided
+ *       return combinedValue;
+ *     });
+ *     binding.bind('name1', obj1, path1);
+ *     binding.bind('name2', obj2, path2);
+ *     //...
+ *     binding.bind('nameN', objN, pathN);
+ *
+ * CompoundBinding is an object which knows how to listen to multiple path
+ * values (registered via [bind]) and invoke its [combinator] when one or more
+ * of the values have changed and set its [value] property to the return value
+ * of the function. When any value has changed, all current values are provided
+ * to the [combinator] in the single `values` argument.
+ *
+ * See [CustomBindingSyntax] for more information.
+ */
+// TODO(jmesserly): what is the public API surface here? I just guessed;
+// most of it seemed non-public.
+@Experimental
+class CompoundBinding extends ObservableBase {
+  CompoundBindingCombinator _combinator;
+
+  // TODO(jmesserly): ideally these would be String keys, but sometimes we
+  // use integers.
+  Map<dynamic, StreamSubscription> _bindings = new Map();
+  Map _values = new Map();
+  bool _scheduled = false;
+  bool _disposed = false;
+  Object _value;
+
+  CompoundBinding([CompoundBindingCombinator combinator]) {
+    // TODO(jmesserly): this is a tweak to the original code, it seemed to me
+    // that passing the combinator to the constructor should be equivalent to
+    // setting it via the property.
+    // I also added a null check to the combinator setter.
+    this.combinator = combinator;
+  }
+
+  CompoundBindingCombinator get combinator => _combinator;
+
+  set combinator(CompoundBindingCombinator combinator) {
+    _combinator = combinator;
+    if (combinator != null) _scheduleResolve();
+  }
+
+  static const _VALUE = const Symbol('value');
+
+  get value => _value;
+
+  void set value(newValue) {
+    _value = notifyPropertyChange(_VALUE, _value, newValue);
+  }
+
+  // TODO(jmesserly): remove these workarounds when dart2js supports mirrors!
+  getValueWorkaround(key) {
+    if (key == _VALUE) return value;
+    return null;
+  }
+  setValueWorkaround(key, val) {
+    if (key == _VALUE) value = val;
+  }
+
+  void bind(name, model, String path) {
+    unbind(name);
+
+    _bindings[name] = new PathObserver(model, path).bindSync((value) {
+      _values[name] = value;
+      _scheduleResolve();
+    });
+  }
+
+  void unbind(name, {bool suppressResolve: false}) {
+    var binding = _bindings.remove(name);
+    if (binding == null) return;
+
+    binding.cancel();
+    _values.remove(name);
+    if (!suppressResolve) _scheduleResolve();
+  }
+
+  // TODO(rafaelw): Is this the right processing model?
+  // TODO(rafaelw): Consider having a seperate ChangeSummary for
+  // CompoundBindings so to excess dirtyChecks.
+  void _scheduleResolve() {
+    if (_scheduled) return;
+    _scheduled = true;
+    queueChangeRecords(resolve);
+  }
+
+  void resolve() {
+    if (_disposed) return;
+    _scheduled = false;
+
+    if (_combinator == null) {
+      throw new StateError(
+          'CompoundBinding attempted to resolve without a combinator');
+    }
+
+    value = _combinator(_values);
+  }
+
+  void dispose() {
+    for (var binding in _bindings.values) {
+      binding.cancel();
+    }
+    _bindings.clear();
+    _values.clear();
+
+    _disposed = true;
+    value = null;
+  }
+}
+
+Stream<Event> _getStreamForInputType(InputElement element) {
+  switch (element.type) {
+    case 'checkbox':
+      return element.onClick;
+    case 'radio':
+    case 'select-multiple':
+    case 'select-one':
+      return element.onChange;
+    default:
+      return element.onInput;
+  }
+}
+
+abstract class _InputBinding {
+  final InputElement element;
+  PathObserver binding;
+  StreamSubscription _pathSub;
+  StreamSubscription _eventSub;
+
+  _InputBinding(this.element, model, String path) {
+    binding = new PathObserver(model, path);
+    _pathSub = binding.bindSync(valueChanged);
+    _eventSub = _getStreamForInputType(element).listen(updateBinding);
+  }
+
+  void valueChanged(newValue);
+
+  void updateBinding(e);
+
+  void unbind() {
+    binding = null;
+    _pathSub.cancel();
+    _eventSub.cancel();
+  }
+}
+
+class _ValueBinding extends _InputBinding {
+  _ValueBinding(element, model, path) : super(element, model, path);
+
+  void valueChanged(value) {
+    element.value = value == null ? '' : '$value';
+  }
+
+  void updateBinding(e) {
+    binding.value = element.value;
+  }
+}
+
+// TODO(jmesserly): not sure what kind of boolean conversion rules to
+// apply for template data-binding. HTML attributes are true if they're present.
+// However Dart only treats "true" as true. Since this is HTML we'll use
+// something closer to the HTML rules: null (missing) and false are false,
+// everything else is true. See: https://github.com/toolkitchen/mdv/issues/59
+bool _templateBooleanConversion(value) => null != value && false != value;
+
+class _CheckedBinding extends _InputBinding {
+  _CheckedBinding(element, model, path) : super(element, model, path);
+
+  void valueChanged(value) {
+    element.checked = _templateBooleanConversion(value);
+  }
+
+  void updateBinding(e) {
+    binding.value = element.checked;
+
+    // Only the radio button that is getting checked gets an event. We
+    // therefore find all the associated radio buttons and update their
+    // CheckedBinding manually.
+    if (element is InputElement && element.type == 'radio') {
+      for (var r in _getAssociatedRadioButtons(element)) {
+        var checkedBinding = r._checkedBinding;
+        if (checkedBinding != null) {
+          // Set the value directly to avoid an infinite call stack.
+          checkedBinding.binding.value = false;
+        }
+      }
+    }
+  }
+}
+
+// TODO(jmesserly): polyfill document.contains API instead of doing it here
+bool _isNodeInDocument(Node node) {
+  // On non-IE this works:
+  // return node.document.contains(node);
+  var document = node.document;
+  if (node == document || node.parentNode == document) return true;
+  return document.documentElement.contains(node);
+}
+
+// |element| is assumed to be an HTMLInputElement with |type| == 'radio'.
+// Returns an array containing all radio buttons other than |element| that
+// have the same |name|, either in the form that |element| belongs to or,
+// if no form, in the document tree to which |element| belongs.
+//
+// This implementation is based upon the HTML spec definition of a
+// "radio button group":
+//   http://www.whatwg.org/specs/web-apps/current-work/multipage/number-state.html#radio-button-group
+//
+Iterable _getAssociatedRadioButtons(element) {
+  if (!_isNodeInDocument(element)) return [];
+  if (element.form != null) {
+    return element.form.nodes.where((el) {
+      return el != element &&
+          el is InputElement &&
+          el.type == 'radio' &&
+          el.name == element.name;
+    });
+  } else {
+    var radios = element.document.queryAll(
+        'input[type="radio"][name="${element.name}"]');
+    return radios.where((el) => el != element && el.form == null);
+  }
+}
+
+Node _createDeepCloneAndDecorateTemplates(Node node, String syntax) {
+  var clone = node.clone(false); // Shallow clone.
+  if (clone is Element && clone.isTemplate) {
+    TemplateElement.decorate(clone, node);
+    if (syntax != null) {
+      clone.attributes.putIfAbsent('syntax', () => syntax);
+    }
+  }
+
+  for (var c = node.$dom_firstChild; c != null; c = c.nextNode) {
+    clone.append(_createDeepCloneAndDecorateTemplates(c, syntax));
+  }
+  return clone;
+}
+
+// http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html#dfn-template-contents-owner
+Document _getTemplateContentsOwner(Document doc) {
+  if (doc.window == null) {
+    return doc;
+  }
+  var d = doc._templateContentsOwner;
+  if (d == null) {
+    // TODO(arv): This should either be a Document or HTMLDocument depending
+    // on doc.
+    d = doc.implementation.createHtmlDocument('');
+    while (d.$dom_lastChild != null) {
+      d.$dom_lastChild.remove();
+    }
+    doc._templateContentsOwner = d;
+  }
+  return d;
+}
+
+Element _cloneAndSeperateAttributeTemplate(Element templateElement) {
+  var clone = templateElement.clone(false);
+  var attributes = templateElement.attributes;
+  for (var name in attributes.keys.toList()) {
+    switch (name) {
+      case 'template':
+      case 'repeat':
+      case 'bind':
+      case 'ref':
+        clone.attributes.remove(name);
+        break;
+      default:
+        attributes.remove(name);
+        break;
+    }
+  }
+
+  return clone;
+}
+
+void _liftNonNativeTemplateChildrenIntoContent(Element templateElement) {
+  var content = templateElement.content;
+
+  if (!templateElement._isAttributeTemplate) {
+    var child;
+    while ((child = templateElement.$dom_firstChild) != null) {
+      content.append(child);
+    }
+    return;
+  }
+
+  // For attribute templates we copy the whole thing into the content and
+  // we move the non template attributes into the content.
+  //
+  //   <tr foo template>
+  //
+  // becomes
+  //
+  //   <tr template>
+  //   + #document-fragment
+  //     + <tr foo>
+  //
+  var newRoot = _cloneAndSeperateAttributeTemplate(templateElement);
+  var child;
+  while ((child = templateElement.$dom_firstChild) != null) {
+    newRoot.append(child);
+  }
+  content.append(newRoot);
+}
+
+void _bootstrapTemplatesRecursivelyFrom(Node node) {
+  void bootstrap(template) {
+    if (!TemplateElement.decorate(template)) {
+      _bootstrapTemplatesRecursivelyFrom(template.content);
+    }
+  }
+
+  // Need to do this first as the contents may get lifted if |node| is
+  // template.
+  // TODO(jmesserly): node is DocumentFragment or Element
+  var templateDescendents = (node as dynamic).queryAll(_allTemplatesSelectors);
+  if (node is Element && node.isTemplate) bootstrap(node);
+
+  templateDescendents.forEach(bootstrap);
+}
+
+final String _allTemplatesSelectors = 'template, option[template], ' +
+    Element._TABLE_TAGS.keys.map((k) => "$k[template]").join(", ");
+
+void _addBindings(Node node, model, [CustomBindingSyntax syntax]) {
+  if (node is Element) {
+    _addAttributeBindings(node, model, syntax);
+  } else if (node is Text) {
+    _parseAndBind(node, node.text, 'text', model, syntax);
+  }
+
+  for (var c = node.$dom_firstChild; c != null; c = c.nextNode) {
+    _addBindings(c, model, syntax);
+  }
+}
+
+
+void _addAttributeBindings(Element element, model, syntax) {
+  element.attributes.forEach((name, value) {
+    if (value == '' && (name == 'bind' || name == 'repeat')) {
+      value = '{{}}';
+    }
+    _parseAndBind(element, value, name, model, syntax);
+  });
+}
+
+void _parseAndBind(Node node, String text, String name, model,
+    CustomBindingSyntax syntax) {
+
+  var tokens = _parseMustacheTokens(text);
+  if (tokens.length == 0 || (tokens.length == 1 && tokens[0].isText)) {
+    return;
+  }
+
+  if (tokens.length == 1 && tokens[0].isBinding) {
+    _bindOrDelegate(node, name, model, tokens[0].value, syntax);
+    return;
+  }
+
+  var replacementBinding = new CompoundBinding();
+  for (var i = 0; i < tokens.length; i++) {
+    var token = tokens[i];
+    if (token.isBinding) {
+      _bindOrDelegate(replacementBinding, i, model, token.value, syntax);
+    }
+  }
+
+  replacementBinding.combinator = (values) {
+    var newValue = new StringBuffer();
+
+    for (var i = 0; i < tokens.length; i++) {
+      var token = tokens[i];
+      if (token.isText) {
+        newValue.write(token.value);
+      } else {
+        var value = values[i];
+        if (value != null) {
+          newValue.write(value);
+        }
+      }
+    }
+
+    return newValue.toString();
+  };
+
+  node.bind(name, replacementBinding, 'value');
+}
+
+void _bindOrDelegate(node, name, model, String path,
+    CustomBindingSyntax syntax) {
+
+  if (syntax != null) {
+    var delegateBinding = syntax.getBinding(model, path, name, node);
+    if (delegateBinding != null) {
+      model = delegateBinding;
+      path = 'value';
+    }
+  }
+
+  node.bind(name, model, path);
+}
+
+class _BindingToken {
+  final String value;
+  final bool isBinding;
+
+  _BindingToken(this.value, {this.isBinding: false});
+
+  bool get isText => !isBinding;
+}
+
+List<_BindingToken> _parseMustacheTokens(String s) {
+  var result = [];
+  var length = s.length;
+  var index = 0, lastIndex = 0;
+  while (lastIndex < length) {
+    index = s.indexOf('{{', lastIndex);
+    if (index < 0) {
+      result.add(new _BindingToken(s.substring(lastIndex)));
+      break;
+    } else {
+      // There is a non-empty text run before the next path token.
+      if (index > 0 && lastIndex < index) {
+        result.add(new _BindingToken(s.substring(lastIndex, index)));
+      }
+      lastIndex = index + 2;
+      index = s.indexOf('}}', lastIndex);
+      if (index < 0) {
+        var text = s.substring(lastIndex - 2);
+        if (result.length > 0 && result.last.isText) {
+          result.last.value += text;
+        } else {
+          result.add(new _BindingToken(text));
+        }
+        break;
+      }
+
+      var value = s.substring(lastIndex, index).trim();
+      result.add(new _BindingToken(value, isBinding: true));
+      lastIndex = index + 2;
+    }
+  }
+  return result;
+}
+
+void _addTemplateInstanceRecord(fragment, model) {
+  if (fragment.$dom_firstChild == null) {
+    return;
+  }
+
+  var instanceRecord = new TemplateInstance(
+      fragment.$dom_firstChild, fragment.$dom_lastChild, model);
+
+  var node = instanceRecord.firstNode;
+  while (node != null) {
+    node._templateInstance = instanceRecord;
+    node = node.nextNode;
+  }
+}
+
+void _removeAllBindingsRecursively(Node node) {
+  node.unbindAll();
+  for (var c = node.$dom_firstChild; c != null; c = c.nextNode) {
+    _removeAllBindingsRecursively(c);
+  }
+}
+
+void _removeTemplateChild(Node parent, Node child) {
+  child._templateInstance = null;
+  if (child is Element && child.isTemplate) {
+    // Make sure we stop observing when we remove an element.
+    var templateIterator = child._templateIterator;
+    if (templateIterator != null) {
+      templateIterator.abandon();
+      child._templateIterator = null;
+    }
+  }
+  child.remove();
+  _removeAllBindingsRecursively(child);
+}
+
+class _InstanceCursor {
+  final Element _template;
+  Node _terminator;
+  Node _previousTerminator;
+  int _previousIndex = -1;
+  int _index = 0;
+
+  _InstanceCursor(this._template, [index]) {
+    _terminator = _template;
+    if (index != null) {
+      while (index-- > 0) {
+        next();
+      }
+    }
+  }
+
+  void next() {
+    _previousTerminator = _terminator;
+    _previousIndex = _index;
+    _index++;
+
+    while (_index > _terminator._instanceTerminatorCount) {
+      _index -= _terminator._instanceTerminatorCount;
+      _terminator = _terminator.nextNode;
+      if (_terminator is Element && _terminator.tagName == 'TEMPLATE') {
+        _index += _instanceCount(_terminator);
+      }
+    }
+  }
+
+  void abandon() {
+    assert(_instanceCount(_template) > 0);
+    assert(_terminator._instanceTerminatorCount > 0);
+    assert(_index > 0);
+
+    _terminator._instanceTerminatorCount--;
+    _index--;
+  }
+
+  void insert(fragment) {
+    assert(_template.parentNode != null);
+
+    _previousTerminator = _terminator;
+    _previousIndex = _index;
+    _index++;
+
+    _terminator = fragment.$dom_lastChild;
+    if (_terminator == null) _terminator = _previousTerminator;
+    _template.parentNode.insertBefore(fragment, _previousTerminator.nextNode);
+
+    _terminator._instanceTerminatorCount++;
+    if (_terminator != _previousTerminator) {
+      while (_previousTerminator._instanceTerminatorCount >
+              _previousIndex) {
+        _previousTerminator._instanceTerminatorCount--;
+        _terminator._instanceTerminatorCount++;
+      }
+    }
+  }
+
+  void remove() {
+    assert(_previousIndex != -1);
+    assert(_previousTerminator != null &&
+           (_previousIndex > 0 || _previousTerminator == _template));
+    assert(_terminator != null && _index > 0);
+    assert(_template.parentNode != null);
+    assert(_instanceCount(_template) > 0);
+
+    if (_previousTerminator == _terminator) {
+      assert(_index == _previousIndex + 1);
+      _terminator._instanceTerminatorCount--;
+      _terminator = _template;
+      _previousTerminator = null;
+      _previousIndex = -1;
+      return;
+    }
+
+    _terminator._instanceTerminatorCount--;
+
+    var parent = _template.parentNode;
+    while (_previousTerminator.nextNode != _terminator) {
+      _removeTemplateChild(parent, _previousTerminator.nextNode);
+    }
+    _removeTemplateChild(parent, _terminator);
+
+    _terminator = _previousTerminator;
+    _index = _previousIndex;
+    _previousTerminator = null;
+    _previousIndex = -1;  // 0?
+  }
+}
+
+
+class _TemplateIterator {
+  final Element _templateElement;
+  int instanceCount = 0;
+  List iteratedValue;
+  bool observing = false;
+  final CompoundBinding inputs;
+
+  StreamSubscription _sub;
+  StreamSubscription _valueBinding;
+
+  _TemplateIterator(this._templateElement)
+    : inputs = new CompoundBinding(resolveInputs) {
+
+    _valueBinding = new PathObserver(inputs, 'value').bindSync(valueChanged);
+  }
+
+  static Object resolveInputs(Map values) {
+    if (values.containsKey('if') && !_templateBooleanConversion(values['if'])) {
+      return null;
+    }
+
+    if (values.containsKey('repeat')) {
+      return values['repeat'];
+    }
+
+    if (values.containsKey('bind')) {
+      return [values['bind']];
+    }
+
+    return null;
+  }
+
+  void valueChanged(value) {
+    clear();
+    if (value is! List) return;
+
+    iteratedValue = value;
+
+    if (value is Observable) {
+      _sub = value.changes.listen(_handleChanges);
+    }
+
+    int len = iteratedValue.length;
+    if (len > 0) {
+      _handleChanges([new ListChangeRecord(0, addedCount: len)]);
+    }
+  }
+
+  // TODO(jmesserly): port MDV v3.
+  getInstanceModel(model, syntax) => model;
+  getInstanceFragment(syntax) => _templateElement.createInstance();
+
+  void _handleChanges(List<ListChangeRecord> splices) {
+    var syntax = TemplateElement.syntax[_templateElement.attributes['syntax']];
+
+    for (var splice in splices) {
+      if (splice is! ListChangeRecord) continue;
+
+      for (int i = 0; i < splice.removedCount; i++) {
+        var cursor = new _InstanceCursor(_templateElement, splice.index + 1);
+        cursor.remove();
+        instanceCount--;
+      }
+
+      for (var addIndex = splice.index;
+          addIndex < splice.index + splice.addedCount;
+          addIndex++) {
+
+        var model = getInstanceModel(iteratedValue[addIndex], syntax);
+        var fragment = getInstanceFragment(syntax);
+
+        _addBindings(fragment, model, syntax);
+        _addTemplateInstanceRecord(fragment, model);
+
+        var cursor = new _InstanceCursor(_templateElement, addIndex);
+        cursor.insert(fragment);
+        instanceCount++;
+      }
+    }
+  }
+
+  void unobserve() {
+    if (_sub == null) return;
+    _sub.cancel();
+    _sub = null;
+  }
+
+  void clear() {
+    unobserve();
+
+    iteratedValue = null;
+    if (instanceCount == 0) return;
+
+    for (var i = 0; i < instanceCount; i++) {
+      var cursor = new _InstanceCursor(_templateElement, 1);
+      cursor.remove();
+    }
+
+    instanceCount = 0;
+  }
+
+  void abandon() {
+    unobserve();
+    _valueBinding.cancel();
+    inputs.dispose();
+  }
+}
+
+int _instanceCount(Element element) {
+  var templateIterator = element._templateIterator;
+  return templateIterator != null ? templateIterator.instanceCount : 0;
+}
diff --git a/tools/dom/src/dart2js_Conversions.dart b/tools/dom/src/dart2js_Conversions.dart
index 9f30f09..1c51b29 100644
--- a/tools/dom/src/dart2js_Conversions.dart
+++ b/tools/dom/src/dart2js_Conversions.dart
@@ -21,6 +21,7 @@
 }
 
 WindowBase _convertNativeToDart_Window(win) {
+  if (win == null) return null;
   return _DOMWindowCrossFrame._createSafe(win);
 }
 
diff --git a/tools/dom/src/native_DOMPublic.dart b/tools/dom/src/native_DOMPublic.dart
index 4034d91..5e96fcd 100644
--- a/tools/dom/src/native_DOMPublic.dart
+++ b/tools/dom/src/native_DOMPublic.dart
@@ -5,7 +5,7 @@
 part of html;
 
 _makeSendPortFuture(spawnRequest) {
-  final completer = new Completer<SendPort>();
+  final completer = new Completer<SendPort>.sync();
   final port = new ReceivePort();
   port.receive((result, _) {
     completer.complete(result);
diff --git a/tools/dom/templates/dart2js_impl.darttemplate b/tools/dom/templates/dart2js_impl.darttemplate
index 6195077..98c247c 100644
--- a/tools/dom/templates/dart2js_impl.darttemplate
+++ b/tools/dom/templates/dart2js_impl.darttemplate
@@ -5,5 +5,5 @@
 part of $LIBRARYNAME;
 
 @DocsEditable
-$(ANNOTATIONS)$(CLASS_MODIFIERS)class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC {
+$(ANNOTATIONS)$(CLASS_MODIFIERS)class $CLASSNAME$EXTENDS$MIXINS$IMPLEMENTS$NATIVESPEC {
 $!MEMBERS}
diff --git a/tools/dom/templates/html/dart2js/html_dart2js.darttemplate b/tools/dom/templates/html/dart2js/html_dart2js.darttemplate
index 47a9861..9021a7d 100644
--- a/tools/dom/templates/html/dart2js/html_dart2js.darttemplate
+++ b/tools/dom/templates/html/dart2js/html_dart2js.darttemplate
@@ -11,12 +11,13 @@
 
 import 'dart:async';
 import 'dart:collection';
-import 'dart:_collection-dev';
+import 'dart:_collection-dev' hide Symbol;
 import 'dart:html_common';
 import 'dart:indexed_db';
 import 'dart:isolate';
 import 'dart:json' as json;
 import 'dart:math';
+import 'dart:mdv_observe_impl';
 import 'dart:typed_data';
 // Not actually used, but imported since dart:html can generate these objects.
 import 'dart:svg' as svg;
@@ -35,14 +36,16 @@
 part '$AUXILIARY_DIR/CssClassSet.dart';
 part '$AUXILIARY_DIR/EventListener.dart';
 part '$AUXILIARY_DIR/EventStreamProvider.dart';
+part '$AUXILIARY_DIR/ImmutableListMixin.dart';
 part '$AUXILIARY_DIR/KeyboardEventStream.dart';
 part '$AUXILIARY_DIR/KeyCode.dart';
 part '$AUXILIARY_DIR/KeyLocation.dart';
 part '$AUXILIARY_DIR/KeyName.dart';
-part '$AUXILIARY_DIR/ModelTreeObserver.dart';
+part '$AUXILIARY_DIR/PathObserver.dart';
 part '$AUXILIARY_DIR/Point.dart';
 part '$AUXILIARY_DIR/ReadyState.dart';
 part '$AUXILIARY_DIR/Rectangle.dart';
+part '$AUXILIARY_DIR/TemplateBindings.dart';
 part '$AUXILIARY_DIR/_HttpRequestUtils.dart';
 part '$AUXILIARY_DIR/Isolates.dart';
 part '$AUXILIARY_DIR/Microtask.dart';
diff --git a/tools/dom/templates/html/dartium/dart_implementation.darttemplate b/tools/dom/templates/html/dartium/dart_implementation.darttemplate
index 3b2c20f..5213adb 100644
--- a/tools/dom/templates/html/dartium/dart_implementation.darttemplate
+++ b/tools/dom/templates/html/dartium/dart_implementation.darttemplate
@@ -7,6 +7,6 @@
 part of $LIBRARYNAME;
 
 @DocsEditable
-$(ANNOTATIONS)$(CLASS_MODIFIERS)class $CLASSNAME$EXTENDS$IMPLEMENTS {
+$(ANNOTATIONS)$(CLASS_MODIFIERS)class $CLASSNAME$EXTENDS$MIXINS$IMPLEMENTS {
 $!MEMBERS
 }
diff --git a/tools/dom/templates/html/dartium/html_dartium.darttemplate b/tools/dom/templates/html/dartium/html_dartium.darttemplate
index 07780e3..3e020a0 100644
--- a/tools/dom/templates/html/dartium/html_dartium.darttemplate
+++ b/tools/dom/templates/html/dartium/html_dartium.darttemplate
@@ -10,13 +10,14 @@
 
 import 'dart:async';
 import 'dart:collection';
-import 'dart:_collection-dev';
+import 'dart:_collection-dev' hide Symbol;
 import 'dart:html_common';
 import 'dart:indexed_db';
 import 'dart:isolate';
 import 'dart:json' as json;
 import 'dart:math';
 import 'dart:nativewrappers';
+import 'dart:mdv_observe_impl';
 import 'dart:typed_data';
 import 'dart:web_gl' as gl;
 import 'dart:web_sql';
@@ -32,14 +33,16 @@
 part '$AUXILIARY_DIR/CssClassSet.dart';
 part '$AUXILIARY_DIR/EventListener.dart';
 part '$AUXILIARY_DIR/EventStreamProvider.dart';
+part '$AUXILIARY_DIR/ImmutableListMixin.dart';
 part '$AUXILIARY_DIR/KeyboardEventStream.dart';
 part '$AUXILIARY_DIR/KeyCode.dart';
 part '$AUXILIARY_DIR/KeyLocation.dart';
 part '$AUXILIARY_DIR/KeyName.dart';
-part '$AUXILIARY_DIR/ModelTreeObserver.dart';
+part '$AUXILIARY_DIR/PathObserver.dart';
 part '$AUXILIARY_DIR/Point.dart';
 part '$AUXILIARY_DIR/ReadyState.dart';
 part '$AUXILIARY_DIR/Rectangle.dart';
+part '$AUXILIARY_DIR/TemplateBindings.dart';
 part '$AUXILIARY_DIR/WrappedEvent.dart';
 part '$AUXILIARY_DIR/WrappedList.dart';
 part '$AUXILIARY_DIR/_HttpRequestUtils.dart';
diff --git a/tools/dom/templates/html/impl/impl_DocumentFragment.darttemplate b/tools/dom/templates/html/impl/impl_DocumentFragment.darttemplate
index c2afcde..f591237 100644
--- a/tools/dom/templates/html/impl/impl_DocumentFragment.darttemplate
+++ b/tools/dom/templates/html/impl/impl_DocumentFragment.darttemplate
@@ -55,7 +55,7 @@
     e.innerHtml = value;
 
     // Copy list first since we don't want liveness during iteration.
-    List nodes = new List.from(e.nodes);
+    List nodes = new List.from(e.nodes, growable: false);
     this.nodes.addAll(nodes);
   }
 
diff --git a/tools/dom/templates/html/impl/impl_Element.darttemplate b/tools/dom/templates/html/impl/impl_Element.darttemplate
index 08a6b08..89329db 100644
--- a/tools/dom/templates/html/impl/impl_Element.darttemplate
+++ b/tools/dom/templates/html/impl/impl_Element.darttemplate
@@ -137,7 +137,7 @@
   }
 }
 
-/** 
+/**
  * An immutable list containing HTML elements. This list contains some
  * additional methods for ease of CSS manipulation on a group of elements.
  */
@@ -625,9 +625,267 @@
 $else
 $endif
 
+  @Creates('Null')
+  Map<String, StreamSubscription> _attributeBindings;
+
+  // TODO(jmesserly): I'm concerned about adding these to every element.
+  // Conceptually all of these belong on TemplateElement. They are here to
+  // support browsers that don't have <template> yet.
+  // However even in the polyfill they're restricted to certain tags
+  // (see [isTemplate]). So we can probably convert it to a (public) mixin, and
+  // only mix it in to the elements that need it.
+$if DART2JS
+  @Creates('Null')  // Set from Dart code; does not instantiate a native type.
+$endif
+  var _model;
+
+$if DART2JS
+  @Creates('Null')  // Set from Dart code; does not instantiate a native type.
+$endif
+  _TemplateIterator _templateIterator;
+
+$if DART2JS
+  @Creates('Null')  // Set from Dart code; does not instantiate a native type.
+$endif
+  Element _templateInstanceRef;
+
+  // Note: only used if `this is! TemplateElement`
+$if DART2JS
+  @Creates('Null')  // Set from Dart code; does not instantiate a native type.
+$endif
+  DocumentFragment _templateContent;
+
+  bool _templateIsDecorated;
+
+  // TODO(jmesserly): should path be optional, and default to empty path?
+  // It is used that way in at least one path in JS TemplateElement tests
+  // (see "BindImperative" test in original JS code).
+  @Experimental
+  void bind(String name, model, String path) {
+    _bindElement(this, name, model, path);
+  }
+
+  // TODO(jmesserly): this is static to work around http://dartbug.com/10166
+  // Similar issue for unbind/unbindAll below.
+  static void _bindElement(Element self, String name, model, String path) {
+    if (self._bindTemplate(name, model, path)) return;
+
+    if (self._attributeBindings == null) {
+      self._attributeBindings = new Map<String, StreamSubscription>();
+    }
+
+    self.attributes.remove(name);
+
+    var changed;
+    if (name.endsWith('?')) {
+      name = name.substring(0, name.length - 1);
+
+      changed = (value) {
+        if (_templateBooleanConversion(value)) {
+          self.attributes[name] = '';
+        } else {
+          self.attributes.remove(name);
+        }
+      };
+    } else {
+      changed = (value) {
+        // TODO(jmesserly): escape value if needed to protect against XSS.
+        // See https://github.com/toolkitchen/mdv/issues/58
+        self.attributes[name] = value == null ? '' : '$value';
+      };
+    }
+
+    self.unbind(name);
+
+    self._attributeBindings[name] =
+        new PathObserver(model, path).bindSync(changed);
+  }
+
+  @Experimental
+  void unbind(String name) {
+    _unbindElement(this, name);
+  }
+
+  static _unbindElement(Element self, String name) {
+    if (self._unbindTemplate(name)) return;
+    if (self._attributeBindings != null) {
+      var binding = self._attributeBindings.remove(name);
+      if (binding != null) binding.cancel();
+    }
+  }
+
+  @Experimental
+  void unbindAll() {
+    _unbindAllElement(this);
+  }
+
+  static void _unbindAllElement(Element self) {
+    self._unbindAllTemplate();
+
+    if (self._attributeBindings != null) {
+      for (var binding in self._attributeBindings.values) {
+        binding.cancel();
+      }
+      self._attributeBindings = null;
+    }
+  }
+
+  // TODO(jmesserly): unlike the JS polyfill, we can't mixin
+  // HTMLTemplateElement at runtime into things that are semantically template
+  // elements. So instead we implement it here with a runtime check.
+  // If the bind succeeds, we return true, otherwise we return false and let
+  // the normal Element.bind logic kick in.
+  bool _bindTemplate(String name, model, String path) {
+    if (isTemplate) {
+      switch (name) {
+        case 'bind':
+        case 'repeat':
+        case 'if':
+          _ensureTemplate();
+          if (_templateIterator == null) {
+            _templateIterator = new _TemplateIterator(this);
+          }
+          _templateIterator.inputs.bind(name, model, path);
+          return true;
+      }
+    }
+    return false;
+  }
+
+  bool _unbindTemplate(String name) {
+    if (isTemplate) {
+      switch (name) {
+        case 'bind':
+        case 'repeat':
+        case 'if':
+          _ensureTemplate();
+          if (_templateIterator != null) {
+            _templateIterator.inputs.unbind(name);
+          }
+          return true;
+      }
+    }
+    return false;
+  }
+
+  void _unbindAllTemplate() {
+    if (isTemplate) {
+      unbind('bind');
+      unbind('repeat');
+      unbind('if');
+    }
+  }
+
+  /**
+   * Gets the template this node refers to.
+   * This is only supported if [isTemplate] is true.
+   */
+  @Experimental
+  Element get ref {
+    _ensureTemplate();
+
+    Element ref = null;
+    var refId = attributes['ref'];
+    if (refId != null) {
+      ref = document.getElementById(refId);
+    }
+
+    return ref != null ? ref : _templateInstanceRef;
+  }
+
+  /**
+   * Gets the content of this template.
+   * This is only supported if [isTemplate] is true.
+   */
+  @Experimental
+  DocumentFragment get content {
+    _ensureTemplate();
+    return _templateContent;
+  }
+
+  /**
+   * Creates an instance of the template.
+   * This is only supported if [isTemplate] is true.
+   */
+  @Experimental
+  DocumentFragment createInstance() {
+    _ensureTemplate();
+
+    var template = ref;
+    if (template == null) template = this;
+
+    var instance = _createDeepCloneAndDecorateTemplates(template.content,
+        attributes['syntax']);
+
+    if (TemplateElement._instanceCreated != null) {
+      TemplateElement._instanceCreated.add(instance);
+    }
+    return instance;
+  }
+
+  /**
+   * The data model which is inherited through the tree.
+   * This is only supported if [isTemplate] is true.
+   *
+   * Setting this will destructive propagate the value to all descendant nodes,
+   * and reinstantiate all of the nodes expanded by this template.
+   *
+   * Currently this does not support propagation through Shadow DOMs.
+   */
+  @Experimental
+  get model => _model;
+
+  @Experimental
+  void set model(value) {
+    _ensureTemplate();
+
+    _model = value;
+    _addBindings(this, model);
+  }
+
+  // TODO(jmesserly): const set would be better
+  static const _TABLE_TAGS = const {
+    'caption': null,
+    'col': null,
+    'colgroup': null,
+    'tbody': null,
+    'td': null,
+    'tfoot': null,
+    'th': null,
+    'thead': null,
+    'tr': null,
+  };
+
+  bool get _isAttributeTemplate => attributes.containsKey('template') &&
+      (localName == 'option' || _TABLE_TAGS.containsKey(localName));
+
+  /**
+   * Returns true if this node is a template.
+   *
+   * A node is a template if [tagName] is TEMPLATE, or the node has the
+   * 'template' attribute and this tag supports attribute form for backwards
+   * compatibility with existing HTML parsers. The nodes that can use attribute
+   * form are table elments (THEAD, TBODY, TFOOT, TH, TR, TD, CAPTION, COLGROUP
+   * and COL) and OPTION.
+   */
+  // TODO(jmesserly): this is not a public MDV API, but it seems like a useful
+  // place to document which tags our polyfill considers to be templates.
+  // Otherwise I'd be repeating it in several other places.
+  // See if we can replace this with a TemplateMixin.
+  @Experimental
+  bool get isTemplate => tagName == 'TEMPLATE' || _isAttributeTemplate;
+
+  void _ensureTemplate() {
+    if (!isTemplate) {
+      throw new UnsupportedError('$this is not a template.');
+    }
+    TemplateElement.decorate(this);
+  }
+
 $!MEMBERS
 }
 
+
 final _START_TAG_REGEXP = new RegExp('<(\\w+)');
 class _ElementFactoryProvider {
   static const _CUSTOM_PARENT_TAG_MAP = const {
@@ -645,19 +903,6 @@
     'track' : 'audio',
   };
 
-  // TODO(jmesserly): const set would be better
-  static const _TABLE_TAGS = const {
-    'caption': null,
-    'col': null,
-    'colgroup': null,
-    'tbody': null,
-    'td': null,
-    'tfoot': null,
-    'th': null,
-    'thead': null,
-    'tr': null,
-  };
-
   @DomName('Document.createElement')
   static Element createElement_html(String html) {
     // TODO(jacobr): this method can be made more robust and performant.
@@ -671,7 +916,7 @@
     final match = _START_TAG_REGEXP.firstMatch(html);
     if (match != null) {
       tag = match.group(1).toLowerCase();
-      if (Device.isIE && _TABLE_TAGS.containsKey(tag)) {
+      if (Device.isIE && Element._TABLE_TAGS.containsKey(tag)) {
         return _createTableForIE(html, tag);
       }
       parentTag = _CUSTOM_PARENT_TAG_MAP[tag];
diff --git a/tools/dom/templates/html/impl/impl_HTMLDocument.darttemplate b/tools/dom/templates/html/impl/impl_HTMLDocument.darttemplate
index dd2e6e4..2d6853b 100644
--- a/tools/dom/templates/html/impl/impl_HTMLDocument.darttemplate
+++ b/tools/dom/templates/html/impl/impl_HTMLDocument.darttemplate
@@ -165,4 +165,11 @@
   @SupportedBrowser(SupportedBrowser.SAFARI)
   @Experimental
   String get visibilityState => $dom_webkitVisibilityState;
+
+
+$if DART2JS
+  @Creates('Null')  // Set from Dart code; does not instantiate a native type.
+$endif
+  // Note: used to polyfill <template>
+  Document _templateContentsOwner;
 }
diff --git a/tools/dom/templates/html/impl/impl_HTMLInputElement.darttemplate b/tools/dom/templates/html/impl/impl_HTMLInputElement.darttemplate
index bcac38d..a376140 100644
--- a/tools/dom/templates/html/impl/impl_HTMLInputElement.darttemplate
+++ b/tools/dom/templates/html/impl/impl_HTMLInputElement.darttemplate
@@ -38,13 +38,73 @@
     }
     return e;
   }
+
+$if DART2JS
+  @Creates('Null')  // Set from Dart code; does not instantiate a native type.
+$endif
+  _ValueBinding _valueBinding;
+
+$if DART2JS
+  @Creates('Null')  // Set from Dart code; does not instantiate a native type.
+$endif
+  _CheckedBinding _checkedBinding;
+
+  @Experimental
+  void bind(String name, model, String path) {
+    switch (name) {
+      case 'value':
+        unbind('value');
+        attributes.remove('value');
+        _valueBinding = new _ValueBinding(this, model, path);
+        break;
+      case 'checked':
+        unbind('checked');
+        attributes.remove('checked');
+        _checkedBinding = new _CheckedBinding(this, model, path);
+        break;
+      default:
+        // TODO(jmesserly): this should be "super" (http://dartbug.com/10166).
+        // Similar issue for unbind/unbindAll below.
+        Element._bindElement(this, name, model, path);
+        break;
+    }
+  }
+
+  @Experimental
+  void unbind(String name) {
+    switch (name) {
+      case 'value':
+        if (_valueBinding != null) {
+          _valueBinding.unbind();
+          _valueBinding = null;
+        }
+        break;
+      case 'checked':
+        if (_checkedBinding != null) {
+          _checkedBinding.unbind();
+          _checkedBinding = null;
+        }
+        break;
+      default:
+        Element._unbindElement(this, name);
+        break;
+    }
+  }
+
+  @Experimental
+  void unbindAll() {
+    unbind('value');
+    unbind('checked');
+    Element._unbindAllElement(this);
+  }
+
 $!MEMBERS
 }
 
 
 // Interfaces representing the InputElement APIs which are supported
-// for the various types of InputElement.
-// From http://dev.w3.org/html5/spec/the-input-element.html#the-input-element.
+// for the various types of InputElement. From:
+// http://www.w3.org/html/wg/drafts/html/master/forms.html#the-input-element.
 
 /**
  * Exposes the functionality common between all InputElement types.
@@ -90,7 +150,7 @@
 /**
  * Hidden input which is not intended to be seen or edited by the user.
  */
-abstract class HiddenInputElement implements Element {
+abstract class HiddenInputElement implements InputElementBase {
   factory HiddenInputElement() => new InputElement(type: 'hidden');
 }
 
diff --git a/tools/dom/templates/html/impl/impl_HTMLTemplateElement.darttemplate b/tools/dom/templates/html/impl/impl_HTMLTemplateElement.darttemplate
new file mode 100644
index 0000000..a8459d1
--- /dev/null
+++ b/tools/dom/templates/html/impl/impl_HTMLTemplateElement.darttemplate
@@ -0,0 +1,119 @@
+// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// WARNING: Do not edit - generated code.
+
+part of $LIBRARYNAME;
+
+@Experimental
+$(ANNOTATIONS)class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC {
+$!MEMBERS
+
+  // For real TemplateElement use the actual DOM .content field instead of
+  // our polyfilled expando.
+  @Experimental
+  DocumentFragment get content => $dom_content;
+
+  static StreamController<DocumentFragment> _instanceCreated;
+
+  /**
+   * *Warning*: This is an implementation helper for Model-Driven Views and
+   * should not be used in your code.
+   *
+   * This event is fired whenever a template is instantiated via
+   * [createInstance].
+   */
+  // TODO(rafaelw): This is a hack, and is neccesary for the polyfill
+  // because custom elements are not upgraded during clone()
+  @Experimental
+  static Stream<DocumentFragment> get instanceCreated {
+    if (_instanceCreated == null) {
+      _instanceCreated = new StreamController<DocumentFragment>();
+    }
+    return _instanceCreated.stream;
+  }
+
+  /**
+   * Ensures proper API and content model for template elements.
+   *
+   * [instanceRef] can be used to set the [Element.ref] property of [template],
+   * and use the ref's content will be used as source when createInstance() is
+   * invoked.
+   *
+   * Returns true if this template was just decorated, or false if it was
+   * already decorated.
+   */
+  @Experimental
+  static bool decorate(Element template, [Element instanceRef]) {
+    // == true check because it starts as a null field.
+    if (template._templateIsDecorated == true) return false;
+
+    template._templateIsDecorated = true;
+
+    _injectStylesheet();
+
+    // Create content
+    if (template is! TemplateElement) {
+      var doc = _getTemplateContentsOwner(template.document);
+      template._templateContent = doc.createDocumentFragment();
+    }
+
+    if (instanceRef != null) {
+      template._templateInstanceRef = instanceRef;
+      return true; // content is empty.
+    }
+
+    if (template is TemplateElement) {
+      _bootstrapTemplatesRecursivelyFrom(template.content);
+    } else {
+      _liftNonNativeTemplateChildrenIntoContent(template);
+    }
+
+    return true;
+  }
+
+  /**
+   * This used to decorate recursively all templates from a given node.
+   *
+   * By default [decorate] will be called on templates lazily when certain
+   * properties such as [model] are accessed, but it can be run eagerly to
+   * decorate an entire tree recursively.
+   */
+  // TODO(rafaelw): Review whether this is the right public API.
+  @Experimental
+  static void bootstrap(Node content) {
+    _bootstrapTemplatesRecursivelyFrom(content);
+  }
+
+  static bool _initStyles;
+
+  static void _injectStylesheet() {
+    if (_initStyles == true) return;
+    _initStyles = true;
+
+    var style = new StyleElement();
+    style.text = r'''
+template,
+thead[template],
+tbody[template],
+tfoot[template],
+th[template],
+tr[template],
+td[template],
+caption[template],
+colgroup[template],
+col[template],
+option[template] {
+  display: none;
+}''';
+    document.head.append(style);
+  }
+
+  /**
+   * A mapping of names to Custom Syntax objects. See [CustomBindingSyntax] for
+   * more information.
+   */
+  @Experimental
+  static Map<String, CustomBindingSyntax> syntax = {};
+}
diff --git a/tools/dom/templates/html/impl/impl_Node.darttemplate b/tools/dom/templates/html/impl/impl_Node.darttemplate
index 4e9474b..935794b 100644
--- a/tools/dom/templates/html/impl/impl_Node.darttemplate
+++ b/tools/dom/templates/html/impl/impl_Node.darttemplate
@@ -121,7 +121,7 @@
     // time.
     Node child = _this.$dom_firstChild;
     while (child != null) {
-      Node nextChild = child.nextSibling;
+      Node nextChild = child.nextNode;
       if (test(child) == removeMatching) {
         _this.$dom_removeChild(child);
       }
@@ -245,96 +245,49 @@
     }
   }
 
-  // Note that this may either be the locally set model or a cached value
-  // of the inherited model. This is cached to minimize model change
-  // notifications.
-$if DART2JS
-  @Creates('Null')
-$endif
-  var _model;
-  bool _hasLocalModel;
-  Set<StreamController<Node>> _modelChangedStreams;
-
-  /**
-   * The data model which is inherited through the tree.
-   *
-   * Setting this will propagate the value to all descendant nodes. If the
-   * model is not set on this node then it will be inherited from ancestor
-   * nodes.
-   *
-   * Currently this does not support propagation through Shadow DOMs.
-   *
-   * [clearModel] must be used to remove the model property from this node
-   * and have the model inherit from ancestor nodes.
-   */
-  @Experimental
-  get model {
-    // If we have a change handler then we've cached the model locally.
-    if (_modelChangedStreams != null && !_modelChangedStreams.isEmpty) {
-      return _model;
-    }
-    // Otherwise start looking up the tree.
-    for (var node = this; node != null; node = node.parentNode) {
-      if (node._hasLocalModel == true) {
-        return node._model;
-      }
-    }
-    return null;
-  }
-
-  @Experimental
-  void set model(value) {
-    var changed = model != value;
-    _model = value;
-    _hasLocalModel = true;
-    _ModelTreeObserver.initialize();
-
-    if (changed) {
-      if (_modelChangedStreams != null && !_modelChangedStreams.isEmpty) {
-        _modelChangedStreams.toList().forEach((stream) => stream.add(this));
-      }
-      // Propagate new model to all descendants.
-      _ModelTreeObserver.propagateModel(this, value, false);
-    }
-  }
-
-  /**
-   * Clears the locally set model and makes this model be inherited from parent
-   * nodes.
-   */
-  @Experimental
-  void clearModel() {
-    if (_hasLocalModel == true) {
-      _hasLocalModel = false;
-
-      // Propagate new model to all descendants.
-      if (parentNode != null) {
-        _ModelTreeObserver.propagateModel(this, parentNode.model, false);
-      } else {
-        _ModelTreeObserver.propagateModel(this, null, false);
-      }
-    }
-  }
-
-  /**
-   * Get a stream of models, whenever the model changes.
-   */
-  Stream<Node> get onModelChanged {
-    if (_modelChangedStreams == null) {
-      _modelChangedStreams = new Set<StreamController<Node>>();
-    }
-    var controller;
-    controller = new StreamController(
-        onListen: () { _modelChangedStreams.add(controller); },
-        onCancel: () { _modelChangedStreams.remove(controller); });
-    return controller.stream;
-  }
-
   /**
    * Print out a String representation of this Node.
    */
   String toString() => localName == null ?
       (nodeValue == null ? super.toString() : nodeValue) : localName;
 
+  /**
+   * Binds the attribute [name] to the [path] of the [model].
+   * Path is a String of accessors such as `foo.bar.baz`.
+   */
+  @Experimental
+  void bind(String name, model, String path) {
+    // TODO(jmesserly): should we throw instead?
+    window.console.error('Unhandled binding to Node: '
+        '$this $name $model $path');
+  }
+
+  /** Unbinds the attribute [name]. */
+  @Experimental
+  void unbind(String name) {}
+
+  /** Unbinds all bound attributes. */
+  @Experimental
+  void unbindAll() {}
+
+  TemplateInstance _templateInstance;
+
+  // TODO(arv): Consider storing all "NodeRareData" on a single object?
+  int __instanceTerminatorCount;
+  int get _instanceTerminatorCount {
+    if (__instanceTerminatorCount == null) return 0;
+    return __instanceTerminatorCount;
+  }
+  set _instanceTerminatorCount(int value) {
+    if (value == 0) value = null;
+    __instanceTerminatorCount = value;
+  }
+
+  /** Gets the template instance that instantiated this node, if any. */
+  @Experimental
+  TemplateInstance get templateInstance =>
+      _templateInstance != null ? _templateInstance :
+      (parent != null ? parent.templateInstance : null);
+
 $!MEMBERS
 }
diff --git a/tools/dom/templates/html/impl/impl_Text.darttemplate b/tools/dom/templates/html/impl/impl_Text.darttemplate
index e5bbf5d..05d0be7 100644
--- a/tools/dom/templates/html/impl/impl_Text.darttemplate
+++ b/tools/dom/templates/html/impl/impl_Text.darttemplate
@@ -9,4 +9,42 @@
 $(ANNOTATIONS)class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC {
   factory $CLASSNAME(String data) => _$(CLASSNAME)FactoryProvider.create$CLASSNAME(data);
 $!MEMBERS
+
+$if DART2JS
+  @Creates('Null')  // Set from Dart code; does not instantiate a native type.
+$endif
+  StreamSubscription _textBinding;
+
+  @Experimental
+  void bind(String name, model, String path) {
+    if (name != 'text') {
+      super.bind(name, model, path);
+      return;
+    }
+
+    unbind('text');
+
+    _textBinding = new PathObserver(model, path).bindSync((value) {
+      text = value == null ? '' : '$value';
+    });
+  }
+
+  @Experimental
+  void unbind(String name) {
+    if (name != 'text') {
+      super.unbind(name);
+      return;
+    }
+
+    if (_textBinding == null) return;
+
+    _textBinding.cancel();
+    _textBinding = null;
+  }
+
+  @Experimental
+  void unbindAll() {
+    unbind('text');
+    super.unbindAll();
+  }
 }
diff --git a/tools/dom/templates/html/impl/impl_TouchList.darttemplate b/tools/dom/templates/html/impl/impl_TouchList.darttemplate
index aa375b7..9188e52 100644
--- a/tools/dom/templates/html/impl/impl_TouchList.darttemplate
+++ b/tools/dom/templates/html/impl/impl_TouchList.darttemplate
@@ -6,7 +6,7 @@
 
 part of $LIBRARYNAME;
 
-$(ANNOTATIONS)class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC {
+$(ANNOTATIONS)class $CLASSNAME$EXTENDS$MIXINS$IMPLEMENTS$NATIVESPEC {
   /// NB: This constructor likely does not work as you might expect it to! This
   /// constructor will simply fail (returning null) if you are not on a device
   /// with touch enabled. See dartbug.com/8314.
diff --git a/tools/dom/templates/immutable_list_mixin.darttemplate b/tools/dom/templates/immutable_list_mixin.darttemplate
index a984eab..e9bffe5 100644
--- a/tools/dom/templates/immutable_list_mixin.darttemplate
+++ b/tools/dom/templates/immutable_list_mixin.darttemplate
@@ -1,208 +1,15 @@
   // -- start List<$E> mixins.
   // $E is the element type.
 
-  // From Iterable<$E>:
-
-  Iterator<$E> get iterator {
-    // Note: NodeLists are not fixed size. And most probably length shouldn't
-    // be cached in both iterator _and_ forEach method. For now caching it
-    // for consistency.
-    return new FixedSizeListIterator<$E>(this);
-  }
-
 $if DEFINE_LENGTH_AS_NUM_ITEMS
   // SVG Collections expose numberOfItems rather than length.
   int get length => numberOfItems;
 $endif
-  $E reduce($E combine($E value, $E element)) {
-    return IterableMixinWorkaround.reduce(this, combine);
-  }
 
-  dynamic fold(dynamic initialValue,
-               dynamic combine(dynamic previousValue, $E element)) {
-    return IterableMixinWorkaround.fold(this, initialValue, combine);
-  }
-
-$if DEFINE_CONTAINS
-  bool contains($E element) => IterableMixinWorkaround.contains(this, element);
-$else
-  // contains() defined by IDL.
-$endif
-
-  void forEach(void f($E element)) => IterableMixinWorkaround.forEach(this, f);
-
-  String join([String separator = ""]) =>
-      IterableMixinWorkaround.joinList(this, separator);
-
-  Iterable map(f($E element)) =>
-      IterableMixinWorkaround.mapList(this, f);
-
-  Iterable<$E> where(bool f($E element)) =>
-      IterableMixinWorkaround.where(this, f);
-
-  Iterable expand(Iterable f($E element)) =>
-      IterableMixinWorkaround.expand(this, f);
-
-  bool every(bool f($E element)) => IterableMixinWorkaround.every(this, f);
-
-  bool any(bool f($E element)) => IterableMixinWorkaround.any(this, f);
-
-  List<$E> toList({ bool growable: true }) =>
-      new List<$E>.from(this, growable: growable);
-
-  Set<$E> toSet() => new Set<$E>.from(this);
-
-  bool get isEmpty => this.length == 0;
-
-  Iterable<$E> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
-  Iterable<$E> takeWhile(bool test($E value)) {
-    return IterableMixinWorkaround.takeWhile(this, test);
-  }
-
-  Iterable<$E> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
-  Iterable<$E> skipWhile(bool test($E value)) {
-    return IterableMixinWorkaround.skipWhile(this, test);
-  }
-
-  $E firstWhere(bool test($E value), { $E orElse() }) {
-    return IterableMixinWorkaround.firstWhere(this, test, orElse);
-  }
-
-  $E lastWhere(bool test($E value), {$E orElse()}) {
-    return IterableMixinWorkaround.lastWhereList(this, test, orElse);
-  }
-
-  $E singleWhere(bool test($E value)) {
-    return IterableMixinWorkaround.singleWhere(this, test);
-  }
-
-  $E elementAt(int index) {
-    return this[index];
-  }
-
-  // From Collection<$E>:
-
-  void add($E value) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void addAll(Iterable<$E> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  // From List<$E>:
 $if DEFINE_LENGTH_SETTER
   void set length(int value) {
     throw new UnsupportedError("Cannot resize immutable List.");
   }
 $endif
 
-$if DEFINE_CLEAR
-  void clear() {
-    throw new UnsupportedError("Cannot clear immutable List.");
-  }
-$else
-  // clear() defined by IDL.
-$endif
-
-  Iterable<$E> get reversed {
-    return IterableMixinWorkaround.reversedList(this);
-  }
-
-  void sort([int compare($E a, $E b)]) {
-    throw new UnsupportedError("Cannot sort immutable List.");
-  }
-
-  int indexOf($E element, [int start = 0]) =>
-      Lists.indexOf(this, element, start, this.length);
-
-  int lastIndexOf($E element, [int start]) {
-    if (start == null) start = length - 1;
-    return Lists.lastIndexOf(this, element, start);
-  }
-
-  $E get first {
-    if (this.length > 0) return this[0];
-    throw new StateError("No elements");
-  }
-
-  $E get last {
-    if (this.length > 0) return this[this.length - 1];
-    throw new StateError("No elements");
-  }
-
-  $E get single {
-    if (length == 1) return this[0];
-    if (length == 0) throw new StateError("No elements");
-    throw new StateError("More than one element");
-  }
-
-  void insert(int index, $E element) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void insertAll(int index, Iterable<$E> iterable) {
-    throw new UnsupportedError("Cannot add to immutable List.");
-  }
-
-  void setAll(int index, Iterable<$E> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  $E removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  $E removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  bool remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeWhere(bool test($E element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainWhere(bool test($E element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int end, Iterable<$E> iterable, [int skipCount=0]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int end) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void replaceRange(int start, int end, Iterable<$E> iterable) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  void fillRange(int start, int end, [$E fillValue]) {
-    throw new UnsupportedError("Cannot modify an immutable List.");
-  }
-
-  Iterable<$E> getRange(int start, int end) =>
-    IterableMixinWorkaround.getRangeList(this, start, end);
-
-  List<$E> sublist(int start, [int end]) {
-    if (end == null) end = length;
-    return Lists.getRange(this, start, end, <$E>[]);
-  }
-
-  Map<int, $E> asMap() =>
-    IterableMixinWorkaround.asMapList(this);
-
-  String toString() {
-    StringBuffer buffer = new StringBuffer('[');
-    buffer.writeAll(this, ', ');
-    buffer.write(']');
-    return buffer.toString();
-  }
-
   // -- end List<$E> mixins.
diff --git a/utils/apidoc/apidoc.dart b/utils/apidoc/apidoc.dart
index 8979ddd..753f8b2 100644
--- a/utils/apidoc/apidoc.dart
+++ b/utils/apidoc/apidoc.dart
@@ -39,7 +39,21 @@
   bool generateAppCache = false;
 
   List<String> excludedLibraries = <String>[];
+
+  // For libraries that have names matching the package name,
+  // such as library unittest in package unittest, we just give
+  // the package name with a --include-lib argument, such as:
+  // --include-lib=unittest. These arguments are collected in
+  // includedLibraries.
   List<String> includedLibraries = <String>[];
+
+  // For libraries that lie within packages but have a different name,
+  // such as the matcher library in package unittest, we can use 
+  // --extra-lib with a full relative path under pkg, such as
+  // --extra-lib=unittest/lib/matcher.dart. These arguments are
+  // collected in extraLibraries.
+  List<String> extraLibraries = <String>[];
+
   String packageRoot;
   String version;
 
@@ -65,6 +79,8 @@
           excludedLibraries.add(arg.substring('--exclude-lib='.length));
         } else if (arg.startsWith('--include-lib=')) {
           includedLibraries.add(arg.substring('--include-lib='.length));
+        } else if (arg.startsWith('--extra-lib=')) {
+          extraLibraries.add(arg.substring('--extra-lib='.length));
         } else if (arg.startsWith('--out=')) {
           outputDir = new Path(arg.substring('--out='.length));
         } else if (arg.startsWith('--package-root=')) {
@@ -140,6 +156,19 @@
       }
     }
   }, onDone: () {
+    // Add any --extra libraries that had full pkg paths.
+    // TODO(gram): if the handling of --include-lib libraries in the
+    // listen() block above is cleaned up, then this will need to be
+    // too, as it is a special case of the above.
+    for (var lib in extraLibraries) {
+      var libPath = new Path('../../$lib');
+      if (new File.fromPath(libPath).existsSync()) {
+        apidocLibraries.add(_pathToFileUri(libPath.toNativePath()));
+        var libName = libPath.filename.replaceAll('.dart', '');
+        includedLibraries.add(libName);
+      }
+    }
+
     final apidoc = new Apidoc(mdn, outputDir, mode, generateAppCache,
                               excludedLibraries, version);
     apidoc.dartdocPath =
diff --git a/utils/apidoc/apidoc.gyp b/utils/apidoc/apidoc.gyp
index f75e4c7..d393354 100644
--- a/utils/apidoc/apidoc.gyp
+++ b/utils/apidoc/apidoc.gyp
@@ -96,7 +96,7 @@
             '--exclude-lib=webdriver',
             '--exclude-lib=yaml',
             '--include-lib=matcher',
-            '--include-lib=mock',
+            '--extra-lib=pkg/unittest/lib/mock.dart',
           ],
           'message': 'Running apidoc: <(_action)',
         },