Version 0.3.6.0 .

svn merge -r 18264:18444 https://dart.googlecode.com/svn/branches/bleeding_edge trunk

git-svn-id: http://dart.googlecode.com/svn/trunk@18536 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/compiler/java/com/google/dart/compiler/resolver/ResolutionContext.java b/compiler/java/com/google/dart/compiler/resolver/ResolutionContext.java
index 7889732..c1522c3 100644
--- a/compiler/java/com/google/dart/compiler/resolver/ResolutionContext.java
+++ b/compiler/java/com/google/dart/compiler/resolver/ResolutionContext.java
@@ -361,10 +361,13 @@
   }
 
   public void onError(SourceInfo sourceInfo, ErrorCode errorCode, Object... arguments) {
-    if (suppressSdkWarnings && errorCode.getErrorSeverity() == ErrorSeverity.WARNING) {
-      Source source = sourceInfo.getSource();
-      if (source != null && PackageLibraryManager.isDartUri(source.getUri())) {
-        return;
+    if (suppressSdkWarnings) {
+      ErrorSeverity errorSeverity = errorCode.getErrorSeverity();
+      if (errorSeverity == ErrorSeverity.WARNING || errorSeverity == ErrorSeverity.INFO) {
+        Source source = sourceInfo.getSource();
+        if (source != null && PackageLibraryManager.isDartUri(source.getUri())) {
+          return;
+        }
       }
     }
     context.onError(new DartCompilationError(sourceInfo, errorCode, arguments));
diff --git a/compiler/java/com/google/dart/compiler/resolver/TopLevelElementBuilder.java b/compiler/java/com/google/dart/compiler/resolver/TopLevelElementBuilder.java
index eb49f52..e97667b 100644
--- a/compiler/java/com/google/dart/compiler/resolver/TopLevelElementBuilder.java
+++ b/compiler/java/com/google/dart/compiler/resolver/TopLevelElementBuilder.java
@@ -148,7 +148,9 @@
         String name = element.getName();
         if (libraryImport.isVisible(name)) {
           Element oldElement = scopeForImport.declareElement(name, element);
-          if (oldElement != null) {
+          // TODO(8474): Remove the "Expect" special casing.
+          if (oldElement != null
+              && !name.equals("Expect") && !name.equals("ExpectException")) {
             scopeForImport.declareElement(name,
                 Elements.createDuplicateElement(oldElement, element));
           }
diff --git a/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java b/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java
index a2d1272..15ee2bb 100644
--- a/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java
+++ b/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java
@@ -288,10 +288,13 @@
     }
 
     private void onError(SourceInfo errorTarget, ErrorCode errorCode, Object... arguments) {
-      if (suppressSdkWarnings && errorCode.getErrorSeverity() == ErrorSeverity.WARNING) {
-        Source source = errorTarget.getSource();
-        if (source != null && PackageLibraryManager.isDartUri(source.getUri())) {
-          return;
+      if (suppressSdkWarnings) {
+        ErrorSeverity errorSeverity = errorCode.getErrorSeverity();
+        if (errorSeverity == ErrorSeverity.WARNING || errorSeverity == ErrorSeverity.INFO) {
+          Source source = errorTarget.getSource();
+          if (source != null && PackageLibraryManager.isDartUri(source.getUri())) {
+            return;
+          }
         }
       }
       context.onError(new DartCompilationError(errorTarget, errorCode, arguments));
@@ -3449,13 +3452,43 @@
                         superMember);
             }
           }
-        } else if (!types.isAssignable(superMember, member.getType())) {
-          typeError(errorTarget,
-                    TypeErrorCode.CANNOT_OVERRIDE_TYPED_MEMBER,
-                    name,
-                    superElement.getEnclosingElement().getName(),
-                    member.getType(),
-                    superMember);
+        } else {
+          if (ElementKind.of(member) == ElementKind.FIELD
+              && ElementKind.of(superElement) == ElementKind.FIELD) {
+            FieldElement field = (FieldElement) member;
+            FieldElement superField = (FieldElement) superElement;
+            //
+            MethodElement fGetter = field.getGetter();
+            MethodElement sGetter = superField.getGetter();
+            if (fGetter != null && sGetter != null) {
+              checkOverride(errorTarget, fGetter, sGetter);
+            } else if (fGetter != null && sGetter == null || fGetter == null && sGetter != null) {
+            } else {
+              if (!types.isAssignable(superMember, member.getType())) {
+                typeError(errorTarget, TypeErrorCode.CANNOT_OVERRIDE_TYPED_MEMBER, name,
+                    superElement.getEnclosingElement().getName(), member.getType(), superMember);
+                return;
+              }
+            }
+            //
+            MethodElement fSetter = field.getSetter();
+            MethodElement sSetter = superField.getSetter();
+            if (fSetter != null && sSetter != null) {
+              checkOverride(errorTarget, fSetter, sSetter);
+            } else if (fSetter != null && sSetter == null || fSetter == null && sSetter != null) {
+            } else {
+              if (!types.isAssignable(superMember, member.getType())) {
+                typeError(errorTarget, TypeErrorCode.CANNOT_OVERRIDE_TYPED_MEMBER, name,
+                    superElement.getEnclosingElement().getName(), member.getType(), superMember);
+                return;
+              }
+            }
+            return;
+          }
+          if (!types.isAssignable(superMember, member.getType())) {
+            typeError(errorTarget, TypeErrorCode.CANNOT_OVERRIDE_TYPED_MEMBER, name,
+                superElement.getEnclosingElement().getName(), member.getType(), superMember);
+          }
         }
       }
 
diff --git a/pkg/args/pubspec.yaml b/pkg/args/pubspec.yaml
index 232ce20..b4b1e45 100644
--- a/pkg/args/pubspec.yaml
+++ b/pkg/args/pubspec.yaml
@@ -1,6 +1,7 @@
 name: args
 author: "Dart Team <misc@dartlang.org>"
 homepage: http://www.dartlang.org
+documentation: http://api.dartlang.org/docs/pkg/args.html
 description: >
  Libraries for defining parsers for parsing raw command-line arguments into
  a set of options and values using GNU and POSIX style options.
diff --git a/pkg/intl/pubspec.yaml b/pkg/intl/pubspec.yaml
index 5b6d3b9..4127fa0 100644
--- a/pkg/intl/pubspec.yaml
+++ b/pkg/intl/pubspec.yaml
@@ -1,6 +1,7 @@
 name: intl
 author: "Dart Team <misc@dartlang.org>"
 homepage: http://www.dartlang.org
+documentation: http://api.dartlang.org/docs/pkg/intl.html
 description: >
   Contains code to deal with internationalized/localized
   messages, date and number formatting and parsing,
diff --git a/pkg/logging/pubspec.yaml b/pkg/logging/pubspec.yaml
index 3380898..5c2c045 100644
--- a/pkg/logging/pubspec.yaml
+++ b/pkg/logging/pubspec.yaml
@@ -1,6 +1,7 @@
 name: logging
 author: "Dart Team <misc@dartlang.org>"
 homepage: http://www.dartlang.org
+documentation: http://api.dartlang.org/docs/pkg/logging.html
 description: >
  Provides APIs for debugging and error logging. This library introduces
  abstractions similar to those used in other languages, such as the Closure JS
diff --git a/pkg/meta/pubspec.yaml b/pkg/meta/pubspec.yaml
index ffa1a02c..894c695 100644
--- a/pkg/meta/pubspec.yaml
+++ b/pkg/meta/pubspec.yaml
@@ -1,6 +1,7 @@
 name: meta
 author: "Dart Team <misc@dartlang.org>"
 homepage: http://www.dartlang.org
+documentation: http://api.dartlang.org/docs/pkg/meta.html
 description: >
  This library contains the definitions of annotations that provide additional
  semantic information about the program being annotated. These annotations are
diff --git a/pkg/pkg.status b/pkg/pkg.status
index 9f94f23..a241322 100644
--- a/pkg/pkg.status
+++ b/pkg/pkg.status
@@ -9,6 +9,9 @@
 # arithmetic natively, i.e., the VM.
 fixnum/test/int_64_vm_test: Skip
 
+# Skip non-test files ending with "_test".
+scheduled_test/lib/*: Skip
+
 [$compiler == dart2dart]
 *: Skip
 
@@ -18,6 +21,10 @@
 oauth2/test/*: Skip
 path/test/*: Skip
 
+# Issue 8440 forces us to use path in the scheduled_test tests, which would
+# otherwise be dart2js-compatible.
+scheduled_test/test/*: Skip
+
 # Skip tests that use local file access if we're running in any browser
 [ $runtime == opera || $runtime == ff || $runtime == ie9 || $runtime == dartium || $runtime == chrome || $runtime == safari || $runtime == drt  || $runtime == jsshell]
 http/test/*: Skip
@@ -30,6 +37,10 @@
 analyzer-experimental/test/generated/parser_test: Skip # Imports dart:io.
 analyzer-experimental/test/generated/scanner_test: Skip # Imports dart:io.
 
+# Issue 8440 forces us to use path in the scheduled_test tests, which would
+# otherwise be dart2js-compatible.
+scheduled_test/test/*: Skip
+
 [ $runtime == opera && $compiler == dart2js ]
 intl/test/find_default_locale_browser_test: Fail
 intl/test/date_time_format_http_request_test: Skip # Timeout.
@@ -38,6 +49,7 @@
 [ $runtime == vm ]
 intl/test/find_default_locale_browser_test: Skip
 intl/test/date_time_format_http_request_test: Skip
+serialization/test/serialization_test: Pass, Fail # Temporary workaround for flakiness
 
 [ $runtime == vm && $system == windows ]
 intl/test/find_default_locale_standalone_test: Fail # Issue 8110
diff --git a/pkg/scheduled_test/lib/scheduled_test.dart b/pkg/scheduled_test/lib/scheduled_test.dart
new file mode 100644
index 0000000..05fb9f0
--- /dev/null
+++ b/pkg/scheduled_test/lib/scheduled_test.dart
@@ -0,0 +1,304 @@
+// 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.
+
+// TODO(nweiz): Add support for calling [schedule] while the schedule is already
+// running.
+// TODO(nweiz): Port the non-Pub-specific scheduled test libraries from Pub.
+/// A package for writing readable tests of asynchronous behavior.
+///
+/// This package works by building up a queue of asynchronous tasks called a
+/// "schedule", then executing those tasks in order. This allows the tests to
+/// read like synchronous, linear code, despite executing asynchronously.
+///
+/// The `scheduled_test` package is built on top of `unittest`, and should be
+/// imported instead of `unittest`. It provides its own version of [group],
+/// [test], and [setUp], and re-exports most other APIs from unittest.
+///
+/// To schedule a task, call the [schedule] function. For example:
+///
+///     import 'package:scheduled_test/scheduled_test.dart';
+///
+///     void main() {
+///       test('writing to a file and reading it back should work', () {
+///         schedule(() {
+///           // The schedule won't proceed until the returned Future has
+///           // completed.
+///           return new File("output.txt").writeAsString("contents");
+///         });
+///
+///         schedule(() {
+///           return new File("output.txt").readAsString().then((contents) {
+///             // The normal unittest matchers can still be used.
+///             expect(contents, equals("contents"));
+///           });
+///         });
+///       });
+///     }
+///
+/// ## Setting Up and Tearing Down
+///
+/// The `scheduled_test` package defines its own [setUp] method that works just
+/// like the one in `unittest`. Tasks can be scheduled in [setUp]; they'll be
+/// run before the tasks scheduled by tests in that group. [currentSchedule] is
+/// also set in the [setUp] callback.
+///
+/// This package doesn't have an explicit `tearDown` method. Instead, the
+/// [currentSchedule.onComplete] and [currentSchedule.onException] task queues
+/// can have tasks scheduled during [setUp]. For example:
+///
+///     import 'package:scheduled_test/scheduled_test.dart';
+///
+///     void main() {
+///       var tempDir;
+///       setUp(() {
+///         schedule(() {
+///           return createTempDir().then((dir) {
+///             tempDir = dir;
+///           });
+///         });
+///
+///         currentSchedule.onComplete.schedule(() => deleteDir(tempDir));
+///       });
+///
+///       // ...
+///     }
+///
+/// ## Passing Values Between Tasks
+///
+/// It's often useful to use values computed in one task in other tasks that are
+/// scheduled afterwards. There are two ways to do this. The most
+/// straightforward is just to define a local variable and assign to it. For
+/// example:
+///
+///     import 'package:scheduled_test/scheduled_test.dart';
+///
+///     void main() {
+///       test('computeValue returns 12', () {
+///         var value;
+///
+///         schedule(() {
+///           return computeValue().then((computedValue) {
+///             value = computedValue;
+///           });
+///         });
+///
+///         schedule(() => expect(value, equals(12)));
+///       });
+///     }
+///
+/// However, this doesn't scale well, especially when you start factoring out
+/// calls to [schedule] into library methods. For that reason, [schedule]
+/// returns a [Future] that will complete to the same value as the return
+/// value of the task. For example:
+///
+///     import 'package:scheduled_test/scheduled_test.dart';
+///
+///     void main() {
+///       test('computeValue returns 12', () {
+///         var valueFuture = schedule(() => computeValue());
+///         schedule(() {
+///           valueFuture.then((value) => expect(value, equals(12)));
+///         });
+///       });
+///     }
+///
+/// ## Out-of-Band Callbacks
+///
+/// Sometimes your tests will have callbacks that don't fit into the schedule.
+/// It's important that errors in these callbacks are still registered, though,
+/// and that [Schedule.onException] and [Schedule.onComplete] still run after
+/// they finish. When using `unittest`, you wrap these callbacks with
+/// `expectAsyncN`; when using `scheduled_test`, you use [wrapAsync].
+///
+/// [wrapAsync] has two important functions. First, any errors that occur in it
+/// will be passed into the [Schedule] instead of causing the whole test to
+/// crash. They can then be handled by [Schedule.onException] and
+/// [Schedule.onComplete]. Second, a task queue isn't considered finished until
+/// all of its [wrapAsync]-wrapped functions have been called. This ensures that
+/// [Schedule.onException] and [Schedule.onComplete] will always run after all
+/// the test code in the main queue.
+///
+/// Note that the [completes], [completion], and [throws] matchers use
+/// [wrapAsync] internally, so they're safe to use in conjunction with scheduled
+/// tests.
+///
+/// Here's an example of a test using [wrapAsync] to catch errors thrown in the
+/// callback of a fictional `startServer` function:
+///
+///     import 'package:scheduled_test/scheduled_test.dart';
+///
+///     void main() {
+///       test('sendRequest sends a request', () {
+///         startServer(wrapAsync((request) {
+///           expect(request.body, equals('payload'));
+///           request.response.close();
+///         }));
+///
+///         schedule(() => sendRequest('payload'));
+///       });
+///     }
+///
+/// ## Timeouts
+///
+/// `scheduled_test` has a built-in timeout of 30 seconds (configurable via
+/// [Schedule.timeout]). This timeout is aware of the structure of the schedule;
+/// this means that it will reset for each task in a queue, when moving between
+/// queues, or almost any other sort of interaction with [currentSchedule]. As
+/// long as the [Schedule] knows your test is making some sort of progress, it
+/// won't time out.
+///
+/// If a single task might take a long time, you can also manually tell the
+/// [Schedule] that it's making progress by calling [Schedule.heartbeat], which
+/// will reset the timeout whenever it's called.
+library scheduled_test;
+
+import 'dart:async';
+
+import 'package:unittest/unittest.dart' as unittest;
+
+import 'src/schedule.dart';
+import 'src/schedule_error.dart';
+import 'src/utils.dart';
+
+export 'package:unittest/matcher.dart';
+export 'package:unittest/unittest.dart' show
+    config, configure, Configuration, logMessage, expectThrow, fail;
+
+export 'src/schedule.dart';
+export 'src/schedule_error.dart';
+export 'src/task.dart';
+
+/// The [Schedule] for the current test. This is used to add new tasks and
+/// inspect the state of the schedule.
+///
+/// This is `null` when there's no test currently running.
+Schedule get currentSchedule => _currentSchedule;
+Schedule _currentSchedule;
+
+/// The user-provided setUp function. This is set for each test during
+/// `unittest.setUp`.
+Function _setUpFn;
+
+/// Creates a new test case with the given description and body. This has the
+/// same semantics as [unittest.test].
+void test(String description, void body()) =>
+  _test(description, body, unittest.test);
+
+/// Creates a new test case with the given description and body that will be the
+/// only test run in this file. This has the same semantics as
+/// [unittest.solo_test].
+void solo_test(String description, void body()) =>
+  _test(description, body, unittest.solo_test);
+
+void _test(String description, void body(), Function testFn) {
+  _ensureInitialized();
+  _ensureSetUpForTopLevel();
+  testFn(description, () {
+    var asyncDone = unittest.expectAsync0(() {});
+    return currentSchedule.run(() {
+      if (_setUpFn != null) _setUpFn();
+      body();
+    }).then((_) {
+      // If we got here, the test completed successfully so tell unittest so.
+      asyncDone();
+    }).catchError((e) {
+      if (e is ScheduleError) {
+        assert(e.schedule.errors.contains(e));
+        assert(e.schedule == currentSchedule);
+        unittest.registerException(e.schedule.errorString());
+      } else if (e is AsyncError) {
+        unittest.registerException(e.error, e.stackTrace);
+      } else {
+        unittest.registerException(e);
+      }
+    });
+  });
+}
+
+/// Whether or not the tests currently being defined are in a group. This is
+/// only true when defining tests, not when executing them.
+bool _inGroup = false;
+
+/// Creates a new named group of tests. This has the same semantics as
+/// [unittest.group].
+void group(String description, void body()) {
+  unittest.group(description, () {
+    var wasInGroup = _inGroup;
+    _inGroup = true;
+    _setUpScheduledTest();
+    body();
+    _inGroup = wasInGroup;
+  });
+}
+
+/// Schedules a task, [fn], to run asynchronously as part of the main task queue
+/// of [currentSchedule]. Tasks will be run in the order they're scheduled. If
+/// [fn] returns a [Future], tasks after it won't be run until that [Future]
+/// completes.
+///
+/// The return value will be completed once the scheduled task has finished
+/// running. Its return value is the same as the return value of [fn], or the
+/// value it completes to if it's a [Future].
+///
+/// If [description] is passed, it's used to describe the task for debugging
+/// purposes when an error occurs.
+///
+/// This function is identical to [currentSchedule.tasks.schedule].
+Future schedule(fn(), [String description]) =>
+  currentSchedule.tasks.schedule(fn, description);
+
+/// Register a [setUp] function for a test [group]. This has the same semantics
+/// as [unittest.setUp]. Tasks may be scheduled using [schedule] within
+/// [setUpFn], and [currentSchedule] may be accessed as well.
+///
+/// Note that there is no associated [tearDown] function. Instead, tasks should
+/// be scheduled for [currentSchedule.onComplete] or
+/// [currentSchedule.onException]. These tasks will be run after each test's
+/// schedule is completed.
+void setUp(void setUpFn()) {
+  _setUpScheduledTest(setUpFn);
+}
+
+/// Whether [unittest.setUp] has been called in the top level scope.
+bool _setUpForTopLevel = false;
+
+/// If we're in the top-level scope (that is, not in any [group]s) and
+/// [unittest.setUp] hasn't been called yet, call it.
+void _ensureSetUpForTopLevel() {
+  if (_inGroup || _setUpForTopLevel) return;
+  _setUpScheduledTest();
+}
+
+/// Registers callbacks for [unittest.setUp] and [unittest.tearDown] that set up
+/// and tear down the scheduled test infrastructure.
+void _setUpScheduledTest([void setUpFn()]) {
+  if (!_inGroup) _setUpForTopLevel = true;
+
+  unittest.setUp(() {
+    if (currentSchedule != null) {
+      throw new StateError('There seems to be another scheduled test '
+          'still running.');
+    }
+    _currentSchedule = new Schedule();
+    _setUpFn = setUpFn;
+  });
+
+  unittest.tearDown(() {
+    _currentSchedule = null;
+  });
+}
+
+/// Ensures that the global configuration for `scheduled_test` has been
+/// initialized.
+void _ensureInitialized() {
+  unittest.ensureInitialized();
+  unittest.wrapAsync = (f) {
+    if (currentSchedule == null) {
+      throw new StateError("Unexpected call to wrapAsync with no current "
+          "schedule.");
+    }
+
+    return currentSchedule.wrapAsync(f);
+  };
+}
diff --git a/pkg/scheduled_test/lib/src/mock_clock.dart b/pkg/scheduled_test/lib/src/mock_clock.dart
new file mode 100644
index 0000000..efcc7eb
--- /dev/null
+++ b/pkg/scheduled_test/lib/src/mock_clock.dart
@@ -0,0 +1,105 @@
+// 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.
+
+/// A library that wraps [Timer] in a way that can be mocked out in test code.
+/// Application code only needs to use [newTimer] to get an instance of [Timer].
+/// Then test code can call [mock] to mock out all new [Timer] instances so that
+/// they're controllable by a returned [Clock] object.
+library mock_clock;
+
+import 'dart:async';
+
+import 'utils.dart';
+
+/// The mock clock object. New [Timer]s will be mocked if and only if this is
+/// non-`null`.
+Clock _clock;
+
+/// Whether or not mocking is active.
+bool get _mocked => _clock != null;
+
+/// Causes all future calls to [newTimer] to return mock [Timer] objects that
+/// are controlled by the returned [Clock]. This may only be called once.
+Clock mock() {
+  if (_mocked) {
+    throw new StateError("mock_clock.mock() has already been called.");
+  }
+
+  _clock = new Clock._();
+  return _clock;
+}
+
+typedef void TimerCallback(Timer timer);
+
+/// Returns a new (possibly mocked) [Timer]. Works the same as [new Timer].
+Timer newTimer(Duration duration, TimerCallback callback) =>
+  _mocked ? new _MockTimer(duration, callback) : new Timer(duration, callback);
+
+/// A clock that controls when mocked [Timer]s move forward in time. It starts
+/// at time 0 and advances forward millisecond-by-millisecond, broadcasting each
+/// tick on the [onTick] stream.
+class Clock {
+  /// The current time of the clock, in milliseconds. Starts at 0.
+  int get time => _time;
+  int _time = 0;
+
+  /// The stream of millisecond ticks of the clock.
+  Stream<int> get onTick => _onTickController.stream;
+  final _onTickController = new StreamController<int>.broadcast();
+
+  Clock._();
+
+  /// Advances the clock forward by [milliseconds]. This works like synchronous
+  /// code that takes [milliseconds] to execute; any [Timer]s that are scheduled
+  /// to fire during the interval will do so asynchronously once control returns
+  /// to the event loop.
+  void tick([int milliseconds=1]) {
+    for (var i = 0; i < milliseconds; i++) {
+      var tickTime = ++_time;
+      new Future.immediate(null).then((_) => _onTickController.add(tickTime));
+    }
+  }
+
+  /// Automatically progresses forward in time as long as there are still
+  /// subscribers to [onTick] (that is, [Timer]s waiting to fire). After each
+  /// tick, this pumps the event loop repeatedly so that all non-clock-dependent
+  /// code runs before the next tick.
+  void run() {
+    pumpEventQueue().then((_) {
+      if (!_onTickController.hasSubscribers) return;
+      tick();
+      return run();
+    });
+  }
+}
+
+/// A mock implementation of [Timer] that uses [Clock] to keep time, rather than
+/// the system clock.
+class _MockTimer implements Timer {
+  /// The time at which the timer should fire.
+  final int _time;
+
+  /// The callback to run when the timer fires.
+  final TimerCallback _callback;
+
+  /// The subscription to the [Clock.onTick] stream.
+  StreamSubscription _subscription;
+
+  // TODO(nweiz): Remove this when issue 8512 is fixed.
+  var _cancelled = false;
+
+  _MockTimer(Duration duration, this._callback)
+      : _time = _clock.time + duration.inMilliseconds {
+    _subscription = _clock.onTick.listen((time) {
+      if (_cancelled || time < _time) return;
+      _subscription.cancel();
+      _callback(this);
+    });
+  }
+
+  void cancel() {
+    _cancelled = true;
+    _subscription.cancel();
+  }
+}
diff --git a/pkg/scheduled_test/lib/src/schedule.dart b/pkg/scheduled_test/lib/src/schedule.dart
new file mode 100644
index 0000000..58a1487
--- /dev/null
+++ b/pkg/scheduled_test/lib/src/schedule.dart
@@ -0,0 +1,420 @@
+// 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 schedule;
+
+import 'dart:async';
+import 'dart:collection';
+
+import 'package:unittest/unittest.dart' as unittest;
+
+import 'mock_clock.dart' as mock_clock;
+import 'schedule_error.dart';
+import 'substitute_future.dart';
+import 'task.dart';
+
+/// The schedule of tasks to run for a single test. This has three separate task
+/// queues: [tasks], [onComplete], and [onException]. It also provides
+/// visibility into the current state of the schedule.
+class Schedule {
+  /// The main task queue for the schedule. These tasks are run before the other
+  /// queues and generally constitute the main test body.
+  TaskQueue get tasks => _tasks;
+  TaskQueue _tasks;
+
+  /// The queue of tasks to run if an error is caught while running [tasks]. The
+  /// error will be available in [errors]. These tasks won't be run if no error
+  /// occurs. Note that expectation failures count as errors.
+  ///
+  /// This queue runs before [onComplete], and errors in [onComplete] will not
+  /// cause this queue to be run.
+  ///
+  /// If an error occurs in a task in this queue, all further tasks will be
+  /// skipped.
+  TaskQueue get onException => _onException;
+  TaskQueue _onException;
+
+  /// The queue of tasks to run after [tasks] and possibly [onException] have
+  /// run. This queue will run whether or not an error occurred. If one did, it
+  /// will be available in [errors]. Note that expectation failures count as
+  /// errors.
+  ///
+  /// This queue runs after [onException]. If an error occurs while running
+  /// [onException], that error will be available in [errors] after the original
+  /// error.
+  ///
+  /// If an error occurs in a task in this queue, all further tasks will be
+  /// skipped.
+  TaskQueue get onComplete => _onComplete;
+  TaskQueue _onComplete;
+
+  /// Returns the [Task] that's currently executing, or `null` if there is no
+  /// such task. This will be `null` both before the schedule starts running and
+  /// after it's finished.
+  Task get currentTask => _currentTask;
+  Task _currentTask;
+
+  /// The current state of the schedule.
+  ScheduleState get state => _state;
+  ScheduleState _state = ScheduleState.SET_UP;
+
+  // TODO(nweiz): make this a read-only view once issue 8321 is fixed.
+
+  /// Errors thrown by the task queues.
+  ///
+  /// When running tasks in [tasks], this will always be empty. If an error
+  /// occurs in [tasks], it will be added to this list and then [onException]
+  /// will be run. If an error occurs there as well, it will be added to this
+  /// list and [onComplete] will be run. Errors thrown during [onComplete] will
+  /// also be added to this list, although no scheduled tasks will be run
+  /// afterwards.
+  ///
+  /// Any out-of-band callbacks that throw errors will also have those errors
+  /// added to this list.
+  final errors = <ScheduleError>[];
+
+  /// The task queue that's currently being run. One of [tasks], [onException],
+  /// or [onComplete]. This starts as [tasks], and can only be `null` after the
+  /// schedule is done.
+  TaskQueue get currentQueue =>
+    _state == ScheduleState.DONE ? null : _currentQueue;
+  TaskQueue _currentQueue;
+
+  /// The time to wait before terminating a task queue for inactivity. Defaults
+  /// to 30 seconds. This can be set to `null` to disable timeouts entirely.
+  ///
+  /// If a task queue times out, an error will be raised that can be handled as
+  /// usual in the [onException] and [onComplete] queues. If [onException] times
+  /// out, that can only be handled in [onComplete]; if [onComplete] times out,
+  /// that cannot be handled.
+  ///
+  /// If a task times out and then later completes with an error, that error
+  /// cannot be handled. The user will still be notified of it.
+  Duration get timeout => _timeout;
+  Duration _timeout = new Duration(seconds: 30);
+  set timeout(Duration duration) {
+    _timeout = duration;
+    heartbeat();
+  }
+
+  /// The number of out-of-band callbacks that have been registered with
+  /// [wrapAsync] but have yet to be called.
+  int _pendingCallbacks = 0;
+
+  /// A completer that will be completed once [_pendingCallbacks] reaches zero.
+  /// This will only be non-`null` if [_awaitPendingCallbacks] has been called
+  /// while [_pendingCallbacks] is non-zero.
+  Completer _noPendingCallbacks;
+
+  /// The timer for keeping track of task timeouts. This may be null.
+  Timer _timeoutTimer;
+
+  /// Creates a new schedule with empty task queues.
+  Schedule() {
+    _tasks = new TaskQueue._("tasks", this);
+    _onComplete = new TaskQueue._("onComplete", this);
+    _onException = new TaskQueue._("onException", this);
+    _currentQueue = _tasks;
+
+    heartbeat();
+  }
+
+  /// Sets up this schedule by running [setUp], then runs all the task queues in
+  /// order. Any errors in [setUp] will cause [onException] to run.
+  Future run(void setUp()) {
+    return new Future.immediate(null).then((_) {
+      try {
+        setUp();
+      } catch (e, stackTrace) {
+        throw new ScheduleError.from(this, e, stackTrace: stackTrace);
+      }
+
+      _state = ScheduleState.RUNNING;
+      return tasks._run();
+    }).catchError((e) {
+      errors.add(e);
+      return onException._run().catchError((innerError) {
+        // If an error occurs in a task in the onException queue, make sure it's
+        // registered in the error list and re-throw it. We could also re-throw
+        // `e`; ultimately, all the errors will be shown to the user if any
+        // ScheduleError is thrown.
+        errors.add(innerError);
+        throw innerError;
+      }).then((_) {
+        // If there are no errors in the onException queue, re-throw the
+        // original error that caused it to run.
+        throw e;
+      });
+    }).whenComplete(() {
+      return onComplete._run().catchError((e) {
+        // If an error occurs in a task in the onComplete queue, make sure it's
+        // registered in the error list and re-throw it.
+        errors.add(e);
+        throw e;
+      });
+    }).whenComplete(() {
+      if (_timeoutTimer != null) _timeoutTimer.cancel();
+      _state = ScheduleState.DONE;
+    });
+  }
+
+  /// Signals that an out-of-band error has occurred. Using [wrapAsync] along
+  /// with `throw` is usually preferable to calling this directly.
+  ///
+  /// The metadata in [AsyncError]s and [ScheduleError]s will be preserved.
+  void signalError(error, [stackTrace]) {
+    heartbeat();
+
+    var scheduleError = new ScheduleError.from(this, error,
+        stackTrace: stackTrace);
+    if (_state == ScheduleState.DONE) {
+      throw new StateError(
+          "An out-of-band error was signaled outside of wrapAsync after the "
+              "schedule finished running.\n"
+          "${errorString()}");
+    } else if (state == ScheduleState.SET_UP) {
+      // If we're setting up, throwing the error will pipe it into the main
+      // error-handling code.
+      throw scheduleError;
+    } else {
+      _currentQueue._signalError(scheduleError);
+    }
+  }
+
+  /// Notifies the schedule of an error that occurred in a task or out-of-band
+  /// callback after the appropriate queue has timed out. If this schedule is
+  /// still running, the error will be added to the errors list to be shown
+  /// along with the timeout error; otherwise, a top-level error will be thrown.
+  void _signalPostTimeoutError(error, [stackTrace]) {
+    var scheduleError = new ScheduleError.from(this, error,
+        stackTrace: stackTrace);
+    errors.add(scheduleError);
+    if (_state == ScheduleState.DONE) {
+      throw new StateError(
+        "An out-of-band error was caught after the test timed out.\n"
+        "${errorString()}");
+    }
+  }
+
+  /// Returns a function wrapping [fn] that pipes any errors into the schedule
+  /// chain. This will also block the current task queue from completing until
+  /// the returned function has been called. It's used to ensure that
+  /// out-of-band callbacks are properly handled by the scheduled test.
+  ///
+  /// The top-level `wrapAsync` function should usually be used in preference to
+  /// this.
+  Function wrapAsync(fn(arg)) {
+    if (_state == ScheduleState.DONE) {
+      throw new StateError("wrapAsync called after the schedule has finished "
+          "running.");
+    }
+    heartbeat();
+
+    var queue = currentQueue;
+    // It's possible that the queue timed out before this.
+    bool _timedOut() => queue != currentQueue || _pendingCallbacks == 0;
+
+    _pendingCallbacks++;
+    return (arg) {
+      try {
+        return fn(arg);
+      } catch (e, stackTrace) {
+        if (_timedOut()) {
+          _signalPostTimeoutError(e, stackTrace);
+        } else {
+          signalError(e, stackTrace);
+        }
+      } finally {
+        if (_timedOut()) return;
+
+        _pendingCallbacks--;
+        if (_pendingCallbacks == 0 && _noPendingCallbacks != null) {
+          _noPendingCallbacks.complete();
+          _noPendingCallbacks = null;
+        }
+      }
+    };
+  }
+
+  /// Returns a string representation of all errors registered on this schedule.
+  String errorString() {
+    if (errors.isEmpty) return "The schedule had no errors.";
+    if (errors.length == 1) return errors.first.toString();
+    var errorStrings = errors.map((e) => e.toString()).join("\n================"
+        "================================================================\n");
+    return "The schedule had ${errors.length} errors:\n$errorStrings";
+  }
+
+  /// Notifies the schedule that progress is being made on an asynchronous task.
+  /// This resets the timeout timer, and can be used in long-running tasks to
+  /// keep them from timing out.
+  void heartbeat() {
+    if (_timeoutTimer != null) _timeoutTimer.cancel();
+    if (_timeout == null) {
+      _timeoutTimer = null;
+    } else {
+      _timeoutTimer = mock_clock.newTimer(_timeout, _signalTimeout);
+    }
+  }
+
+  /// The callback to run when the timeout timer fires. Notifies the current
+  /// queue that a timeout has occurred.
+  void _signalTimeout(_) {
+    // Reset the timer so that we can detect timeouts in the onException and
+    // onComplete queues.
+    _timeoutTimer = null;
+
+    var error = new ScheduleError.from(this, "The schedule timed out after "
+        "$_timeout of inactivity.");
+
+    _pendingCallbacks = 0;
+    if (_noPendingCallbacks != null) {
+      var noPendingCallbacks = _noPendingCallbacks;
+      _noPendingCallbacks = null;
+      noPendingCallbacks.completeError(error);
+    } else {
+      currentQueue._signalTimeout(error);
+    }
+  }
+
+  /// Returns a [Future] that will complete once there are no pending
+  /// out-of-band callbacks.
+  Future _awaitNoPendingCallbacks() {
+    if (_pendingCallbacks == 0) return new Future.immediate(null);
+    if (_noPendingCallbacks == null) _noPendingCallbacks = new Completer();
+    return _noPendingCallbacks.future;
+  }
+}
+
+/// An enum of states for a [Schedule].
+class ScheduleState {
+  /// The schedule can have tasks added to its queue, but is not yet running
+  /// them.
+  static const SET_UP = const ScheduleState._("SET_UP");
+
+  /// The schedule is actively running tasks. This includes running tasks in
+  /// [Schedule.onException] and [Schedule.onComplete].
+  static const RUNNING = const ScheduleState._("RUNNING");
+
+  /// The schedule has finished running all its tasks, either successfully or
+  /// with an error.
+  static const DONE = const ScheduleState._("DONE");
+
+  /// The name of the state.
+  final String name;
+
+  const ScheduleState._(this.name);
+
+  String toString() => name;
+}
+
+/// A queue of asynchronous tasks to execute in order.
+class TaskQueue {
+  // TODO(nweiz): make this a read-only view when issue 8321 is fixed.
+  /// The tasks in the queue.
+  Collection<Task> get contents => _contents;
+  final _contents = new Queue<Task>();
+
+  /// The name of the queue, for debugging purposes.
+  final String name;
+
+  /// The [Schedule] that created this queue.
+  final Schedule _schedule;
+
+  /// An out-of-band error signaled by [_schedule]. If this is non-null, it
+  /// indicates that the queue should stop as soon as possible and re-throw this
+  /// error.
+  ScheduleError _error;
+
+  /// The [SubstituteFuture] for the currently-running task in the queue, or
+  /// null if no task is currently running.
+  SubstituteFuture _taskFuture;
+
+  TaskQueue._(this.name, this._schedule);
+
+  /// Schedules a task, [fn], to run asynchronously as part of this queue. Tasks
+  /// will be run in the order they're scheduled. In [fn] returns a [Future],
+  /// tasks after it won't be run until that [Future] completes.
+  ///
+  /// The return value will be completed once the scheduled task has finished
+  /// running. Its return value is the same as the return value of [fn], or the
+  /// value it completes to if it's a [Future].
+  ///
+  /// If [description] is passed, it's used to describe the task for debugging
+  /// purposes when an error occurs.
+  Future schedule(fn(), [String description]) {
+    var task = new Task(fn, this, description);
+    _contents.add(task);
+    return task.result;
+  }
+
+  /// Runs all the tasks in this queue in order.
+  Future _run() {
+    _schedule._currentQueue = this;
+    _schedule.heartbeat();
+    return Future.forEach(_contents, (task) {
+      _schedule._currentTask = task;
+      if (_error != null) throw _error;
+
+      _taskFuture = new SubstituteFuture(task.fn());
+      return _taskFuture.whenComplete(() {
+        _taskFuture = null;
+        _schedule.heartbeat();
+      }).catchError((e) {
+        if (_error != null) _schedule.errors.add(_error);
+        throw new ScheduleError.from(_schedule, e);
+      });
+    }).whenComplete(() {
+      _schedule._currentTask = null;
+      return _schedule._awaitNoPendingCallbacks();
+    }).then((_) {
+      _schedule.heartbeat();
+      if (_error != null) throw _error;
+    });
+  }
+
+  /// Signals that an out-of-band error has been detected and the queue should
+  /// stop running as soon as possible.
+  void _signalError(ScheduleError error) {
+    // If multiple errors are detected while a task is running, make sure the
+    // earlier ones are recorded in the schedule.
+    if (_error != null) _schedule.errors.add(_error);
+    _error = error;
+  }
+
+  /// Notifies the queue that it has timed out and it needs to terminate
+  /// immediately with a timeout error.
+  void _signalTimeout(ScheduleError error) {
+    if (_taskFuture != null) {
+      // Catch errors coming off the old task future, in case it completes after
+      // timing out.
+      _taskFuture.substitute(new Future.immediateError(error)).catchError((e) {
+        _schedule._signalPostTimeoutError(e);
+      });
+    } else {
+      // This branch probably won't be reached, but it's conceivable that the
+      // event loop might get pumped when _taskFuture is null but we haven't yet
+      // called _awaitNoPendingCallbacks.
+      _signalError(error);
+    }
+  }
+
+  String toString() => name;
+
+  /// Returns a detailed representation of the queue as a tree of tasks. If
+  /// [highlight] is passed, that task is specially highlighted.
+  ///
+  /// [highlight] must be a task in this queue.
+  String generateTree([Task highlight]) {
+    assert(highlight == null || highlight.queue == this);
+    return _contents.map((task) {
+      var lines = task.toString().split("\n");
+      var firstLine = task == highlight ?
+          "> ${lines.first}" : "* ${lines.first}";
+      lines = new List.from(lines.skip(1).map((line) => "| $line"));
+      lines.insertRange(0, 1, firstLine);
+      return lines.join("\n");
+    }).join("\n");
+  }
+}
diff --git a/pkg/scheduled_test/lib/src/schedule_error.dart b/pkg/scheduled_test/lib/src/schedule_error.dart
new file mode 100644
index 0000000..76d6e00
--- /dev/null
+++ b/pkg/scheduled_test/lib/src/schedule_error.dart
@@ -0,0 +1,86 @@
+// 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 schedule_error;
+
+import 'dart:async';
+
+import 'schedule.dart';
+import 'task.dart';
+import 'utils.dart';
+
+/// A wrapper for errors that occur during a scheduled test.
+class ScheduleError extends AsyncError {
+  /// The schedule during which this error occurred.
+  final Schedule schedule;
+
+  /// The task that was running when this error occurred. This may be `null` if
+  /// there was no such task.
+  final Task task;
+
+  /// The task queue that was running when this error occured. This may be
+  /// `null` if there was no such queue.
+  final TaskQueue queue;
+
+  /// The state of the schedule at the time the error was detected.
+  final ScheduleState _stateWhenDetected;
+
+  /// Creates a new [ScheduleError] wrapping [error]. The metadata in
+  /// [AsyncError]s and [ScheduleError]s will be preserved.
+  factory ScheduleError.from(Schedule schedule, error, {stackTrace,
+      AsyncError cause}) {
+    if (error is ScheduleError) {
+      if (schedule == null) schedule = error.schedule;
+    }
+
+    if (error is AsyncError) {
+      // Overwrite the explicit stack trace, because it probably came from a
+      // rethrow in the first place.
+      stackTrace = error.stackTrace;
+      if (cause == null) cause = error.cause;
+      error = error.error;
+    }
+
+    return new ScheduleError(schedule, error, stackTrace, cause);
+  }
+
+  ScheduleError(Schedule schedule, error, stackTrace, AsyncError cause)
+      : super.withCause(error, stackTrace, cause),
+        this.schedule = schedule,
+        this.task = schedule.currentTask,
+        this.queue = schedule.currentQueue,
+        this._stateWhenDetected = schedule.state;
+
+  String toString() {
+    var result = new StringBuffer();
+
+    var errorString = error.toString();
+    if (errorString.contains("\n")) {
+      result.add('ScheduleError:\n');
+      result.add(prefixLines(errorString.trim()));
+      result.add("\n\n");
+    } else {
+      result.add('ScheduleError: "$errorString"\n');
+    }
+
+    result.add('Stack trace:\n');
+    result.add(prefixLines(stackTrace.toString().trim()));
+    result.add("\n\n");
+
+    if (task != null) {
+      result.add('Error detected during task in queue "$queue":\n');
+      result.add(task.generateTree());
+    } else if (_stateWhenDetected == ScheduleState.DONE) {
+      result.add('Error detected after all tasks in the schedule had '
+          'finished.');
+    } else if (_stateWhenDetected == ScheduleState.RUNNING) {
+      result.add('Error detected when waiting for out-of-band callbacks in '
+          'queue "$queue".');
+    } else { // _stateWhenDetected == ScheduleState.SET_UP
+      result.add('Error detected before the schedule started running.');
+    }
+
+    return result.toString();
+  }
+}
diff --git a/pkg/scheduled_test/lib/src/substitute_future.dart b/pkg/scheduled_test/lib/src/substitute_future.dart
new file mode 100644
index 0000000..4122a3d
--- /dev/null
+++ b/pkg/scheduled_test/lib/src/substitute_future.dart
@@ -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.
+
+library substitute_future;
+
+import 'dart:async';
+
+/// A wrapper for [Future] that allows other [Future]s to be substituted in as
+/// the wrapped [Future]. This is used for injecting timeout errors into
+/// long-running [Future]s.
+class SubstituteFuture<T> implements Future<T> {
+  /// The wrapped [Future].
+  Future<T> _inner;
+
+  /// The completer that corresponds to [this]'s result.
+  final Completer<T> _completer = new Completer<T>();
+
+  /// Whether or not [this] has been completed yet.
+  bool _complete = false;
+
+  SubstituteFuture(Future wrapped) {
+    substitute(wrapped);
+  }
+
+  Stream<T> asStream() => _completer.future.asStream();
+  Future catchError(onError(AsyncError asyncError), {bool test(error)}) =>
+    _completer.future.catchError(onError, test: test);
+  Future then(onValue(T value), {onError(AsyncError asyncError)}) =>
+    _completer.future.then(onValue, onError: onError);
+  Future<T> whenComplete(action()) => _completer.future.whenComplete(action);
+
+  /// Substitutes [newFuture] for the currently wrapped [Future], which is
+  /// returned.
+  Future<T> substitute(Future<T> newFuture) {
+    if (_complete) {
+      throw new StateError("You may not call substitute on a SubstituteFuture "
+          "that's already complete.");
+    }
+
+    var oldFuture = _inner;
+    _inner = newFuture;
+    _inner.then((value) {
+      if (_inner != newFuture) return;
+      _completer.complete(value);
+      _complete = true;
+    }).catchError((error) {
+      if (_inner != newFuture) return;
+      _completer.completeError(error);
+      _complete = true;
+    });
+    return oldFuture;
+  }
+}
diff --git a/pkg/scheduled_test/lib/src/task.dart b/pkg/scheduled_test/lib/src/task.dart
new file mode 100644
index 0000000..558a9cd
--- /dev/null
+++ b/pkg/scheduled_test/lib/src/task.dart
@@ -0,0 +1,51 @@
+// 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 task;
+
+import 'dart:async';
+
+import 'schedule.dart';
+import 'utils.dart';
+
+typedef Future TaskBody();
+
+/// A single task to be run as part of a [TaskQueue].
+class Task {
+  /// The queue to which this [Task] belongs.
+  final TaskQueue queue;
+
+  /// A description of this task. Used for debugging. May be `null`.
+  final String description;
+
+  /// The body of the task.
+  TaskBody fn;
+
+  /// The identifier of the task. This is unique within [queue]. It's used for
+  /// debugging when [description] isn't provided.
+  int _id;
+
+  /// A Future that will complete to the return value of [fn] once this task
+  /// finishes running.
+  Future get result => _resultCompleter.future;
+  final _resultCompleter = new Completer();
+
+  Task(fn(), this.queue, this.description) {
+    _id = this.queue.contents.length;
+    this.fn = () {
+      var future = new Future.immediate(null).then((_) => fn());
+      chainToCompleter(future, _resultCompleter);
+      return future;
+    };
+
+    // Make sure any error thrown by fn isn't top-leveled by virtue of being
+    // passed to the result future.
+    result.catchError((_) {});
+  }
+
+  String toString() => description == null ? "#$_id" : description;
+
+  /// Returns a detailed representation of [queue] with this task highlighted.
+  String generateTree() => queue.generateTree(this);
+}
diff --git a/pkg/scheduled_test/lib/src/utils.dart b/pkg/scheduled_test/lib/src/utils.dart
new file mode 100644
index 0000000..b5e086e
--- /dev/null
+++ b/pkg/scheduled_test/lib/src/utils.dart
@@ -0,0 +1,26 @@
+// 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:async';
+
+/// Configures [future] so that its result (success or exception) is passed on
+/// to [completer].
+void chainToCompleter(Future future, Completer completer) {
+  future.then((value) => completer.complete(value),
+      onError: (e) => completer.completeError(e.error, e.stackTrace));
+}
+
+/// Prepends each line in [text] with [prefix].
+String prefixLines(String text, {String prefix: '| '}) =>
+  text.split('\n').map((line) => '$prefix$line').join('\n');
+
+/// Returns a [Future] that completes after pumping the event queue [times]
+/// times. By default, this should pump the event queue enough times to allow
+/// any code to run, as long as it's not waiting on some external event.
+Future pumpEventQueue([int times=200]) {
+  if (times == 0) return new Future.immediate(null);
+  return new Future.immediate(null).then((_) => pumpEventQueue(times - 1));
+}
diff --git a/pkg/scheduled_test/pubspec.yaml b/pkg/scheduled_test/pubspec.yaml
new file mode 100644
index 0000000..10db576
--- /dev/null
+++ b/pkg/scheduled_test/pubspec.yaml
@@ -0,0 +1,13 @@
+name: scheduled_test
+author: "Dart Team <misc@dartlang.org>"
+homepage: http://www.dartlang.org
+description: >
+  A package for writing readable tests of asynchronous behavior.
+
+  This package works by building up a queue of asynchronous tasks called a
+  "schedule", then executing those tasks in order. This allows the tests to
+  read like synchronous, linear code, despite executing asynchronously.
+
+dependencies:
+  unittest: any
+  pathos: ">=0.3.2 <1.0.0"
diff --git a/pkg/scheduled_test/test/metatest.dart b/pkg/scheduled_test/test/metatest.dart
new file mode 100644
index 0000000..ff85d51
--- /dev/null
+++ b/pkg/scheduled_test/test/metatest.dart
@@ -0,0 +1,206 @@
+// 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.
+
+/// A test library for testing test libraries? We must go deeper.
+///
+/// Since unit testing code tends to use a lot of global state, it can be tough
+/// to test. This library manages it by running each test case in a child
+/// isolate, then reporting the results back to the parent isolate.
+library metatest;
+
+import 'dart:async';
+import 'dart:isolate';
+
+import '../../../pkg/path/lib/path.dart' as path;
+import 'package:unittest/unittest.dart';
+
+import 'utils.dart';
+
+/// Declares a test with the given [description] and [body]. [body] corresponds
+/// to the `main` method of a test file, and will be run in an isolate. By
+/// default, this expects that all tests defined in [body] pass, but if
+/// [passing] is passed, only tests listed there are expected to pass.
+void expectTestsPass(String description, void body(), {List<String> passing}) {
+  _setUpTest(description, body, (results) {
+    if (_hasError(results)) {
+      throw 'Expected all tests to pass, but got error(s):\n'
+          '${_summarizeTests(results)}';
+    } else if (passing == null) {
+      if (results['failed'] != 0) {
+        throw 'Expected all tests to pass, but some failed:\n'
+            '${_summarizeTests(results)}';
+      }
+    } else {
+      var shouldPass = new Set.from(passing);
+      var didPass = new Set.from(results['results']
+          .where((t) => t['result'] == 'pass')
+          .map((t) => t['description']));
+
+      if (!shouldPass.containsAll(didPass) ||
+          !didPass.containsAll(shouldPass)) {
+        String stringify(Set<String> tests) =>
+          '{${tests.map((t) => '"$t"').join(', ')}}';
+
+        fail('Expected exactly ${stringify(shouldPass)} to pass, but '
+            '${stringify(didPass)} passed.\n'
+            '${_summarizeTests(results)}');
+      }
+    }
+  });
+}
+
+/// Declares a test with the given [description] and [body]. [body] corresponds
+/// to the `main` method of a test file, and will be run in an isolate. Expects
+/// all tests defined by [body] to fail.
+void expectTestsFail(String description, void body()) {
+  _setUpTest(description, body, (results) {
+    if (_hasError(results)) {
+      throw 'Expected all tests to fail, but got error(s):\n'
+          '${_summarizeTests(results)}';
+    } else if (results['passed'] != 0) {
+      throw 'Expected all tests to fail, but some passed:\n'
+          '${_summarizeTests(results)}';
+    }
+  });
+}
+
+/// Sets up a test with the given [description] and [body]. After the test runs,
+/// calls [validate] with the result map.
+void _setUpTest(String description, void body(), void validate(Map)) {
+  _inChildIsolate.then((inIsolate) {
+    if (inIsolate) {
+      _ensureInitialized();
+      if (_testToRun == description) body();
+    } else {
+      test(description, () {
+        expect(_runInIsolate(description).then(validate), completes);
+      });
+    }
+  });
+}
+
+/// The description of the test to run in the child isolate. `null` in the
+/// parent isolate. Not set until [_inChildIsolate] completes.
+String _testToRun;
+
+/// The port with which the child isolate should communicate with the parent
+/// isolate. `null` in the parent isolate. Not set until [_inChildIsolate]
+/// completes.
+SendPort _replyTo;
+
+/// The cached [Future] for [_inChildIsolate].
+Future<bool> _inChildIsolateFuture;
+
+/// Returns whether or not we're running in a child isolate that's supposed to
+/// run a test.
+Future<bool> get _inChildIsolate {
+  if (_inChildIsolateFuture != null) return _inChildIsolateFuture;
+
+  var completer = new Completer();
+  port.receive((message, replyTo) {
+    _testToRun = message;
+    _replyTo = replyTo;
+    port.close();
+    completer.complete(true);
+  });
+
+  // TODO(nweiz): don't use a timeout here once issue 8416 is fixed.
+  _inChildIsolateFuture = timeout(completer.future, 500, () {
+    port.close();
+    return false;
+  });
+  return _inChildIsolateFuture;
+}
+
+/// Runs the test described by [description] in its own isolate. Returns a map
+/// describing the results of that test run.
+Future<Map> _runInIsolate(String description) {
+  // TODO(nweiz): Don't use path here once issue 8440 is fixed.
+  var future = spawnUri(path.join(path.current, new Options().script))
+      .call(description);
+  // TODO(nweiz): Remove this timeout once issue 8417 is fixed and we can
+  // capture top-level exceptions.
+  return timeout(future, 30 * 1000, () {
+    throw 'Timed out waiting for test to complete.';
+  });
+}
+
+/// Returns whether [results] (a test result map) describes a test run in which
+/// an error occurred.
+bool _hasError(Map results) {
+  return results['errors'] > 0 || results['uncaughtError'] != null ||
+    (results['passed'] == 0 && results['failed'] == 0);
+}
+
+/// Returns a string description of the test run descibed by [results].
+String _summarizeTests(Map results) {
+  var buffer = new StringBuffer();
+  for (var t in results["results"]) {
+    buffer.add("${t['result'].toUpperCase()}: ${t['description']}\n");
+    if (t['message'] != '') buffer.add("${_indent(t['message'])}\n");
+    if (t['stackTrace'] != null && t['stackTrace'] != '') {
+      buffer.add("${_indent(t['stackTrace'])}\n");
+    }
+  }
+
+  buffer.add("\n");
+
+  var success = false;
+  if (results['passed'] == 0 && results['failed'] == 0 &&
+      results['errors'] == 0 && results['uncaughtError'] == null) {
+    buffer.add('No tests found.');
+    // This is considered a failure too.
+  } else if (results['failed'] == 0 && results['errors'] == 0 &&
+      results['uncaughtError'] == null) {
+    buffer.add('All ${results['passed']} tests passed.');
+    success = true;
+  } else {
+    if (results['uncaughtError'] != null) {
+      buffer.add('Top-level uncaught error: ${results['uncaughtError']}');
+    }
+    buffer.add('${results['passed']} PASSED, ${results['failed']} FAILED, '
+        '${results['errors']} ERRORS');
+  }
+  return prefixLines(buffer.toString());
+}
+
+/// Indents each line of [str] by two spaces.
+String _indent(String str) {
+  // TODO(nweiz): Use this simpler code once issue 2980 is fixed.
+  // return str.replaceAll(new RegExp("^", multiLine: true), "  ");
+
+  return Strings.join(str.split("\n").map((line) => "  $line"), "\n");
+}
+
+/// Ensure that the metatest configuration is loaded.
+void _ensureInitialized() {
+  if (config is! _MetaConfiguration) configure(new _MetaConfiguration());
+}
+
+/// Special test configuration for use within the child isolates. This hides all
+/// output and reports data back to the parent isolate.
+class _MetaConfiguration extends Configuration {
+  final name = "MetaConfiguration";
+
+  void logTestCaseMesssage(TestCase testCase, String message) {}
+
+  void onSummary(int passed, int failed, int errors, List<TestCase> results,
+      String uncaughtError) {
+    _replyTo.send({
+      "passed": passed,
+      "failed": failed,
+      "errors": errors,
+      "uncaughtError": uncaughtError,
+      "results": results.map((testCase) => {
+        "description": testCase.description,
+        "message": testCase.message,
+        "result": testCase.result,
+        "stackTrace": testCase.stackTrace
+      }).toList()
+    });
+  }
+
+  void onInit() {}
+  void onDone(bool success) {}
+}
diff --git a/pkg/scheduled_test/test/scheduled_test_test.dart b/pkg/scheduled_test/test/scheduled_test_test.dart
new file mode 100644
index 0000000..dcf5bea
--- /dev/null
+++ b/pkg/scheduled_test/test/scheduled_test_test.dart
@@ -0,0 +1,920 @@
+// 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 scheduled_test_test;
+
+import 'dart:async';
+
+import 'package:scheduled_test/scheduled_test.dart';
+import 'package:scheduled_test/src/mock_clock.dart' as mock_clock;
+
+import 'metatest.dart';
+import 'utils.dart';
+
+void main() {
+  expectTestsPass('a scheduled test with a correct synchronous expectation '
+      'should pass', () {
+    test('test', () {
+      expect('foo', equals('foo'));
+    });
+  });
+
+  expectTestsFail('a scheduled test with an incorrect synchronous expectation '
+      'should fail', () {
+    test('test', () {
+      expect('foo', equals('bar'));
+    });
+  });
+
+  expectTestsPass('a scheduled test with a correct asynchronous expectation '
+      'should pass', () {
+    test('test', () {
+      expect(new Future.immediate('foo'), completion(equals('foo')));
+    });
+  });
+
+  expectTestsFail('a scheduled test with an incorrect asynchronous expectation '
+      'should fail', () {
+    test('test', () {
+      expect(new Future.immediate('foo'), completion(equals('bar')));
+    });
+  });
+
+  expectTestsPass('a passing scheduled synchronous expect should register', () {
+    test('test', () {
+      schedule(() => expect('foo', equals('foo')));
+    });
+  });
+
+  expectTestsFail('a failing scheduled synchronous expect should register', () {
+    test('test', () {
+      schedule(() => expect('foo', equals('bar')));
+    });
+  });
+
+  expectTestsPass('a passing scheduled asynchronous expect should '
+      'register', () {
+    test('test', () {
+      schedule(() =>
+          expect(new Future.immediate('foo'), completion(equals('foo'))));
+    });
+  });
+
+  expectTestsFail('a failing scheduled synchronous expect should '
+      'register', () {
+    test('test', () {
+      schedule(() =>
+          expect(new Future.immediate('foo'), completion(equals('bar'))));
+    });
+  });
+
+  expectTestsPass('scheduled blocks should be run in order after the '
+      'synchronous setup', () {
+    test('test', () {
+      var list = [1];
+      schedule(() => list.add(2));
+      list.add(3);
+      schedule(() => expect(list, equals([1, 3, 4, 2])));
+      list.add(4);
+    });
+  });
+
+  expectTestsPass('scheduled blocks should forward their return values as '
+      'Futures', () {
+    test('synchronous value', () {
+      var future = schedule(() => 'value');
+      expect(future, completion(equals('value')));
+    });
+
+    test('asynchronous value', () {
+      var future = schedule(() => new Future.immediate('value'));
+      expect(future, completion(equals('value')));
+    });
+  });
+
+  expectTestsPass('scheduled blocks should wait for their Future return values '
+      'to complete before proceeding', () {
+    test('test', () {
+      var value = 'unset';
+      schedule(() => pumpEventQueue().then((_) {
+        value = 'set';
+      }));
+      schedule(() => expect(value, equals('set')));
+    });
+  });
+
+  expectTestsFail('a test failure in a chained future in a scheduled block '
+      'should be registered', () {
+    test('test', () {
+      schedule(() => new Future.immediate('foo')
+          .then((v) => expect(v, equals('bar'))));
+    });
+  });
+
+  expectTestsFail('an error in a chained future in a scheduled block should be '
+      'registered', () {
+    test('test', () {
+      schedule(() => new Future.immediate(null).then((_) {
+        throw 'error';
+      }));
+    });
+  });
+
+  expectTestsFail('an out-of-band failure in wrapAsync is handled', () {
+    mock_clock.mock().run();
+    test('test', () {
+      schedule(() {
+        sleep(1).then(wrapAsync((_) => expect('foo', equals('bar'))));
+      });
+      schedule(() => sleep(2));
+    });
+  });
+
+  expectTestsFail('an out-of-band failure in wrapAsync that finishes after the '
+      'schedule is handled', () {
+    mock_clock.mock().run();
+    test('test', () {
+      schedule(() {
+        sleep(2).then(wrapAsync((_) => expect('foo', equals('bar'))));
+      });
+      schedule(() => sleep(1));
+    });
+  });
+
+  expectTestsFail('an out-of-band error reported via signalError is '
+      'handled', () {
+    mock_clock.mock().run();
+    test('test', () {
+      schedule(() {
+        sleep(1).then((_) => currentSchedule.signalError('bad'));
+      });
+      schedule(() => sleep(2));
+    });
+  });
+
+  expectTestsFail('an out-of-band error reported via signalError that finished '
+      'after the schedule is handled', () {
+    mock_clock.mock().run();
+    test('test', () {
+      schedule(() {
+        var done = wrapAsync((_) {});
+        sleep(2).then((_) {
+          currentSchedule.signalError('bad');
+          done(null);
+        });
+      });
+      schedule(() => sleep(1));
+    });
+  });
+
+  expectTestsFail('a synchronous error reported via signalError is handled', () {
+    test('test', () {
+      currentSchedule.signalError('bad');
+    });
+  });
+
+  expectTestsPass('the onComplete queue is run if a test is successful', () {
+    var onCompleteRun = false;
+    test('test 1', () {
+      currentSchedule.onComplete.schedule(() {
+        onCompleteRun = true;
+      });
+
+      schedule(() => expect('foo', equals('foo')));
+    });
+
+    test('test 2', () {
+      expect(onCompleteRun, isTrue);
+    });
+  });
+
+  expectTestsPass('the onComplete queue is run after an out-of-band callback',
+      () {
+    var outOfBandRun = false;
+    test('test1', () {
+      currentSchedule.onComplete.schedule(() {
+        expect(outOfBandRun, isTrue);
+      });
+
+      pumpEventQueue().then(wrapAsync((_) {
+        outOfBandRun = true;
+      }));
+    });
+  });
+
+  expectTestsPass('the onComplete queue is run after an out-of-band callback '
+      'and waits for another out-of-band callback', () {
+    var outOfBand1Run = false;
+    var outOfBand2Run = false;
+    test('test1', () {
+      currentSchedule.onComplete.schedule(() {
+        expect(outOfBand1Run, isTrue);
+
+        pumpEventQueue().then(wrapAsync((_) {
+          outOfBand2Run = true;
+        }));
+      });
+
+      pumpEventQueue().then(wrapAsync((_) {
+        outOfBand1Run = true;
+      }));
+    });
+
+    test('test2', () => expect(outOfBand2Run, isTrue));
+  });
+
+  expectTestsFail('an out-of-band callback in the onComplete queue blocks the '
+      'test', () {
+    test('test', () {
+      currentSchedule.onComplete.schedule(() {
+        pumpEventQueue().then(wrapAsync((_) => expect('foo', equals('bar'))));
+      });
+    });
+  });
+
+  expectTestsPass('an out-of-band callback blocks onComplete even with an '
+      'unrelated error', () {
+    var outOfBandRun = false;
+    var outOfBandSetInOnComplete = false;
+    test('test 1', () {
+      currentSchedule.onComplete.schedule(() {
+        outOfBandSetInOnComplete = outOfBandRun;
+      });
+
+      pumpEventQueue().then(wrapAsync((_) {
+        outOfBandRun = true;
+      }));
+
+      schedule(() => expect('foo', equals('bar')));
+    });
+
+    test('test 2', () => expect(outOfBandSetInOnComplete, isTrue));
+  }, passing: ['test 2']);
+
+  expectTestsPass('the onComplete queue is run after an asynchronous error',
+      () {
+    var onCompleteRun = false;
+    test('test 1', () {
+      currentSchedule.onComplete.schedule(() {
+        onCompleteRun = true;
+      });
+
+      schedule(() => expect('foo', equals('bar')));
+    });
+
+    test('test 2', () {
+      expect(onCompleteRun, isTrue);
+    });
+  }, passing: ['test 2']);
+
+  expectTestsPass('the onComplete queue is run after a synchronous error', () {
+    var onCompleteRun = false;
+    test('test 1', () {
+      currentSchedule.onComplete.schedule(() {
+        onCompleteRun = true;
+      });
+
+      throw 'error';
+    });
+
+    test('test 2', () {
+      expect(onCompleteRun, isTrue);
+    });
+  }, passing: ['test 2']);
+
+  expectTestsPass('the onComplete queue is run after an out-of-band error', () {
+    var onCompleteRun = false;
+    test('test 1', () {
+      currentSchedule.onComplete.schedule(() {
+        onCompleteRun = true;
+      });
+
+      pumpEventQueue().then(wrapAsync((_) => expect('foo', equals('bar'))));
+    });
+
+    test('test 2', () {
+      expect(onCompleteRun, isTrue);
+    });
+  }, passing: ['test 2']);
+
+  expectTestsPass('currentSchedule.errors contains the error in the onComplete '
+      'queue', () {
+    var errors;
+    test('test 1', () {
+      currentSchedule.onComplete.schedule(() {
+        errors = currentSchedule.errors;
+      });
+
+      throw 'error';
+    });
+
+    test('test 2', () {
+      expect(errors, everyElement(new isInstanceOf<ScheduleError>()));
+      expect(errors.map((e) => e.error), equals(['error']));
+    });
+  }, passing: ['test 2']);
+
+  expectTestsPass('onComplete tasks can be scheduled during normal tasks', () {
+    var onCompleteRun = false;
+    test('test 1', () {
+      schedule(() {
+        currentSchedule.onComplete.schedule(() {
+          onCompleteRun = true;
+        });
+      });
+    });
+
+    test('test 2', () {
+      expect(onCompleteRun, isTrue);
+    });
+  });
+
+  expectTestsFail('failures in onComplete cause test failures', () {
+    test('test', () {
+      currentSchedule.onComplete.schedule(() {
+        expect('foo', equals('bar'));
+      });
+    });
+  });
+
+  expectTestsPass('the onException queue is not run if a test is successful',
+      () {
+    var onExceptionRun = false;
+    test('test 1', () {
+      currentSchedule.onException.schedule(() {
+        onExceptionRun = true;
+      });
+
+      schedule(() => expect('foo', equals('foo')));
+    });
+
+    test('test 2', () {
+      expect(onExceptionRun, isFalse);
+    });
+  });
+
+  expectTestsPass('the onException queue is run after an asynchronous error',
+      () {
+    var onExceptionRun = false;
+    test('test 1', () {
+      currentSchedule.onException.schedule(() {
+        onExceptionRun = true;
+      });
+
+      schedule(() => expect('foo', equals('bar')));
+    });
+
+    test('test 2', () {
+      expect(onExceptionRun, isTrue);
+    });
+  }, passing: ['test 2']);
+
+  expectTestsPass('the onException queue is run after a synchronous error', () {
+    var onExceptionRun = false;
+    test('test 1', () {
+      currentSchedule.onException.schedule(() {
+        onExceptionRun = true;
+      });
+
+      throw 'error';
+    });
+
+    test('test 2', () {
+      expect(onExceptionRun, isTrue);
+    });
+  }, passing: ['test 2']);
+
+  expectTestsPass('the onException queue is run after an out-of-band error', () {
+    var onExceptionRun = false;
+    test('test 1', () {
+      currentSchedule.onException.schedule(() {
+        onExceptionRun = true;
+      });
+
+      pumpEventQueue().then(wrapAsync((_) => expect('foo', equals('bar'))));
+    });
+
+    test('test 2', () {
+      expect(onExceptionRun, isTrue);
+    });
+  }, passing: ['test 2']);
+
+  expectTestsPass('currentSchedule.errors contains the error in the '
+      'onException queue', () {
+    var errors;
+    test('test 1', () {
+      currentSchedule.onException.schedule(() {
+        errors = currentSchedule.errors;
+      });
+
+      throw 'error';
+    });
+
+    test('test 2', () {
+      expect(errors, everyElement(new isInstanceOf<ScheduleError>()));
+      expect(errors.map((e) => e.error), equals(['error']));
+    });
+  }, passing: ['test 2']);
+
+  expectTestsPass('currentSchedule.errors contains an error passed into '
+      'signalError synchronously', () {
+    var errors;
+    test('test 1', () {
+      currentSchedule.onException.schedule(() {
+        errors = currentSchedule.errors;
+      });
+
+      currentSchedule.signalError('error');
+    });
+
+    test('test 2', () {
+      expect(errors, everyElement(new isInstanceOf<ScheduleError>()));
+      expect(errors.map((e) => e.error), equals(['error']));
+    });
+  }, passing: ['test 2']);
+
+  expectTestsPass('currentSchedule.errors contains an error passed into '
+      'signalError asynchronously', () {
+    var errors;
+    test('test 1', () {
+      currentSchedule.onException.schedule(() {
+        errors = currentSchedule.errors;
+      });
+
+      schedule(() => currentSchedule.signalError('error'));
+    });
+
+    test('test 2', () {
+      expect(errors, everyElement(new isInstanceOf<ScheduleError>()));
+      expect(errors.map((e) => e.error), equals(['error']));
+    });
+  }, passing: ['test 2']);
+
+  expectTestsPass('currentSchedule.errors contains an error passed into '
+      'signalError out-of-band', () {
+    var errors;
+    test('test 1', () {
+      currentSchedule.onException.schedule(() {
+        errors = currentSchedule.errors;
+      });
+
+      pumpEventQueue().then(wrapAsync((_) {
+        return currentSchedule.signalError('error');
+      }));
+    });
+
+    test('test 2', () {
+      expect(errors, everyElement(new isInstanceOf<ScheduleError>()));
+      expect(errors.map((e) => e.error), equals(['error']));
+    });
+  }, passing: ['test 2']);
+
+  expectTestsPass('currentSchedule.errors contains errors from both the task '
+      'queue and the onException queue in onComplete', () {
+    var errors;
+    test('test 1', () {
+      currentSchedule.onComplete.schedule(() {
+        errors = currentSchedule.errors;
+      });
+
+      currentSchedule.onException.schedule(() {
+        throw 'error2';
+      });
+
+      throw 'error1';
+    });
+
+    test('test 2', () {
+      expect(errors, everyElement(new isInstanceOf<ScheduleError>()));
+      expect(errors.map((e) => e.error), equals(['error1', 'error2']));
+    });
+  }, passing: ['test 2']);
+
+  expectTestsPass('currentSchedule.errors contains multiple out-of-band errors '
+      'from both the main task queue and onException in onComplete', () {
+    mock_clock.mock().run();
+    var errors;
+    test('test 1', () {
+      currentSchedule.onComplete.schedule(() {
+        errors = currentSchedule.errors;
+      });
+
+      currentSchedule.onException.schedule(() {
+        sleep(1).then(wrapAsync((_) {
+          throw 'error3';
+        }));
+        sleep(2).then(wrapAsync((_) {
+          throw 'error4';
+        }));
+      });
+
+      sleep(1).then(wrapAsync((_) {
+        throw 'error1';
+      }));
+      sleep(2).then(wrapAsync((_) {
+        throw 'error2';
+      }));
+    });
+
+    test('test 2', () {
+      expect(errors, everyElement(new isInstanceOf<ScheduleError>()));
+      expect(errors.map((e) => e.error),
+          orderedEquals(['error1', 'error2', 'error3', 'error4']));
+    });
+  }, passing: ['test 2']);
+
+  expectTestsPass('currentSchedule.errors contains both an out-of-band error '
+      'and an error raised afterwards in a task', () {
+    mock_clock.mock().run();
+    var errors;
+    test('test 1', () {
+      currentSchedule.onComplete.schedule(() {
+        errors = currentSchedule.errors;
+      });
+
+      sleep(1).then(wrapAsync((_) {
+        throw 'out-of-band';
+      }));
+
+      schedule(() => sleep(2).then((_) {
+        throw 'in-band';
+      }));
+    });
+
+    test('test 2', () {
+      expect(errors, everyElement(new isInstanceOf<ScheduleError>()));
+      expect(errors.map((e) => e.error), equals(['out-of-band', 'in-band']));
+    });
+  }, passing: ['test 2']);
+
+  expectTestsPass('currentSchedule.currentTask returns the current task while '
+      'executing a task', () {
+    test('test', () {
+      schedule(() => expect('foo', equals('foo')), 'task 1');
+
+      schedule(() {
+        expect(currentSchedule.currentTask.description, equals('task 2'));
+      }, 'task 2');
+
+      schedule(() => expect('bar', equals('bar')), 'task 3');
+    });
+  });
+
+  expectTestsPass('currentSchedule.currentTask is null before the schedule has '
+      'started', () {
+    test('test', () {
+      schedule(() => expect('foo', equals('foo')));
+
+      expect(currentSchedule.currentTask, isNull);
+    });
+  });
+
+  expectTestsPass('currentSchedule.currentTask is null after the schedule has '
+      'completed', () {
+    test('test', () {
+      schedule(() {
+        expect(pumpEventQueue().then((_) {
+          expect(currentSchedule.currentTask, isNull);
+        }), completes);
+      });
+
+      schedule(() => expect('foo', equals('foo')));
+    });
+  });
+
+  expectTestsPass('currentSchedule.currentQueue returns the current queue while '
+      'executing a task', () {
+    test('test', () {
+      schedule(() {
+        expect(currentSchedule.currentQueue.name, equals('tasks'));
+      });
+    });
+  });
+
+  expectTestsPass('currentSchedule.currentQueue is tasks before the schedule '
+      'has started', () {
+    test('test', () {
+      schedule(() => expect('foo', equals('foo')));
+
+      expect(currentSchedule.currentQueue.name, equals('tasks'));
+    });
+  });
+
+  expectTestsPass('currentSchedule.state starts out as SET_UP', () {
+    test('test', () {
+      expect(currentSchedule.state, equals(ScheduleState.SET_UP));
+    });
+  });
+
+  expectTestsPass('currentSchedule.state is RUNNING in tasks', () {
+    test('test', () {
+      schedule(() {
+        expect(currentSchedule.state, equals(ScheduleState.RUNNING));
+      });
+
+      currentSchedule.onComplete.schedule(() {
+        expect(currentSchedule.state, equals(ScheduleState.RUNNING));
+      });
+    });
+  });
+
+  expectTestsPass('currentSchedule.state is DONE after the test', () {
+    var oldSchedule;
+    test('test 1', () {
+      oldSchedule = currentSchedule;
+    });
+
+    test('test 2', () {
+      expect(oldSchedule.state, equals(ScheduleState.DONE));
+    });
+  });
+
+  expectTestsPass('setUp is run before each test', () {
+    var setUpRun = false;
+    setUp(() {
+      setUpRun = true;
+    });
+
+    test('test 1', () {
+      expect(setUpRun, isTrue);
+      setUpRun = false;
+    });
+
+    test('test 2', () {
+      expect(setUpRun, isTrue);
+      setUpRun = false;
+    });
+  });
+
+  expectTestsPass('setUp can schedule events', () {
+    var setUpRun = false;
+    setUp(() {
+      schedule(() {
+        setUpRun = true;
+      });
+      currentSchedule.onComplete.schedule(() {
+        setUpRun = false;
+      });
+    });
+
+    test('test 1', () {
+      expect(setUpRun, isFalse);
+      schedule(() => expect(setUpRun, isTrue));
+    });
+
+    test('test 2', () {
+      expect(setUpRun, isFalse);
+      schedule(() => expect(setUpRun, isTrue));
+    });
+  });
+
+  expectTestsFail('synchronous errors in setUp will cause tests to fail', () {
+    setUp(() => expect('foo', equals('bar')));
+    test('test 1', () => expect('foo', equals('foo')));
+    test('test 2', () => expect('foo', equals('foo')));
+  });
+
+  expectTestsFail('scheduled errors in setUp will cause tests to fail', () {
+    setUp(() => schedule(() => expect('foo', equals('bar'))));
+    test('test 1', () => expect('foo', equals('foo')));
+    test('test 2', () => expect('foo', equals('foo')));
+  });
+
+  expectTestsPass('synchronous errors in setUp will cause onException to run',
+      () {
+    var onExceptionRun = false;
+    setUp(() {
+      currentSchedule.onException.schedule(() {
+        onExceptionRun = true;
+      });
+
+      if (!onExceptionRun) expect('foo', equals('bar'));
+    });
+
+    test('test 1', () => expect('foo', equals('foo')));
+    test('test 2', () => expect(onExceptionRun, isTrue));
+  }, passing: ['test 2']);
+
+  expectTestsPass("setUp doesn't apply to child groups", () {
+    var setUpRun = false;
+    setUp(() {
+      setUpRun = true;
+      currentSchedule.onComplete.schedule(() {
+        setUpRun = false;
+      });
+    });
+
+    test('outer', () {
+      expect(setUpRun, isTrue);
+    });
+
+    group('group', () {
+      test('inner', () {
+        expect(setUpRun, isFalse);
+      });
+    });
+  });
+
+  expectTestsPass("setUp doesn't apply to parent groups", () {
+    var setUpRun = false;
+    group('group', () {
+      setUp(() {
+        setUpRun = true;
+        currentSchedule.onComplete.schedule(() {
+          setUpRun = false;
+        });
+      });
+
+      test('inner', () {
+        expect(setUpRun, isTrue);
+      });
+    });
+
+    test('outer', () {
+      expect(setUpRun, isFalse);
+    });
+  });
+
+  expectTestsPass("setUp doesn't apply to sibling groups", () {
+    var setUpRun = false;
+    group('group 1', () {
+      setUp(() {
+        setUpRun = true;
+        currentSchedule.onComplete.schedule(() {
+          setUpRun = false;
+        });
+      });
+
+      test('test 1', () {
+        expect(setUpRun, isTrue);
+      });
+    });
+
+    group('group 2', () {
+      test('test 2', () {
+        expect(setUpRun, isFalse);
+      });
+    });
+  });
+
+  expectTestsPass("a single task that takes too long will cause a timeout "
+      "error", () {
+    mock_clock.mock().run();
+    var errors;
+    test('test 1', () {
+      currentSchedule.timeout = new Duration(milliseconds: 1);
+
+      currentSchedule.onException.schedule(() {
+        errors = currentSchedule.errors;
+      });
+
+      schedule(() => sleep(2));
+    });
+
+    test('test 2', () {
+      expect(errors, everyElement(new isInstanceOf<ScheduleError>()));
+      expect(errors.map((e) => e.error), equals(["The schedule timed out after "
+        "0:00:00.001 of inactivity."]));
+    });
+  }, passing: ['test 2']);
+
+  expectTestsPass("an out-of-band callback that takes too long will cause a "
+      "timeout error", () {
+    mock_clock.mock().run();
+    var errors;
+    test('test 1', () {
+      currentSchedule.timeout = new Duration(milliseconds: 1);
+
+      currentSchedule.onException.schedule(() {
+        errors = currentSchedule.errors;
+      });
+
+      sleep(2).then(wrapAsync((_) => expect('foo', equals('foo'))));
+    });
+
+    test('test 2', () {
+      expect(errors, everyElement(new isInstanceOf<ScheduleError>()));
+      expect(errors.map((e) => e.error), equals(["The schedule timed out after "
+        "0:00:00.001 of inactivity."]));
+    });
+  }, passing: ['test 2']);
+
+  expectTestsPass("each task resets the timeout timer", () {
+    mock_clock.mock().run();
+    test('test', () {
+      currentSchedule.timeout = new Duration(milliseconds: 2);
+
+      schedule(() => sleep(1));
+      schedule(() => sleep(1));
+      schedule(() => sleep(1));
+    });
+  });
+
+  expectTestsPass("setting up the test doesn't trigger a timeout", () {
+    var clock = mock_clock.mock();
+    test('test', () {
+      currentSchedule.timeout = new Duration(milliseconds: 1);
+
+      clock.tick(2);
+      schedule(() => expect('foo', equals('foo')));
+    });
+  });
+
+  expectTestsPass("an out-of-band error that's signaled after a timeout but "
+      "before the test completes is registered", () {
+    mock_clock.mock().run();
+    var errors;
+    test('test 1', () {
+      currentSchedule.timeout = new Duration(milliseconds: 3);
+
+      currentSchedule.onException.schedule(() => sleep(2));
+      currentSchedule.onException.schedule(() {
+        errors = currentSchedule.errors;
+      });
+
+      sleep(4).then(wrapAsync((_) {
+        throw 'out-of-band';
+      }));
+    });
+
+    test('test 2', () {
+      expect(errors, everyElement(new isInstanceOf<ScheduleError>()));
+      expect(errors.map((e) => e.error), equals([
+        "The schedule timed out after 0:00:00.003 of inactivity.",
+        "out-of-band"
+      ]));
+    });
+  }, passing: ['test 2']);
+
+  expectTestsPass("an out-of-band error that's signaled after a timeout but "
+      "before the test completes plays nicely with other out-of-band callbacks",
+      () {
+    mock_clock.mock().run();
+    var errors;
+    var onExceptionCallbackRun = false;
+    var onCompleteRunAfterOnExceptionCallback = false;
+    test('test 1', () {
+      currentSchedule.timeout = new Duration(milliseconds: 2);
+
+      currentSchedule.onException.schedule(() {
+        sleep(1).then(wrapAsync((_) {
+          onExceptionCallbackRun = true;
+        }));
+      });
+
+      currentSchedule.onComplete.schedule(() {
+        onCompleteRunAfterOnExceptionCallback = onExceptionCallbackRun;
+      });
+
+      sleep(3).then(wrapAsync((_) {
+        throw 'out-of-band';
+      }));
+    });
+
+    test('test 2', () {
+      expect(onCompleteRunAfterOnExceptionCallback, isTrue);
+    });
+  }, passing: ['test 2']);
+
+  expectTestsPass("a task that times out while waiting to handle an "
+      "out-of-band error records both", () {
+    mock_clock.mock().run();
+    var errors;
+    test('test 1', () {
+      currentSchedule.timeout = new Duration(milliseconds: 2);
+
+      currentSchedule.onException.schedule(() {
+        errors = currentSchedule.errors;
+      });
+
+      schedule(() => sleep(4));
+      sleep(1).then((_) => currentSchedule.signalError('out-of-band'));
+    });
+
+    test('test 2', () {
+      expect(errors, everyElement(new isInstanceOf<ScheduleError>()));
+      expect(errors.map((e) => e.error), equals([
+        "out-of-band",
+        "The schedule timed out after 0:00:00.002 of inactivity."
+      ]));
+    });
+  }, passing: ['test 2']);
+
+  expectTestsPass("currentSchedule.heartbeat resets the timeout timer", () {
+    mock_clock.mock().run();
+    test('test', () {
+      currentSchedule.timeout = new Duration(milliseconds: 3);
+
+      schedule(() {
+        return sleep(2).then((_) {
+          currentSchedule.heartbeat();
+          return sleep(2);
+        });
+      });
+    });
+  });
+
+  // TODO(nweiz): test out-of-band post-timeout errors that are detected after
+  // the test finishes once we can detect top-level errors (issue 8417).
+}
diff --git a/pkg/scheduled_test/test/substitute_future_test.dart b/pkg/scheduled_test/test/substitute_future_test.dart
new file mode 100644
index 0000000..f9c8b32
--- /dev/null
+++ b/pkg/scheduled_test/test/substitute_future_test.dart
@@ -0,0 +1,154 @@
+// 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 substitute_future_test;
+
+import 'dart:async';
+
+import 'package:scheduled_test/src/substitute_future.dart';
+import 'package:unittest/unittest.dart';
+
+void main() {
+  group('with no substitution, works like a normal Future for', () {
+    var completer;
+    var future;
+
+    setUp(() {
+      completer = new Completer();
+      future = new SubstituteFuture(completer.future);
+    });
+
+    test('.asStream on success', () {
+      expect(future.asStream().toList(), completion(equals(['success'])));
+      completer.complete('success');
+    });
+
+    test('.asStream on error', () {
+      expect(future.asStream().toList(), throwsA(equals('error')));
+      completer.completeError('error');
+    });
+
+    test('.then and .catchError on success', () {
+      expect(future.then((v) => "transformed $v")
+              .catchError((e) => "caught ${e.error}"),
+          completion(equals('transformed success')));
+      completer.complete('success');
+    });
+
+    test('.then and .catchError on error', () {
+      expect(future.then((v) => "transformed $v")
+              .catchError((e) => "caught ${e.error}"),
+          completion(equals('caught error')));
+      completer.completeError('error');
+    });
+
+    test('.then with onError on success', () {
+      expect(future.then((v) => "transformed $v",
+              onError: (e) => "caught ${e.error}"),
+          completion(equals('transformed success')));
+      completer.complete('success');
+    });
+
+    test('.then with onError on error', () {
+      expect(future.then((v) => "transformed $v",
+              onError: (e) => "caught ${e.error}"),
+          completion(equals('caught error')));
+      completer.completeError('error');
+    });
+
+    test('.whenComplete on success', () {
+      expect(future.whenComplete(() {
+        throw 'whenComplete';
+      }), throwsA(equals('whenComplete')));
+      completer.complete('success');
+    });
+
+    test('.whenComplete on error', () {
+      expect(future.whenComplete(() {
+        throw 'whenComplete';
+      }), throwsA(equals('whenComplete')));
+      completer.completeError('error');
+    });
+  });
+
+  group('with a single substitution', () {
+    var oldCompleter;
+    var oldFuture;
+    var newCompleter;
+    var future;
+
+    setUp(() {
+      oldCompleter = new Completer();
+      future = new SubstituteFuture(oldCompleter.future);
+      newCompleter = new Completer();
+      oldFuture = future.substitute(newCompleter.future);
+    });
+
+    test('pipes a success from the new future to the substitute future, and '
+        'from the old future to the return value of .substitute', () {
+      expect(oldFuture, completion(equals('old')));
+      expect(future, completion(equals('new')));
+
+      oldCompleter.complete('old');
+      newCompleter.complete('new');
+    });
+
+    test('pipes an error from the new future to the substitute future, and '
+        'from the old future to the return value of .substitute', () {
+      expect(oldFuture, throwsA(equals('old')));
+      expect(future, throwsA(equals('new')));
+
+      oldCompleter.completeError('old');
+      newCompleter.completeError('new');
+    });
+  });
+
+  group('with multiple substitutions', () {
+    var completer1;
+    var completer2;
+    var completer3;
+    var future1;
+    var future2;
+    var future;
+
+    setUp(() {
+      completer1 = new Completer();
+      completer2 = new Completer();
+      completer3 = new Completer();
+      future = new SubstituteFuture(completer1.future);
+      future1 = future.substitute(completer2.future);
+      future2 = future.substitute(completer3.future);
+    });
+
+    test('pipes a success from the newest future to the substitute future, and '
+        'from the old futures to the return values of .substitute', () {
+      expect(future1, completion(equals(1)));
+      expect(future2, completion(equals(2)));
+      expect(future, completion(equals(3)));
+
+      completer1.complete(1);
+      completer2.complete(2);
+      completer3.complete(3);
+    });
+
+    test('pipes an error from the newest future to the substitute future, and '
+        'from the old futures to the return values of .substitute', () {
+      expect(future1, throwsA(equals(1)));
+      expect(future2, throwsA(equals(2)));
+      expect(future, throwsA(equals(3)));
+
+      completer1.completeError(1);
+      completer2.completeError(2);
+      completer3.completeError(3);
+    });
+  });
+
+  test('substituting after a future has completed is an error', () {
+    var completer = new Completer();
+    var future = new SubstituteFuture(completer.future);
+    completer.complete('success');
+    expect(() => future.substitute(new Future.immediate(null)),
+        throwsStateError);
+  });
+}
diff --git a/pkg/scheduled_test/test/utils.dart b/pkg/scheduled_test/test/utils.dart
new file mode 100644
index 0000000..bd87edf
--- /dev/null
+++ b/pkg/scheduled_test/test/utils.dart
@@ -0,0 +1,49 @@
+// 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 test_utils;
+
+import 'dart:async';
+
+import 'package:scheduled_test/src/utils.dart';
+import 'package:scheduled_test/src/mock_clock.dart' as mock_clock;
+
+export 'package:scheduled_test/src/utils.dart';
+
+/// Wraps [input] to provide a timeout. If [input] completes before
+/// [milliseconds] have passed, then the return value completes in the same way.
+/// However, if [milliseconds] pass before [input] has completed, [onTimeout] is
+/// run and its result is passed to [input] (with chaining, if it returns a
+/// [Future]).
+///
+/// Note that timing out will not cancel the asynchronous operation behind
+/// [input].
+Future timeout(Future input, int milliseconds, onTimeout()) {
+  bool completed = false;
+  var completer = new Completer();
+  var timer = new Timer(milliseconds, (_) {
+    completed = true;
+    chainToCompleter(new Future.immediate(null).then((_) => onTimeout()),
+        completer);
+  });
+  input.then((value) {
+    if (completed) return;
+    timer.cancel();
+    completer.complete(value);
+  }).catchError((e) {
+    if (completed) return;
+    timer.cancel();
+    completer.completeError(e.error, e.stackTrace);
+  });
+  return completer.future;
+}
+
+/// Returns a [Future] that will complete in [milliseconds].
+Future sleep(int milliseconds) {
+  var completer = new Completer();
+  mock_clock.newTimer(new Duration(milliseconds: milliseconds), (_) {
+    completer.complete();
+  });
+  return completer.future;
+}
diff --git a/pkg/serialization/lib/serialization.dart b/pkg/serialization/lib/serialization.dart
index 3f2146d..a72dc57 100644
--- a/pkg/serialization/lib/serialization.dart
+++ b/pkg/serialization/lib/serialization.dart
@@ -21,8 +21,8 @@
  * This creates a new serialization and adds a rule for address objects. Right
  * now it has to be passed an address instance because of limitations using
  * Address as a literal. Then we ask the Serialization to write the address
- * and we get back a String which is a [JSON] representation of the state of
- * it and related objects.
+ * and we get back a Map which is a [json]able representation of the state of
+ * the address and related objects.
  *
  * The version above used reflection to automatically identify the public
  * fields of the address object. We can also specify those fields explicitly.
@@ -105,7 +105,8 @@
  * There are cases where the constructor needs values that we can't easily get
  * from the serialized object. For example, we may just want to pass null, or a
  * constant value. To support this, we can specify as constructor fields
- * values that aren't field names. If any value isn't a String, it will be
+ * values that aren't field names. If any value isn't a String, or is a string
+ * that doesn't correspond to a field name, it will be
  * treated as a constant and passed unaltered to the constructor.
  *
  * In some cases a non-constructor field should not be set using field
@@ -125,7 +126,9 @@
  *
  * By default this uses a representation in which objects are represented as
  * maps keyed by field name, but in which references between objects have been
- * converted into Reference objects. This is then encoded as a [json] string.
+ * converted into Reference objects. This is then typically encoded as
+ * a [json] string, but can also be used in other ways, e.g. sent to another
+ * isolate.
  *
  * We can write objects in different formats by passing a [Format] object to
  * the [write] method or by getting a [Writer] object. The available formats
@@ -133,12 +136,12 @@
  * and a simple JSON format that produces output more suitable for talking to
  * services that expect JSON in a predefined format. Examples of these are
  *
- *      String output = serialization.write(address, new SimpleMapFormat());
+ *      Map output = serialization.write(address, new SimpleMapFormat());
  *      List output = serialization.write(address, new SimpleFlatFormat());
- *      Map output = serialization.write(address, new SimpleJsonFormat());
+ *      var output = serialization.write(address, new SimpleJsonFormat());
  * Or, using a [Writer] explicitly
  *      var writer = serialization.newWriter(new SimpleFlatFormat());
- *      var output = writer.write(address);
+ *      List output = writer.write(address);
  *
  * These representations are not yet considered stable.
  *
@@ -146,7 +149,7 @@
  * =======
  * To read objects, the corresponding [read] method can be used.
  *
- *       Address input = serialization.read(aString);
+ *       Address input = serialization.read(input);
  *
  * When reading, the serialization instance doing the reading must be configured
  * with compatible rules to the one doing the writing. It's possible for the
@@ -235,6 +238,8 @@
    * is off by default.
    */
   bool get selfDescribing {
+    // 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);
   }
@@ -344,14 +349,16 @@
 
   /**
    * Read the serialized data from [input] and return the root object
-   * from the result. If there are objects that need to be resolved
+   * from the result. The [input] can be of any type that the [Format]
+   * reads/writes, but normally will be a [List], [Map], or a simple type.
+   * If there are objects that need to be resolved
    * in the current context, they should be provided in [externals] as a
    * Map from names to values. In particular, in the current implementation
    * any class mirrors needed should be provided in [externals] using the
    * class name as a key. In addition to the [externals] map provided here,
-   * values will be looked up in the [externalObjects] map.
+   * values will be looked up in the [namedObjects] map.
    */
-  read(String input, [Map externals = const {}]) {
+  read(input, [Map externals = const {}]) {
     return newReader().read(input, externals);
   }
 
diff --git a/pkg/serialization/lib/src/basic_rule.dart b/pkg/serialization/lib/src/basic_rule.dart
index d8f1ebe..303a9e1 100644
--- a/pkg/serialization/lib/src/basic_rule.dart
+++ b/pkg/serialization/lib/src/basic_rule.dart
@@ -161,7 +161,7 @@
    * Extract the state from [object] using an instanceMirror and the field
    * names in [fields]. Call the function [callback] on each value.
    */
-  extractState(object, Function callback) {
+  extractState(object, Function callback, Writer w) {
     var result = createStateHolder();
     var mirror = reflect(object);
 
diff --git a/pkg/serialization/lib/src/format.dart b/pkg/serialization/lib/src/format.dart
index c1d122e..ab602c9 100644
--- a/pkg/serialization/lib/src/format.dart
+++ b/pkg/serialization/lib/src/format.dart
@@ -52,23 +52,22 @@
    * This effectively defines a custom JSON serialization format, although
    * the details of the format vary depending which rules were used.
    */
-  String generateOutput(Writer w) {
+  Map<String, dynamic> generateOutput(Writer w) {
     var result = {
       "rules" : w.serializedRules(),
       "data" : w.states,
       "roots" : w._rootReferences()
     };
-    return json.stringify(result);
+    return result;
   }
 
   /**
-   * Read a [json] encoded string representing serialized data in this format
+   * Read a [json] compatible representation of serialized data in this format
    * and return the nested Map representation described in [generateOutput]. If
    * the data also includes rule definitions, then these will replace the rules
    * in the [Serialization] for [reader].
    */
-  Map<String, dynamic> read(String input, Reader reader) {
-    var topLevel = json.parse(input);
+  Map<String, dynamic> read(topLevel, Reader reader) {
     var ruleString = topLevel["rules"];
     reader.readRules(ruleString);
     return topLevel;
@@ -76,12 +75,13 @@
 }
 
 /**
- * A format for "normal" JSON representation of objects. It stores
+ * A format for "normal" [json] representation of objects. It stores
  * the fields of the objects as nested maps, and doesn't allow cycles. This can
- * be useful in talking to existing APIs that expect JSON format data. However,
- * note that since the classes of objects aren't stored, this isn't enough
- * information to read back the objects. This format also doesn't support the
- * [selfDescriptive] option on the [Serialization], as storing the rules.
+ * be useful in talking to existing APIs that expect [json] format data. The
+ * output will be either a simple object (string, num, bool), a List, or a Map,
+ * with nesting of those.
+ * Note that since the classes of objects aren't normally stored, this isn't
+ * enough information to read back the objects. However, if the
  * If the [storeRoundTripData] field of the format is set to true, then this
  * will store the rule number along with the data, allowing reconstruction.
  */
@@ -98,17 +98,27 @@
   /**
    * If we store the rule numbers, what key should we use to store them.
    */
-  String ruleIdentifier = "__rule";
+  static final String RULE = "_rule";
+  static final String RULES = "_rules";
+  static final String DATA = "_data";
+  static final String ROOTS = "_root";
 
   SimpleJsonFormat({this.storeRoundTripInfo : false});
 
   /**
-   * Generate output for this format from [w] and return it as a String which
-   * is the [json] representation of a nested Map structure.
+   * Generate output for this format from [w] and return it as
+   * the [json] representation of a nested Map structure.
    */
-  String generateOutput(Writer w) {
+  generateOutput(Writer w) {
     jsonify(w);
-    return json.stringify(w.stateForReference(w._rootReferences().first));
+    var root = w._rootReferences().first;
+    if (root is Reference) root = w.stateForReference(root);
+    if (w.selfDescribing && storeRoundTripInfo) {
+      root = new Map()
+          ..[RULES] = w.serializedRules()
+          ..[DATA] = root;
+    }
+    return root;
   }
 
   /**
@@ -134,7 +144,7 @@
         if (storeRoundTripInfo) ruleData[i].add(rule.number);
       } else if (each is Map) {
         jsonifyEntry(each, w);
-        if (storeRoundTripInfo) each[ruleIdentifier] = rule.number;
+        if (storeRoundTripInfo) each[RULE] = rule.number;
       }
     }
   }
@@ -150,19 +160,29 @@
   }
 
   /**
-   * Read a [json] encoded string representing serialized data in this format
-   * and return the Map representation that the reader expects, with top-level
+   * Read serialized data saved in this format, which should look like
+   * either a simple type, a List or a Map and return the Map
+   * representation that the reader expects, with top-level
    * entries for "rules", "data", and "roots". Nested lists/maps will be
    * converted into Reference objects. Note that if the data was not written
    * with [storeRoundTripInfo] true this will fail.
    */
-  Map<String, dynamic> read(String input, Reader r) {
-    var data = json.parse(input);
-    var result = {};
-    result["rules"] = null;
-    var ruleData =
-        new List(r.serialization.rules.length).map((x) => []).toList();
-    var top = recursivelyFixUp(data, r, ruleData);
+  Map<String, dynamic> read(data, Reader reader) {
+    var result = new Map();
+    // Check the case of having been written without additional data and
+    // read as if it had been written with storeRoundTripData set.
+    if (reader.selfDescribing && !(data.containsKey(DATA))) {
+      throw new SerializationException("Missing $DATA entry, "
+          "may mean this was written and read with different values "
+          "of selfDescribing.");
+    }
+    // If we are self-describing, we should have separate rule and data
+    // sections. If not, we assume that we have just the data at the top level.
+    var rules = reader.selfDescribing ? data[RULES] : null;
+    var actualData = reader.selfDescribing ? data[DATA] : data;
+    reader.readRules(rules);
+    var ruleData = new List(reader.rules.length).map((x) => []).toList();
+    var top = recursivelyFixUp(actualData, reader, ruleData);
     result["data"] = ruleData;
     result["roots"] = [top];
     return result;
@@ -171,20 +191,28 @@
   /**
    * Convert nested references in [data] into [Reference] objects.
    */
-  recursivelyFixUp(data, Reader r, List result) {
+  recursivelyFixUp(input, Reader r, List result) {
+    var data = input;
     if (isPrimitive(data)) {
       result[r._primitiveRule().number].add(data);
       return data;
     }
     var ruleNumber;
+    // If we've added the rule number on as the last item in a list we have
+    // to get rid of it or it will be interpreted as extra data. For a map
+    // the library will be ok, but we need to get rid of the extra key before
+    // the data is shown to the user, so we destructively modify.
     if (data is List) {
-      ruleNumber = data.removeLast();
+      ruleNumber = data.last;
+      data = data.take(data.length -1);
     } else if (data is Map) {
-      ruleNumber = data.remove(ruleIdentifier);
+      ruleNumber = data.remove(RULE);
     } else {
       throw new SerializationException("Invalid data format");
     }
-    var newData = mapValues(data, (x) => recursivelyFixUp(x, r, result));
+    // Do not use mappedBy or other lazy operations for this. They do not play
+    // well with a function that destructively modifies its arguments.
+    var newData = mapValues(data, (each) => recursivelyFixUp(each, r, result));
     result[ruleNumber].add(newData);
     return new Reference(r, ruleNumber, result[ruleNumber].length - 1);
   }
diff --git a/pkg/serialization/lib/src/reader_writer.dart b/pkg/serialization/lib/src/reader_writer.dart
index 146255d..2209f78 100644
--- a/pkg/serialization/lib/src/reader_writer.dart
+++ b/pkg/serialization/lib/src/reader_writer.dart
@@ -123,7 +123,7 @@
     if (rule.shouldUseReferenceFor(object, this)) {
       references.putIfAbsent(object, () =>
           new Reference(this, rule.number, _nextObjectNumberFor(rule)));
-      var state = rule.extractState(object, trace.note);
+      var state = rule.extractState(object, trace.note, this);
       _addStateForRule(rule, state);
     }
   }
@@ -143,7 +143,7 @@
   serializedRules() {
     if (!selfDescribing) return null;
     var meta = serialization.ruleSerialization();
-    var writer = new Writer(meta);
+    var writer = new Writer(meta, format);
     writer.selfDescribing = false;
     return writer.write(serialization._rules);
   }
@@ -309,15 +309,20 @@
    * Look up the reference to an external object. This can be held either in
    * the reader-specific list of externals or in the serializer's
    */
-  objectNamed(key) {
+  objectNamed(key, [Function ifAbsent]) {
     var map = (namedObjects.containsKey(key))
         ? namedObjects : serialization.namedObjects;
     if (!map.containsKey(key)) {
-      throw 'Cannot find named object to link to: $key';
+      (ifAbsent == null ? keyNotFound : ifAbsent)(key);
     }
     return map[key];
   }
 
+  void keyNotFound(key) {
+    throw new SerializationException(
+        'Cannot find named object to link to: $key');
+  }
+
   /**
    * Return the list of rules to be used when writing. These come from the
    * [serialization].
@@ -350,10 +355,11 @@
    * If the data we are reading from has rules written to it, read them back
    * and set them as the rules we will use.
    */
-  void readRules(String newRules) {
+  void readRules(newRules) {
     // TODO(alanknight): Replacing the serialization is kind of confusing.
-    List rulesWeRead = (newRules == null) ?
-        null : serialization.ruleSerialization().read(newRules, namedObjects);
+    if (newRules == null) return;
+    var reader = serialization.ruleSerialization().newReader(format);
+    List rulesWeRead = reader.read(newRules, namedObjects);
     if (rulesWeRead != null && !rulesWeRead.isEmpty) {
       serialization = new Serialization.blank();
       rulesWeRead.forEach(serialization.addRule);
diff --git a/pkg/serialization/lib/src/serialization_rule.dart b/pkg/serialization/lib/src/serialization_rule.dart
index 5c397b0..8c58af6 100644
--- a/pkg/serialization/lib/src/serialization_rule.dart
+++ b/pkg/serialization/lib/src/serialization_rule.dart
@@ -45,7 +45,7 @@
    * state at the end. The state that results will still have direct
    * pointers to objects, rather than references.
    */
-  extractState(object, void f(value));
+  extractState(object, void f(value), Writer w);
 
   /**
    * Allows rules to tell us how they expect to store their state. If this
@@ -142,7 +142,7 @@
 
   bool get storesStateAsLists => true;
 
-  List extractState(List list, f) {
+  List extractState(List list, f, Writer w) {
     var result = new List();
     for (var each in list) {
       result.add(each);
@@ -200,7 +200,7 @@
 
   bool get storesStateAsMaps => true;
 
-  extractState(Map map, f) {
+  extractState(Map map, f, Writer w) {
     // Note that we make a copy here because flattening may be destructive.
     var newMap = new Map.from(map);
     newMap.forEach((key, value) {
@@ -219,7 +219,7 @@
    */
   flatten(Map state, Writer writer) {
     bool keysAreAllStrings = state.keys.every((x) => x is String);
-    if (keysAreAllStrings) {
+    if (keysAreAllStrings && !writer.shouldUseReferencesForPrimitives) {
       keysAndValues(state).forEach(
           (key, value) => state[key] = writer._referenceFor(value));
       return state;
@@ -275,7 +275,7 @@
   appliesTo(object, Writer w) {
     return isPrimitive(object);
   }
-  extractState(object, Function f) => object;
+  extractState(object, Function f, Writer w) => object;
   flatten(object, Writer writer) {}
   inflateEssential(state, Reader r) => state;
   inflateNonEssential(object, _, Reader r) {}
@@ -355,7 +355,11 @@
   }
 
   /** Extract the state of the named objects as just the object itself. */
-  extractState(object, Function f) => [object];
+  extractState(object, Function f, Writer writer) {
+    var result = [nameFor(object, writer)];
+    f(result.first);
+    return result;
+  }
 
   /** When we flatten the state we save it as the name. */
   // TODO(alanknight): This seems questionable. In a truly flat format we may
@@ -364,11 +368,12 @@
   // extractState, and I'm reluctant to add yet another parameter until
   // proven necessary.
   flatten(state, Writer writer) {
-    state[0] = nameFor(state.first, writer);
+    state[0] = writer._referenceFor(state[0]);
   }
 
   /** Look up the named object and return it. */
-  inflateEssential(state, Reader r) => r.objectNamed(state.first);
+  inflateEssential(state, Reader r) =>
+      r.objectNamed(r.resolveReference(state.first));
 
   /** Set any non-essential state on the object. For this rule, a no-op. */
   inflateNonEssential(state, object, Reader r) {}
@@ -378,15 +383,28 @@
 }
 
 /**
- * This rule handles the special case of Mirrors, restricted to those that
- * have a simpleName. It knows that it applies to any such mirror and
- * automatically uses its simpleName as the key into the namedObjects.
- * When reading, the user is still responsible for adding the appropriate
- * mirrors to namedObject.
+ * This rule handles the special case of Mirrors. It stores the mirror by its
+ * qualifiedName and attempts to look it up in both the namedObjects
+ * collection, or if it's not found there, by looking it up in the mirror
+ * system. When reading, the user is responsible for supplying the appropriate
+ * values in [namedObjects] or in the [externals] paramter to
+ * [Serialization.read].
  */
 class MirrorRule extends NamedObjectRule {
   bool appliesTo(object, Writer writer) => object is DeclarationMirror;
-  nameFor(DeclarationMirror object, Writer writer) => object.simpleName;
+  nameFor(DeclarationMirror object, Writer writer) => object.qualifiedName;
+
+  inflateEssential(state, Reader r) {
+    var qualifiedName = r.resolveReference(state.first);
+    var lookupFull = r.objectNamed(qualifiedName, (x) => null);
+    if (lookupFull != null) return lookupFull;
+    var lib = qualifiedName.substring(0, qualifiedName.indexOf("."));
+    var type = qualifiedName.substring(qualifiedName.indexOf(".") + 1);
+    var lookup = r.objectNamed(type, (x) => null);
+    if (lookup != null) return lookup;
+    var libMirror = currentMirrorSystem().libraries[lib];
+    return libMirror.classes[type];
+  }
 }
 
 /**
@@ -430,7 +448,7 @@
    */
   void setState(object, List state);
 
-  extractState(instance, Function f) {
+  extractState(instance, Function f, Writer w) {
     var state = getState(instance);
     for (var each in values(state)) {
       f(each);
@@ -440,8 +458,9 @@
 
   inflateEssential(state, Reader r) => create(_lazy(state, r));
 
-  void inflateNonEssential(state, object, Reader r) =>
-      setState(object, _lazy(state, r));
+  void inflateNonEssential(state, object, Reader r) {
+    setState(object, _lazy(state, r));
+  }
 
   // We don't want to have to make the end user tell us how long the list is
   // separately, so write it out for each object, even though they're all
diff --git a/pkg/serialization/pubspec.yaml b/pkg/serialization/pubspec.yaml
index 2f70b82..897348c 100644
--- a/pkg/serialization/pubspec.yaml
+++ b/pkg/serialization/pubspec.yaml
@@ -1,6 +1,7 @@
 name: serialization
 author: "Dart Team <misc@dartlang.org>"
 homepage: http://www.dartlang.org
+documentation: http://api.dartlang.org/docs/pkg/serialization.html
 description: >
   Provide a serialization facility for Dart objects.
 dependencies:
diff --git a/pkg/serialization/test/serialization_test.dart b/pkg/serialization/test/serialization_test.dart
index f4713a2..d45f562 100644
--- a/pkg/serialization/test/serialization_test.dart
+++ b/pkg/serialization/test/serialization_test.dart
@@ -18,6 +18,9 @@
   a1.street = 'N 34th';
   a1.city = 'Seattle';
 
+  var formats = [new SimpleFlatFormat(), new SimpleMapFormat(),
+                 new SimpleJsonFormat(storeRoundTripInfo: true)];
+
   test('Basic extraction of a simple object', () {
     // TODO(alanknight): Switch these to use literal types. Issue
     var s = new Serialization()
@@ -37,8 +40,6 @@
   });
 
   test('Slightly further with a simple object', () {
-    // TODO(alanknight): Tests that rely on what index rules are going to be
-    // at are very fragile. At least abstract it to something calculated.
     var p1 = new Person()..name = 'Alice'..address = a1;
     var s = new Serialization()
         ..addRuleFor(p1).configureForMaps()
@@ -259,14 +260,23 @@
     // Create a meta-serializer, that serializes serializations, then
     // use it to serialize a basic serialization, then run a test on the
     // the result.
-    var s = new Serialization()
+    var s = new Serialization.blank()
+      // Add the rules in a deliberately unusual order.
       ..addRuleFor(new Node(''), constructorFields: ['name'])
+      ..addRule(new ListRule())
+      ..addRule(new PrimitiveRule())
       ..selfDescribing = false;
     var meta = metaSerialization();
-    var serialized = meta.write(s);
-    var s2 = new Reader(meta)
-        .read(serialized, {"Node" : reflect(new Node('')).type});
-    runRoundTripTest((x) => s2);
+    var metaWithMaps = metaSerializationUsingMaps();
+    for (var eachFormat in formats) {
+      for (var eachMeta in [meta, metaWithMaps]) {
+        var serialized = eachMeta.write(s, eachFormat);
+        var reader = new Reader(eachMeta, eachFormat);
+        var newSerialization = reader.read(serialized,
+           {"serialization_test.Node" : reflect(new Node('')).type});
+        runRoundTripTest((x) => newSerialization);
+      }
+    }
   });
 
   test("Verify we're not serializing lists twice if they're essential", () {
@@ -320,7 +330,7 @@
           constructor: 'withData',
           constructorFields: ["street", "Kirkland", "WA", "98103"],
           fields: []);
-    String out = s.write(a1);
+    var out = s.write(a1);
     var newAddress = s.read(out);
     expect(newAddress.street, a1.street);
     expect(newAddress.city, "Kirkland");
@@ -331,7 +341,7 @@
   test("Straight JSON format", () {
     var s = new Serialization();
     var writer = s.newWriter(new SimpleJsonFormat());
-    var out = writer.write(a1);
+    var out = json.stringify(writer.write(a1));
     var reconstituted = json.parse(out);
     expect(reconstituted.length, 4);
     expect(reconstituted[0], "Seattle");
@@ -339,23 +349,23 @@
 
   test("Straight JSON format, nested objects", () {
     var p1 = new Person()..name = 'Alice'..address = a1;
-    var s = new Serialization();
+    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 out = writer.write(p1);
+    var out = json.stringify(writer.write(p1));
     var reconstituted = json.parse(out);
     var expected = {
       "name" : "Alice",
       "rank" : null,
       "serialNumber" : null,
-      "__rule" : personRule.number,
+      "_rule" : personRule.number,
       "address" : {
         "street" : "N 34th",
         "city" : "Seattle",
         "state" : null,
         "zip" : null,
-        "__rule" : addressRule.number
+        "_rule" : addressRule.number
       }
     };
     expect(expected, reconstituted);
@@ -368,40 +378,65 @@
     var s = new Serialization()
       ..addRuleFor(a1)
       ..addRuleFor(p1).configureForMaps();
-    var writer = s.newWriter(new SimpleJsonFormat(storeRoundTripInfo: true));
-    var out = writer.write(p1);
-    var reader = s.newReader(new SimpleJsonFormat(storeRoundTripInfo: true));
-    var p2 = reader.read(out);
+    var p2 = writeAndReadBack(s,
+        new SimpleJsonFormat(storeRoundTripInfo: true), p1);
     expect(p2.name, "Alice");
     var a2 = p2.address;
     expect(a2.street, "N 34th");
     expect(a2.city, "Seattle");
   });
 
-  test("Straight JSON format, root is a Map", () {
+  test("Root is a Map", () {
     // Note that we can't use the usual round-trip test because it has cycles.
     var p1 = new Person()..name = 'Alice'..address = a1;
     // Use maps for one rule, lists for the other.
     var s = new Serialization()
       ..addRuleFor(a1)
       ..addRuleFor(p1).configureForMaps();
-    var format = new SimpleJsonFormat(storeRoundTripInfo: true);
-    var writer = s.newWriter(format);
-    var out = writer.write({"stuff" : p1});
-    var reader = s.newReader(format);
-    var p2 = reader.read(out)["stuff"];
-    expect(p2.name, "Alice");
-    var a2 = p2.address;
-    expect(a2.street, "N 34th");
-    expect(a2.city, "Seattle");
+    for (var eachFormat in formats) {
+      var w = s.newWriter(eachFormat);
+      var output = w.write({"stuff" : p1});
+      var r = s.newReader(w.format);
+      var result = r.read(output);
+      var p2 = result["stuff"];
+      expect(p2.name, "Alice");
+      var a2 = p2.address;
+      expect(a2.street, "N 34th");
+      expect(a2.city, "Seattle");
+    }
   });
 
+  test("Root is a List", () {
+    var s = new Serialization();
+    for (var eachFormat in formats) {
+      var result = writeAndReadBack(s, eachFormat, [a1]);
+    var a2 = result.first;
+    expect(a2.street, "N 34th");
+    expect(a2.city, "Seattle");
+    }
+  });
 
-  test("Straight JSON format, round-trip with named objects", () {
+  test("Root is a simple object", () {
+    var s = new Serialization();
+    for (var eachFormat in formats) {
+      expect(writeAndReadBack(s, eachFormat, null), null);
+      expect(writeAndReadBack(s, eachFormat, [null]), [null]);
+      expect(writeAndReadBack(s, eachFormat, 3), 3);
+      expect(writeAndReadBack(s, eachFormat, [3]), [3]);
+      expect(writeAndReadBack(s, eachFormat, "hello"), "hello");
+      expect(writeAndReadBack(s, eachFormat, [3]), [3]);
+      expect(writeAndReadBack(s, eachFormat, {"hello" : "world"}),
+          {"hello" : "world"});
+      expect(writeAndReadBack(s, eachFormat, true), true);
+    }
+  });
+
+  test("Simple JSON format, round-trip with named objects", () {
     // Note that we can't use the usual round-trip test because it has cycles.
     var p1 = new Person()..name = 'Alice'..address = a1;
     // Use maps for one rule, lists for the other.
     var s = new Serialization()
+      ..selfDescribing = false
       ..addRule(new NamedObjectRule())
       ..addRuleFor(a1)
       ..addRuleFor(p1).configureForMaps()
@@ -415,15 +450,13 @@
     expect(a2, 12);
   });
 
-  test("Maps", () {
+  test("More complicated Maps", () {
     var s = new Serialization()..selfDescribing = false;
     var p1 = new Person()..name = 'Alice'..address = a1;
     var data = new Map();
     data["simple data"] = 1;
     data[p1] = a1;
     data[a1] = p1;
-    var formats = [new SimpleFlatFormat(), new SimpleMapFormat(),
-        new SimpleJsonFormat(storeRoundTripInfo: true)];
     for (var eachFormat in formats) {
       var output = s.write(data, eachFormat);
       var reader = s.newReader(eachFormat);
@@ -456,12 +489,13 @@
     data["person"] = new Person()..name = "Foo";
     var output = s.write(data, new SimpleMapFormat());
     var mapRule = s.rules.firstMatching((x) => x is MapRule);
-    var map = json.parse(output)["data"][mapRule.number][0];
+    var map = output["data"][mapRule.number][0];
     expect(map is Map, isTrue);
     expect(map["abc"], 1);
     expect(map["def"], "ghi");
     expect(new Reader(s).asReference(map["person"]) is Reference, isTrue);
   });
+
 }
 
 /******************************************************************************
@@ -469,6 +503,13 @@
  * it easier to write the repetitive sections.
  ******************************************************************************/
 
+writeAndReadBack(Serialization s, Format format, object) {
+  var w = s.newWriter(format);
+  var output = w.write(object);
+  var r = s.newReader(w.format);
+  return r.read(output);
+}
+
 /** Create a Serialization for serializing Serializations. */
 Serialization metaSerialization() {
   // Make some bogus rule instances so we have something to feed rule creation
@@ -492,7 +533,15 @@
              rules.forEach((x) => s.reflectee.addRule(x));
            })
     ..addRule(new NamedObjectRule())
-    ..addRule(new MirrorRule());
+    ..addRule(new MirrorRule())
+    ..addRule(new MapRule());
+  return meta;
+}
+
+Serialization metaSerializationUsingMaps() {
+  var meta = metaSerialization();
+  meta.rules.where((each) => each is BasicRule)
+      .forEach((x) => x.configureForMaps());
   return meta;
 }
 
@@ -515,6 +564,7 @@
   var reader = new Reader(aSerialization);
   // We're not sure which rule needs the sample data, so put it everywhere
   // and trust that the extra will just be ignored.
+
   var fillValue = [sampleData];
   var data = [];
   for (int i = 0; i < 10; i++) {
@@ -641,7 +691,7 @@
 /** Extract the state from [object] using the rules in [s] and return it. */
 states(object, Serialization s) {
   var rules = s.rulesFor(object, null);
-  return rules.map((x) => x.extractState(object, doNothing)).toList();
+  return rules.map((x) => x.extractState(object, doNothing, null)).toList();
 }
 
 /** A hard-coded rule for serializing Node instances. */
diff --git a/pkg/unittest/lib/html_enhanced_config.dart b/pkg/unittest/lib/html_enhanced_config.dart
index 92ad206..64c7a57 100644
--- a/pkg/unittest/lib/html_enhanced_config.dart
+++ b/pkg/unittest/lib/html_enhanced_config.dart
@@ -10,6 +10,7 @@
  */
 library unittest_html_enhanced_config;
 
+import 'dart:async';
 import 'dart:collection' show LinkedHashMap;
 import 'dart:html';
 import 'unittest.dart';
@@ -19,32 +20,38 @@
   final bool _isLayoutTest;
   HtmlEnhancedConfiguration(this._isLayoutTest);
 
-  // TODO(rnystrom): Get rid of this if we get canonical closures for methods.
-  EventListener _onErrorClosure;
-  EventListener _onMessageClosure;
+  var _onErrorSubscription = null;
+  var _onMessageSubscription = null;
 
-  void _installHandlers() {
-    if (_onErrorClosure == null) {
-      _onErrorClosure =
-          (e) => handleExternalError(e, '(DOM callback has errors)');
+  void _installOnErrorHandler() {
+    if (_onErrorSubscription == null) {
       // Listen for uncaught errors.
-      window.on.error.add(_onErrorClosure);
-    }
-    if (_onMessageClosure == null) {
-      _onMessageClosure = (e) => processMessage(e);
-      // Listen for errors from JS.
-      window.on.message.add(_onMessageClosure);
+      _onErrorSubscription = window.onError.listen(
+          (e) => handleExternalError(e, '(DOM callback has errors)'));
     }
   }
 
-  void _uninstallHandlers() {
-    if (_onErrorClosure != null) {
-      window.on.error.remove(_onErrorClosure);
-      _onErrorClosure = null;
+  void _installOnMessageHandler() {
+    if (_onMessageSubscription == null) {
+      // Listen for errors from JS.
+      _onMessageSubscription = window.onMessage.listen(
+          (e) => processMessage(e));
     }
-    if (_onMessageClosure != null) {
-      window.on.message.remove(_onMessageClosure);
-      _onMessageClosure = null;
+  }
+
+  void _installHandlers() {
+    _installOnErrorHandler();
+    _installOnMessageHandler();
+  }
+
+  void _uninstallHandlers() {
+    if (_onErrorSubscription != null) {
+      _onErrorSubscription.cancel();
+      _onErrorSubscription = null;
+    }
+    if (_onMessageSubscription != null) {
+      _onMessageSubscription.cancel();
+      _onMessageSubscription = null;
     }
   }
 
@@ -61,7 +68,7 @@
 
     var cssElement = document.head.query('#${_CSSID}');
     if (cssElement == null){
-      document.head.elements.add(new Element.html(
+      document.head.children.add(new Element.html(
           '<style id="${_CSSID}"></style>'));
       cssElement = document.head.query('#${_CSSID}');
     }
@@ -72,7 +79,7 @@
 
   void onStart() {
     // Listen for uncaught errors.
-    window.on.error.add(_onErrorClosure);
+    _installOnErrorHandler();
   }
 
   void onTestResult(TestCase testCase) {}
@@ -96,25 +103,25 @@
       // changed the StringBuffer to an Element fragment
       Element te = new Element.html('<div class="unittest-table"></div>');
 
-      te.elements.add(new Element.html(passed == results.length
+      te.children.add(new Element.html(passed == results.length
           ? "<div class='unittest-overall unittest-pass'>PASS</div>"
           : "<div class='unittest-overall unittest-fail'>FAIL</div>"));
 
       // moved summary to the top since web browsers
       // don't auto-scroll to the bottom like consoles typically do.
       if (passed == results.length && uncaughtError == null) {
-        te.elements.add(new Element.html("""
+        te.children.add(new Element.html("""
           <div class='unittest-pass'>All ${passed} tests passed</div>"""));
       } else {
 
         if (uncaughtError != null) {
-          te.elements.add(new Element.html("""
+          te.children.add(new Element.html("""
             <div class='unittest-summary'>
               <span class='unittest-error'>Uncaught error: $uncaughtError</span>
             </div>"""));
         }
 
-        te.elements.add(new Element.html("""
+        te.children.add(new Element.html("""
           <div class='unittest-summary'>
             <span class='unittest-pass'>Total ${passed} passed</span>,
             <span class='unittest-fail'>${failed} failed</span>,
@@ -123,12 +130,12 @@
           </div>"""));
       }
 
-      te.elements.add(new Element.html("""
+      te.children.add(new Element.html("""
         <div><button id='btnCollapseAll'>Collapse All</button></div>
        """));
 
       // handle the click event for the collapse all button
-      te.query('#btnCollapseAll').on.click.add((_){
+      te.query('#btnCollapseAll').onClick.listen((_){
         document
           .queryAll('.unittest-row')
           .forEach((el) => el.attributes['class'] = el.attributes['class']
@@ -184,7 +191,7 @@
           var passFailClass = "unittest-group-status unittest-group-"
               "status-${groupPassFail ? 'pass' : 'fail'}";
 
-          te.elements.add(new Element.html("""
+          te.children.add(new Element.html("""
             <div>
               <div id='${safeGroup}'
                    class='unittest-group ${safeGroup} test${safeGroup}'>
@@ -203,7 +210,7 @@
           // 'safeGroup' could be empty
           var grp = (safeGroup == '') ? null : te.query('#${safeGroup}');
           if (grp != null){
-            grp.on.click.add((_){
+            grp.onClick.listen((_){
               var row = document.query('.unittest-row-${safeGroup}');
               if (row.attributes['class'].contains('unittest-row ')){
                 document.queryAll('.unittest-row-${safeGroup}').forEach(
@@ -221,8 +228,8 @@
         _buildRow(test_, te, safeGroup, !groupPassFail);
       }
 
-      document.body.elements.clear();
-      document.body.elements.add(te);
+      document.body.children.clear();
+      document.body.children.add(te);
     }
   }
 
@@ -239,7 +246,7 @@
     }
 
     addRowElement(id, status, description){
-      te.elements.add(
+      te.children.add(
         new Element.html(
           ''' <div>
                 <div class='$display unittest-row-${groupID} $background'>
@@ -270,7 +277,7 @@
   }
 
 
-  static bool get _isIE => document.window.navigator.userAgent.contains('MSIE');
+  static bool get _isIE => window.navigator.userAgent.contains('MSIE');
 
   String get _htmlTestCSS =>
   '''
diff --git a/pkg/unittest/lib/src/core_matchers.dart b/pkg/unittest/lib/src/core_matchers.dart
index 449990b..44356d2 100644
--- a/pkg/unittest/lib/src/core_matchers.dart
+++ b/pkg/unittest/lib/src/core_matchers.dart
@@ -5,14 +5,15 @@
 part of matcher;
 
 /**
- * Returns a matcher that matches empty strings, maps or collections.
+ * Returns a matcher that matches empty strings, maps or iterables
+ * (including collections).
  */
 const Matcher isEmpty = const _Empty();
 
 class _Empty extends BaseMatcher {
   const _Empty();
   bool matches(item, MatchState matchState) {
-    if (item is Map || item is Collection) {
+    if (item is Map || item is Iterable) {
       return item.isEmpty;
     } else if (item is String) {
       return item.length == 0;
@@ -134,37 +135,62 @@
     } else {
       if (expected is Iterable && canRecurse) {
         String r = _compareIterables(expected, actual,
-          _recursiveMatch, depth+1);
+            _recursiveMatch, depth+1);
         if (r != null) reason = new StringDescription(r);
       } else if (expected is Map && canRecurse) {
         if (actual is !Map) {
           reason = new StringDescription('expected a map');
-        } else if (expected.length != actual.length) {
-          reason = new StringDescription('different map lengths');
         } else {
+          var err = (expected.length == actual.length) ? '' :
+                    'different map lengths; ';
           for (var key in expected.keys) {
             if (!actual.containsKey(key)) {
-              reason = new StringDescription('missing map key ');
+              reason = new StringDescription(err);
+              reason.add('missing map key ');
               reason.addDescriptionOf(key);
               break;
             }
-            reason = _recursiveMatch(expected[key], actual[key],
-                'with key <${key}> ${location}', depth+1);
-            if (reason != null) {
-              break;
+          }
+          if (reason == null) {
+            for (var key in actual.keys) {
+              if (!expected.containsKey(key)) {
+                reason = new StringDescription(err);
+                reason.add('extra map key ');
+                reason.addDescriptionOf(key);
+                break;
+              }
+            }
+            if (reason == null) {
+              for (var key in expected.keys) {
+                reason = _recursiveMatch(expected[key], actual[key],
+                    'with key <${key}> ${location}', depth+1);
+                if (reason != null) {
+                  break;
+                }
+              }
             }
           }
         }
       } else {
-        // If we have recursed, show the expected value too; if not,
-        // expect() will show it for us.
         reason = new StringDescription();
-        if (depth > 1) {
-          reason.add('expected ').addDescriptionOf(expected).add(' but was ').
-              addDescriptionOf(actual);
-        } else {
-          reason.add('was ').addDescriptionOf(actual);
+        var eType = typeName(expected);
+        var aType = typeName(actual);
+        var includeTypes = eType != aType;
+        // If we have recursed, show the expected value too; if not,
+        // expect() will show it for us. As expect will not show type
+        // mismatches at the top level we handle those here too.
+        if (includeTypes || depth > 1) {
+          reason.add('expected ');
+          if (includeTypes) {
+            reason.add(eType).add(':');
+          }
+          reason.addDescriptionOf(expected).add(' but ');
         }
+        reason.add('was ');
+        if (includeTypes) {
+          reason.add(aType).add(':');
+        }
+        reason.addDescriptionOf(actual);
       }
     }
     if (reason != null && location.length > 0) {
@@ -173,6 +199,17 @@
     return reason;
   }
 
+  String typeName(x) {
+    // dart2js blows up on some objects (e.g. window.navigator).
+    // So we play safe here.
+    try {
+      if (x == null) return "null";
+      return x.runtimeType.toString();
+    } catch (e) {
+      return "Unknown";
+    }
+  }
+
   String _match(expected, actual) {
     Description reason = _recursiveMatch(expected, actual, '', 0);
     return reason == null ? null : reason.toString();
@@ -281,6 +318,7 @@
     this._matcher = matcher;
 
   bool matches(item, MatchState matchState) {
+    if (item is! Function && item is! Future) return false;
     if (item is Future) {
       var done = wrapAsync((fn) => fn());
 
@@ -301,7 +339,6 @@
           expect(e.error, _matcher, reason: reason);
         });
       });
-
       // It hasn't failed yet.
       return true;
     }
@@ -334,7 +371,9 @@
   Description describeMismatch(item, Description mismatchDescription,
                                MatchState matchState,
                                bool verbose) {
-    if (_matcher == null ||  matchState.state == null) {
+    if (item is! Function && item is! Future) {
+      return mismatchDescription.add(' not a Function or Future');
+    } else if (_matcher == null ||  matchState.state == null) {
       return mismatchDescription.add(' no exception');
     } else {
       mismatchDescription.
@@ -572,9 +611,10 @@
 /**
  * Returns a matcher that matches if the match argument contains
  * the expected value. For [String]s this means substring matching;
- * for [Map]s is means the map has the key, and for [Collection]s it
- * means the collection has a matching element. In the case of collections,
- * [expected] can itself be a matcher.
+ * for [Map]s it means the map has the key, and for [Iterable]s
+ * (including [Collection]s) it means the iterable has a matching
+ * element. In the case of iterables, [expected] can itself be a
+ * matcher.
  */
 Matcher contains(expected) => new _Contains(expected);
 
@@ -587,11 +627,11 @@
   bool matches(item, MatchState matchState) {
     if (item is String) {
       return item.indexOf(_expected) >= 0;
-    } else if (item is Collection) {
+    } else if (item is Iterable) {
       if (_expected is Matcher) {
         return item.any((e) => _expected.matches(e, matchState));
       } else {
-        return item.any((e) => e == _expected);
+        return item.contains(_expected);
       }
     } else if (item is Map) {
       return item.containsKey(_expected);
diff --git a/pkg/unittest/lib/src/description.dart b/pkg/unittest/lib/src/description.dart
index f566d32..cdd8193 100644
--- a/pkg/unittest/lib/src/description.dart
+++ b/pkg/unittest/lib/src/description.dart
@@ -47,6 +47,12 @@
       String description = (value == null) ? "null" : value.toString();
       if (description.startsWith('<') && description.endsWith('>')) {
           add(description);
+      } else if (description.startsWith("Instance of")) {
+        add('<');
+        add(description);
+        add(':');
+        add(value.hashCode.toString());
+        add('>');
       } else {
         add('<');
         add(description);
diff --git a/pkg/unittest/lib/src/expect.dart b/pkg/unittest/lib/src/expect.dart
index 3a5a160..5cc56fd 100644
--- a/pkg/unittest/lib/src/expect.dart
+++ b/pkg/unittest/lib/src/expect.dart
@@ -122,8 +122,24 @@
       add('\n     but: ');
   matcher.describeMismatch(actual, description, matchState, verbose);
   description.add('.\n');
-  if (verbose && actual is Iterable) {
-    description.add('Actual: ').addDescriptionOf(actual).add('\n');
+  if (verbose) {
+    if (actual is Iterable) {
+      description.add('Actual: ').addDescriptionOf(actual).add('\n');
+    } else if (actual is Map) {
+      description.add('Actual: ');
+      var count = 25;
+      for (var k in actual.keys) {
+        if (count == 0) {
+          description.add('...\n');
+          break;
+        }
+        description.addDescriptionOf(k);
+        description.add(' : ');
+        description.addDescriptionOf(actual[k]);
+        description.add('\n');
+        --count;
+      }
+    }
   }
   if (reason != null) {
     description.add(reason).add('\n');
diff --git a/pkg/unittest/lib/src/test_case.dart b/pkg/unittest/lib/src/test_case.dart
index b21f1af..8efa694 100644
--- a/pkg/unittest/lib/src/test_case.dart
+++ b/pkg/unittest/lib/src/test_case.dart
@@ -60,6 +60,8 @@
 
   bool _doneTeardown = false;
 
+  Completer _testComplete;
+
   TestCase(this.id, this.description, this.test,
            this.callbackFunctionsOutstanding)
   : currentGroup = _currentGroup,
@@ -68,18 +70,59 @@
 
   bool get isComplete => !enabled || result != null;
 
-  void run() {
-    if (enabled) {
-      result = stackTrace = null;
-      message = '';
-      _doneTeardown = false;
-      if (_setUp != null) {
-        _setUp();
-      }
-      _config.onTestStart(this);
-      startTime = new DateTime.now();
-      runningTime = null;
-      test();
+  void _prepTest() {
+    _config.onTestStart(this);
+    startTime = new DateTime.now();
+    runningTime = null;
+  }
+
+  void _runTest() {
+    _prepTest();
+    test();
+    if (result == null && callbackFunctionsOutstanding == 0) {
+      pass();
+    }
+  }
+
+  /**
+   * Perform any associated [setUp] function and run the test. Returns
+   * a [Future] that can be used to schedule the next test. If the test runs
+   * to completion synchronously, or is disabled, we return null, to
+   * tell unittest to schedule the next test immediately.
+   */
+  Future run() {
+    if (!enabled) return null;
+
+    result = stackTrace = null;
+    message = '';
+    _doneTeardown = false;
+    var rtn = _setUp == null ? null : _setUp();
+    if (rtn is Future) {
+      rtn.then(expectAsync1((_) =>_runTest(),
+          id: '[Async setUp completion handler]'))
+      .catchError((e) {
+        _prepTest();
+        // Calling error() will result in the tearDown being done.
+        // One could debate whether tearDown should be done after
+        // a failed setUp. There is no right answer, but doing it
+        // seems to be the more conservative approach, because 
+        // unittest will not stop at a test failure.
+        error("$description: Test setup failed: ${e.error}");
+      });
+    } else {
+      _runTest();
+    }
+    if (result == null) { // Not complete.
+      _testComplete = new Completer();
+      return _testComplete.future;
+    }
+    return null;
+  }
+
+  void _notifyComplete() {
+    if (_testComplete != null) {
+      _testComplete.complete(this);
+      _testComplete = null;
     }
   }
 
@@ -91,12 +134,37 @@
       runningTime = new Duration(milliseconds: 0);
     }
     if (!_doneTeardown) {
-      if (_tearDown != null) {
-        _tearDown();
-      }
       _doneTeardown = true;
+      if (_tearDown != null) {
+        var rtn = _tearDown();
+        if (rtn is Future) {
+          rtn.then((_) {
+            if (result == null) {
+              // The test passed. In some cases we will already
+              // have set this result (e.g. if the test was async
+              // and all callbacks completed). If not, we do it here.
+              pass();
+            } else {
+              // The test has already been marked as pass/fail.
+              // Just report the updated result.
+              _config.onTestResult(this);
+            }
+            _notifyComplete();
+          })
+          .catchError((e) {
+            // We don't call fail() as that will potentially result in
+            // spurious messages like 'test failed more than once'.
+            result = ERROR;
+            message = "$description: Test teardown failed: ${e.error}";
+            _config.onTestResult(this);
+            _notifyComplete();
+          });
+          return;
+        }
+      }
     }
     _config.onTestResult(this);
+    _notifyComplete();
   }
 
   void pass() {
diff --git a/pkg/unittest/lib/unittest.dart b/pkg/unittest/lib/unittest.dart
index 0bd231d..de9cc45 100644
--- a/pkg/unittest/lib/unittest.dart
+++ b/pkg/unittest/lib/unittest.dart
@@ -9,8 +9,7 @@
  * Create a pubspec.yaml file in your project and add
  * a dependency on unittest with the following lines:
  *     dependencies:
- *       unittest:
- *         sdk: unittest
+ *       unittest: any
  *
  * Then run 'pub install' from your project directory or using
  * the DartEditor.
@@ -90,19 +89,19 @@
  *       test('callback is executed once', () {
  *         // wrap the callback of an asynchronous call with [expectAsync0] if
  *         // the callback takes 0 arguments...
- *         var timer = new Timer(0, (_) => expectAsync0(() {
+ *         var timer = Timer.run(expectAsync0(() {
  *           int x = 2 + 3;
  *           expect(x, equals(5));
  *         }));
  *       });
  *
  *       test('callback is executed twice', () {
- *         var callback = (_) => expectAsync0(() {
+ *         var callback = expectAsync0(() {
  *           int x = 2 + 3;
  *           expect(x, equals(5));
  *         }, count: 2); // <-- we can indicate multiplicity to [expectAsync0]
- *         new Timer(0, callback);
- *         new Timer(0, callback);
+ *         Timer.run(callback);
+ *         Timer.run(callback);
  *       });
  *     }
  *
@@ -131,7 +130,7 @@
  *       test('callback is executed', () {
  *         // indicate ahead of time that an async callback is expected.
  *         var async = startAsync();
- *         new Timer(0, (_) {
+ *         Timer.run(() {
  *           // Guard the body of the callback, so errors are propagated
  *           // correctly.
  *           guardAsync(() {
@@ -234,34 +233,6 @@
 Map testState = {};
 
 /**
- * (Deprecated) Evaluates the [function] and validates that it throws an
- * exception. If [callback] is provided, then it will be invoked with the
- * thrown exception. The callback may do any validation it wants. In addition,
- * if it returns `false`, that also indicates an expectation failure.
- */
-void expectThrow(function, [bool callback(exception)]) {
-  bool threw = false;
-  try {
-    function();
-  } catch (e) {
-    threw = true;
-
-    // Also let the callback look at it.
-    if (callback != null) {
-      var result = callback(e);
-
-      // If the callback explicitly returned false, treat that like an
-      // expectation too. (If it returns null, though, don't.)
-      if (result == false) {
-        fail('Exception:\n$e\ndid not match expectation.');
-      }
-    }
-  }
-
-  if (threw != true) fail('An expected exception was not thrown.');
-}
-
-/**
  * Creates a new test case with the given description and body. The
  * description will include the descriptions of any surrounding group()
  * calls.
@@ -272,25 +243,6 @@
 }
 
 /**
- * (Deprecated) Creates a new async test case with the given description
- * and body. The description will include the descriptions of any surrounding
- * group() calls.
- */
-// TODO(sigmund): deprecate this API
-void asyncTest(String spec, int callbacks, TestFunction body) {
-  ensureInitialized();
-
-  final testCase = new TestCase(
-      _tests.length + 1, _fullSpec(spec), body, callbacks);
-  _tests.add(testCase);
-
-  if (callbacks < 1) {
-    testCase.error(
-        'Async tests must wait for at least one callback ', '');
-  }
-}
-
-/**
  * Creates a new test case with the given description and body. The
  * description will include the descriptions of any surrounding group()
  * calls.
@@ -329,6 +281,7 @@
   TestCase _testCase;
   Function _shouldCallBack;
   Function _isDone;
+  String _id;
   static const _sentinel = const _Sentinel();
 
   _init(Function callback, Function shouldCallBack, Function isDone,
@@ -352,14 +305,30 @@
     if (expectedCalls > 0) {
       _testCase.callbackFunctionsOutstanding++;
     }
+    _id = '';
+    // If the callback is not an anonymous closure, try to get the
+    // name.
+    var fname = callback.toString();
+    var prefix = "Function '";
+    var pos = fname.indexOf(prefix);
+    if (pos > 0) {
+      pos += prefix.length;
+      var epos = fname.indexOf("'", pos);
+      if (epos > 0) {
+        _id = "${fname.substring(pos, epos)} ";
+      }
+    }
   }
 
   _SpreadArgsHelper(callback, shouldCallBack, isDone) {
     _init(callback, shouldCallBack, isDone);
   }
 
-  _SpreadArgsHelper.fixedCallCount(callback, expectedCalls) {
+  _SpreadArgsHelper.fixedCallCount(callback, expectedCalls, id) {
     _init(callback, _checkCallCount, _allCallsDone, expectedCalls);
+    if (id != null) {
+      _id = "$id ";
+    }
   }
 
   _SpreadArgsHelper.variableCallCount(callback, isDone) {
@@ -372,7 +341,7 @@
 
   _after() {
     if (_isDone()) {
-      _handleCallbackFunctionComplete(_testNum);
+      _handleCallbackFunctionComplete(_testNum, _id);
     }
   }
 
@@ -382,7 +351,8 @@
     // Always run except if the test is done.
     if (_testCase.isComplete) {
       _testCase.error(
-          'Callback called after already being marked as done ($_actualCalls).',
+          'Callback ${_id}called after already being marked '
+          'as done ($_actualCalls).',
           '');
       return false;
     } else {
@@ -452,7 +422,7 @@
   /** Returns false if we exceded the number of expected calls. */
   bool _checkCallCount() {
     if (_actualCalls > _expectedCalls) {
-      _testCase.error('Callback called more times than expected '
+      _testCase.error('Callback ${_id}called more times than expected '
              '($_actualCalls > $_expectedCalls).', '');
       return false;
     }
@@ -466,10 +436,13 @@
  * specified [count] times before it continues with the following test.  Using
  * [_expectAsync] will also ensure that errors that occur within [callback] are
  * tracked and reported. [callback] should take between 0 and 4 positional
- * arguments (named arguments are not supported here).
+ * arguments (named arguments are not supported here). [id] can be used
+ * to provide more descriptive error messages if the callback is called more
+ * often than expected.
  */
-Function _expectAsync(Function callback, {int count: 1}) {
-  return new _SpreadArgsHelper.fixedCallCount(callback, count).invoke;
+Function _expectAsync(Function callback, {int count: 1, String id}) {
+  return new _SpreadArgsHelper.
+      fixedCallCount(callback, count, id).invoke;
 }
 
 /**
@@ -478,23 +451,28 @@
  * specified [count] times before it continues with the following test.  Using
  * [expectAsync0] will also ensure that errors that occur within [callback] are
  * tracked and reported. [callback] should take 0 positional arguments (named
- * arguments are not supported).
+ * arguments are not supported). [id] can be used to provide more
+ * descriptive error messages if the callback is called more often than
+ * expected.
  */
 // TODO(sigmund): deprecate this API when issue 2706 is fixed.
-Function expectAsync0(Function callback, {int count: 1}) {
-  return new _SpreadArgsHelper.fixedCallCount(callback, count).invoke0;
+Function expectAsync0(Function callback, {int count: 1, String id}) {
+  return new _SpreadArgsHelper.
+      fixedCallCount(callback, count, id).invoke0;
 }
 
 /** Like [expectAsync0] but [callback] should take 1 positional argument. */
 // TODO(sigmund): deprecate this API when issue 2706 is fixed.
-Function expectAsync1(Function callback, {int count: 1}) {
-  return new _SpreadArgsHelper.fixedCallCount(callback, count).invoke1;
+Function expectAsync1(Function callback, {int count: 1, String id}) {
+  return new _SpreadArgsHelper.
+      fixedCallCount(callback, count, id).invoke1;
 }
 
 /** Like [expectAsync0] but [callback] should take 2 positional arguments. */
 // TODO(sigmund): deprecate this API when issue 2706 is fixed.
-Function expectAsync2(Function callback, {int count: 1}) {
-  return new _SpreadArgsHelper.fixedCallCount(callback, count).invoke2;
+Function expectAsync2(Function callback, {int count: 1, String id}) {
+  return new _SpreadArgsHelper.
+      fixedCallCount(callback, count, id).invoke2;
 }
 
 /**
@@ -618,9 +596,10 @@
 /**
  * Register a [setUp] function for a test [group]. This function will
  * be called before each test in the group is run. Note that if groups
- * are nested only the most locally scoped [setUp] function will be run.
+ * are nested only the most locally scoped [setUpTest] function will be run.
  * [setUp] and [tearDown] should be called within the [group] before any
- * calls to [test].
+ * calls to [test]. The [setupTest] function can be asynchronous; in this
+ * case it must return a [Future].
  */
 void setUp(Function setupTest) {
   _testSetup = setupTest;
@@ -629,9 +608,10 @@
 /**
  * Register a [tearDown] function for a test [group]. This function will
  * be called after each test in the group is run. Note that if groups
- * are nested only the most locally scoped [tearDown] function will be run.
+ * are nested only the most locally scoped [teardownTest] function will be run.
  * [setUp] and [tearDown] should be called within the [group] before any
- * calls to [test].
+ * calls to [test]. The [teardownTest] function can be asynchronous; in this
+ * case it must return a [Future].
  */
 void tearDown(Function teardownTest) {
   _testTeardown = teardownTest;
@@ -641,7 +621,7 @@
  * Called when one of the callback functions is done with all expected
  * calls.
  */
-void _handleCallbackFunctionComplete(testNum) {
+void _handleCallbackFunctionComplete(testNum, [id = '']) {
   // TODO (gram): we defer this to give the nextBatch recursive
   // stack a chance to unwind. This is a temporary hack but
   // really a bunch of code here needs to be fixed. We have a
@@ -651,11 +631,9 @@
   _defer(() {
     if (_currentTest != testNum) {
       if (_tests[testNum].result == PASS) {
-        _tests[testNum].error("Unexpected extra callbacks", '');
+        _tests[testNum].error("${id}Unexpected extra callbacks", '');
       }
-      return; // Extraneous callback.
-    }
-    if (_currentTest < _tests.length) {
+    } else if (_currentTest < _tests.length) {
       final testCase = _tests[_currentTest];
       --testCase.callbackFunctionsOutstanding;
       if (testCase.callbackFunctionsOutstanding < 0) {
@@ -663,11 +641,9 @@
         testCase.error(
             'More calls to _handleCallbackFunctionComplete() than expected.',
              '');
-      } else if (testCase.callbackFunctionsOutstanding == 0) {
-        if (!testCase.isComplete) {
-          testCase.pass();
-        }
-        _nextTestCase();
+      } else if (testCase.callbackFunctionsOutstanding == 0 &&
+                 !testCase.isComplete) {
+        testCase.pass();
       }
     }
   });
@@ -675,8 +651,10 @@
 
 /** Advance to the next test case. */
 void _nextTestCase() {
-  _currentTest++;
-  _testRunner();
+  _defer(() {
+    _currentTest++;
+    _testRunner();
+  });
 }
 
 /**
@@ -695,9 +673,6 @@
  if (_currentTest < _tests.length) {
     final testCase = _tests[_currentTest];
     testCase.error(msg, trace);
-    if (testCase.callbackFunctionsOutstanding > 0) {
-      _nextTestCase();
-    }
   } else {
     _uncaughtErrorMessage = "$msg: $trace";
   }
@@ -790,10 +765,6 @@
   } else {
     _tests[testNum].error('Caught $e', trace);
   }
-  if (testNum == _currentTest &&
-      _tests[testNum].callbackFunctionsOutstanding > 0) {
-    _nextTestCase();
-  }
 }
 
 /**
@@ -802,21 +773,25 @@
  * [done] or if it fails with an exception.
  */
 _nextBatch() {
-  while (_currentTest < _tests.length) {
+  while (true) {
+    if (_currentTest >= _tests.length) {
+      _completeTests();
+      break;
+    }
     final testCase = _tests[_currentTest];
-    guardAsync(() {
-      testCase.run();
-      if (!testCase.isComplete && testCase.callbackFunctionsOutstanding == 0) {
-        testCase.pass();
-      }
-    }, null, _currentTest);
-
-    if (!testCase.isComplete &&
-        testCase.callbackFunctionsOutstanding > 0) return;
+    var f = guardAsync(testCase.run, null, _currentTest);
+    if (f != null) {
+      f.then((_){})
+       .catchError((e) {
+        testCase.error(e.toString(), e.stackTrace);
+      })
+      .whenComplete(() {
+        _nextTestCase(); // Schedule the next test.
+      });
+      break;
+    }
     _currentTest++;
   }
-
-  _completeTests();
 }
 
 /** Publish results on the page and notify controller. */
diff --git a/pkg/unittest/pubspec.yaml b/pkg/unittest/pubspec.yaml
index ea74d06..9a2a484 100644
--- a/pkg/unittest/pubspec.yaml
+++ b/pkg/unittest/pubspec.yaml
@@ -1,6 +1,7 @@
 name: unittest
 author: "Dart Team <misc@dartlang.org>"
 homepage: http://www.dartlang.org
+documentation: http://api.dartlang.org/docs/pkg/unittest.html
 description: >
  A library for writing dart unit tests.
 dependencies:
diff --git a/pkg/unittest/test/matchers_test.dart b/pkg/unittest/test/matchers_test.dart
index c95bfa1..71cdb6b 100644
--- a/pkg/unittest/test/matchers_test.dart
+++ b/pkg/unittest/test/matchers_test.dart
@@ -34,6 +34,45 @@
   featureValueOf(actual) => actual.price;
 }
 
+class SimpleIterable extends Iterable {
+  int count;
+  SimpleIterable(this.count);
+  
+  bool contains(int val) => count < val ? false : true;
+  
+  bool any(bool f(element)) {
+    for(var i = 0; i <= count; i++) {
+      if(f(i)) return true;
+    }
+    return false;
+  }
+  
+  String toString() => "<[$count]>";
+
+  Iterator get iterator {
+    return new SimpleIterator(count);
+  }
+}
+
+class SimpleIterator implements Iterator {
+  int _count;
+  int _current;
+
+  SimpleIterator(this._count);
+
+  bool moveNext() {
+    if (_count > 0) {
+      _current = _count;
+      _count--;
+      return true;
+    }
+    _current = null;
+    return false;
+  }
+
+  get current => _current;
+}
+
 void main() {
 
   initUtils();
@@ -89,6 +128,8 @@
       shouldFail(doesNotThrow, throws,
         "Expected: throws an exception but: no exception.");
       shouldPass(doesThrow, throws);
+      shouldFail(true, throws,
+          "Expected: throws an exception but: not a Function or Future.");
     });
 
     test('throwsA', () {
@@ -165,6 +206,16 @@
         "but:  exception <Exception> does not match "
             "UnsupportedError.");
     });
+    
+    test('throwsStateError', () {
+      shouldPass(() { throw new StateError(''); },
+          throwsStateError);
+      shouldFail(() { throw new Exception(); },
+          throwsStateError,
+        "Expected: throws an exception which matches StateError "
+        "but:  exception <Exception> does not match "
+            "StateError.");
+    });
 
     test('returnsNormally', () {
       shouldPass(doesNotThrow, returnsNormally);
@@ -194,6 +245,22 @@
         "but: was <[0, 0]> with length of <2>.");
       shouldPass(b, hasLength(2));
     });
+
+    test('type mismatch', () {
+      var a = new DateTime.utc(2000);
+      var b = a.toString();
+      // We should get something like:
+      //    Expected: '2000-01-01 00:00:00.000Z'
+      //    but: expected String:'2000-01-01 00:00:00.000Z'
+      //    but was DateTime:<2000-01-01 00:00:00.000Z>.
+      // However, if minification is applied, then the type names
+      // will be shortened to two letters. The key thing is that
+      // there will be a "but: expected" part in the middle;
+      // this only happens with type mismatches or mismatches
+      // inside container types.
+      shouldFail(a, equals(b),
+          matches(new RegExp("^Expected.*but: expected .*but was.*\$")));
+    });
   });
 
   group('Numeric Matchers', () {
@@ -388,6 +455,21 @@
     });
   });
 
+  group('Iterable Matchers', () {
+    test('isEmpty', () {
+      var d = new SimpleIterable(0);
+      var e = new SimpleIterable(1);
+      shouldPass(d, isEmpty);
+      shouldFail(e, isEmpty, "Expected: empty but: was <[1]>.");
+    });
+    
+    test('contains', () {
+      var d = new SimpleIterable(3);
+      shouldPass(d, contains(2));
+      shouldFail(d, contains(5), "Expected: contains <5> but: was <[3]>.");
+    });
+  });
+  
   group('Collection Matchers', () {
 
     test('isEmpty', () {
@@ -457,6 +539,47 @@
       shouldFail(a, isEmpty, "Expected: empty but: was <{foo: bar}>.");
     });
 
+    test('equals', () {
+      var a = new Map();
+      a['foo'] = 'bar';
+      var b = new Map();
+      b['foo'] = 'bar';
+      var c = new Map();
+      c['bar'] = 'foo';
+      shouldPass(a, equals(b));
+      shouldFail(b, equals(c),
+          "Expected: <{bar: foo}> but: missing map key 'bar'.");
+    });
+
+    test('equals with different lengths', () {
+      var a = new Map();
+      a['foo'] = 'bar';
+      var b = new Map();
+      b['foo'] = 'bar';
+      b['bar'] = 'foo';
+      var c = new Map();
+      c['bar'] = 'foo';
+      c['barrista'] = 'caffeine';
+      shouldFail(a, equals(b),
+          "Expected: <{bar: foo, foo: bar}> "
+          "but: different map lengths; missing map key 'bar'.");
+      shouldFail(b, equals(a),
+          "Expected: <{foo: bar}> "
+          "but: different map lengths; extra map key 'bar'.");
+      shouldFail(b, equals(c),
+          "Expected: <{bar: foo, barrista: caffeine}> "
+          "but: missing map key 'barrista'.");
+      shouldFail(c, equals(b),
+          "Expected: <{bar: foo, foo: bar}> "
+          "but: missing map key 'foo'.");
+      shouldFail(a, equals(c),
+          "Expected: <{bar: foo, barrista: caffeine}> "
+          "but: different map lengths; missing map key 'bar'.");
+      shouldFail(c, equals(a),
+          "Expected: <{foo: bar}> "
+          "but: different map lengths; missing map key 'foo'.");
+    });
+
     test('contains', () {
       var a = new Map();
       a['foo'] = 'bar';
diff --git a/pkg/unittest/test/mock_test.dart b/pkg/unittest/test/mock_test.dart
index 0a8033f..1788e82 100644
--- a/pkg/unittest/test/mock_test.dart
+++ b/pkg/unittest/test/mock_test.dart
@@ -649,4 +649,16 @@
     m3.clearLogs();
     expect(log.logs, hasLength(0));
   });
+
+  test("Mocking: instances", () {
+    var alice = new Object();
+    var bob = new Object();
+    var m = new Mock();
+    m.when(callsTo("foo", alice)).alwaysReturn(true);
+    m.when(callsTo("foo", bob)).alwaysReturn(false);
+    expect(m.foo(alice), true);
+    expect(m.foo(bob), false);
+    expect(m.foo(alice), true);
+    expect(m.foo(bob), false);
+  });
 }
diff --git a/pkg/unittest/test/test_utils.dart b/pkg/unittest/test/test_utils.dart
index 4a7e1ae..d783665 100644
--- a/pkg/unittest/test/test_utils.dart
+++ b/pkg/unittest/test/test_utils.dart
@@ -26,20 +26,20 @@
   errorCount = 0;
   errorString = '';
   expect(value, matcher);
-  afterTest(_) {
+  afterTest() {
     configureExpectFailureHandler(null);
     expect(errorCount, equals(1));
     if (expected is String) {
       expect(errorString, equalsIgnoringWhitespace(expected));
     } else {
-     expect(errorString, expected);
+     expect(errorString.replaceAll('\n',''), expected);
     }
   }
 
   if (isAsync) {
-    new Timer(0, expectAsync1(afterTest));
+    Timer.run(expectAsync0(afterTest));
   } else {
-    afterTest(null);
+    afterTest();
   }
 }
 
@@ -48,13 +48,13 @@
   errorCount = 0;
   errorString = '';
   expect(value, matcher);
-  afterTest(_) {
+  afterTest() {
     configureExpectFailureHandler(null);
     expect(errorCount, equals(0));
   }
   if (isAsync) {
-    new Timer(0, expectAsync1(afterTest));
+    Timer.run(expectAsync0(afterTest));
   } else {
-    afterTest(null);
+    afterTest();
   }
 }
diff --git a/pkg/unittest/test/unittest_test.dart b/pkg/unittest/test/unittest_test.dart
index 98fe22a..0ee50c2 100644
--- a/pkg/unittest/test/unittest_test.dart
+++ b/pkg/unittest/test/unittest_test.dart
@@ -11,6 +11,7 @@
 
 library unittestTest;
 import 'dart:isolate';
+import 'dart:async';
 import 'package:unittest/unittest.dart';
 
 var tests; // array of test names
@@ -76,6 +77,7 @@
     _port.send(_result);
   }
 }
+
 runTest() {
   port.receive((testName, sendport) {
     configure(_testconfig = new TestConfiguration(sendport));
@@ -113,7 +115,7 @@
         () =>_defer(expectAsync0((){ ++_testconfig.count;})));
     } else if (testName == 'excess callback test') {
       test(testName, () {
-        var _callback = expectAsync0((){ ++_testconfig.count;});
+        var _callback = expectAsync0(() => ++_testconfig.count);
         _defer(_callback);
         _defer(_callback);
       });
@@ -154,6 +156,84 @@
           done();
         });
       });
+    } else if (testName == 'async setup/teardown test') {
+      group('good setup/good teardown', () {
+        setUp(() { 
+          var completer = new Completer();
+          _defer(() {
+            completer.complete(0);
+          });
+          return completer.future;
+        });
+        tearDown(() {
+          var completer = new Completer();
+          _defer(() {
+            completer.complete(0);
+          });
+          return completer.future;
+        });
+        test('foo1', (){});
+      });
+      group('good setup/bad teardown', () {
+        setUp(() { 
+          var completer = new Completer();
+          _defer(() {
+            completer.complete(0);
+          });
+          return completer.future;
+        });
+        tearDown(() {
+          var completer = new Completer();
+          _defer(() {
+            //throw "Failed to complete tearDown";
+            completer.completeError(
+                new AsyncError("Failed to complete tearDown"));
+          });
+          return completer.future;
+        });
+        test('foo2', (){});
+      });
+      group('bad setup/good teardown', () {
+        setUp(() { 
+          var completer = new Completer();
+          _defer(() {
+            //throw "Failed to complete setUp";
+            completer.completeError(new AsyncError("Failed to complete setUp"));
+          });
+          return completer.future;
+        });
+        tearDown(() {
+          var completer = new Completer();
+          _defer(() {
+            completer.complete(0);
+          });
+          return completer.future;
+        });
+        test('foo3', (){});
+      });
+      group('bad setup/bad teardown', () {
+        setUp(() { 
+          var completer = new Completer();
+          _defer(() {
+            //throw "Failed to complete setUp";
+            completer.completeError(new AsyncError("Failed to complete setUp"));
+          });
+          return completer.future;
+        });
+        tearDown(() {
+          var completer = new Completer();
+          _defer(() {
+            //throw "Failed to complete tearDown";
+            completer.completeError(
+                new AsyncError("Failed to complete tearDown"));
+          });
+          return completer.future;
+        });
+        test('foo4', (){});
+      });
+      // The next test is just to make sure we make steady progress 
+      // through the tests.
+      test('post groups', () {});
     }
   });
 }
@@ -186,7 +266,8 @@
     'completion test',
     'async exception test',
     'late exception test',
-    'middle exception test'
+    'middle exception test',
+    'async setup/teardown test'
   ];
 
   expected = [
@@ -205,8 +286,20 @@
         message: 'Callback called more times than expected (2 > 1).'),
     buildStatusString(1, 0, 0, tests[9], count: 10),
     buildStatusString(0, 1, 0, tests[10], message: 'Caught error!'),
-    buildStatusString(1, 0, 1, 'testOne', message: 'Callback called after already being marked as done (1).:testTwo:'),
-    buildStatusString(2, 1, 0, 'testOne::testTwo:Expected: false but: was <true>.:testThree')
+    buildStatusString(1, 0, 1, 'testOne',
+        message: 'Callback called after already being marked as done '
+                 '(1).:testTwo:'),
+    buildStatusString(2, 1, 0,
+        'testOne::testTwo:Expected: false but: was <true>.:testThree'),
+    buildStatusString(2, 0, 3, 
+        'good setup/good teardown foo1::'
+        'good setup/bad teardown foo2:good setup/bad teardown '
+        'foo2: Test teardown failed: Failed to complete tearDown:'
+        'bad setup/good teardown foo3:bad setup/good teardown '
+        'foo3: Test setup failed: Failed to complete setUp:'
+        'bad setup/bad teardown foo4:bad setup/bad teardown '
+        'foo4: Test teardown failed: Failed to complete tearDown:'
+        'post groups')
   ];
 
   actual = [];
diff --git a/runtime/bin/dartutils.cc b/runtime/bin/dartutils.cc
index 7eac81a..2f281ac 100644
--- a/runtime/bin/dartutils.cc
+++ b/runtime/bin/dartutils.cc
@@ -159,6 +159,11 @@
 }
 
 
+bool DartUtils::IsDartBuiltinLibURL(const char* url_name) {
+  return (strcmp(url_name, kBuiltinLibURL) == 0);
+}
+
+
 bool DartUtils::IsDartJsonLibURL(const char* url_name) {
   return (strcmp(url_name, kJsonLibURL) == 0);
 }
diff --git a/runtime/bin/dartutils.h b/runtime/bin/dartutils.h
index 03c9477..eca83d5 100644
--- a/runtime/bin/dartutils.h
+++ b/runtime/bin/dartutils.h
@@ -99,6 +99,7 @@
   static bool IsDartExtensionSchemeURL(const char* url_name);
   static bool IsDartCryptoLibURL(const char* url_name);
   static bool IsDartIOLibURL(const char* url_name);
+  static bool IsDartBuiltinLibURL(const char* url_name);
   static bool IsDartJsonLibURL(const char* url_name);
   static bool IsDartUriLibURL(const char* url_name);
   static bool IsDartUtfLibURL(const char* url_name);
diff --git a/runtime/bin/file_win.cc b/runtime/bin/file_win.cc
index 9f6bae8c..73d195c 100644
--- a/runtime/bin/file_win.cc
+++ b/runtime/bin/file_win.cc
@@ -209,7 +209,7 @@
       static_cast<wchar_t*>(malloc(required_size * sizeof(wchar_t)));
   int written = GetFullPathNameW(system_name, required_size, path, NULL);
   free(const_cast<wchar_t*>(system_name));
-  ASSERT(written == (required_size - 1));
+  ASSERT(written <= (required_size - 1));
   char* result = StringUtils::WideToUtf8(path);
   free(path);
   return result;
diff --git a/runtime/embedders/openglui/common/canvas_context.cc b/runtime/embedders/openglui/common/canvas_context.cc
index ab6420e..3750344 100644
--- a/runtime/embedders/openglui/common/canvas_context.cc
+++ b/runtime/embedders/openglui/common/canvas_context.cc
@@ -9,12 +9,30 @@
 
 #include "embedders/openglui/common/support.h"
 
-CanvasContext::CanvasContext(int16_t widthp, int16_t heightp)
+// TODO(gram): this should be dynamic.
+#define MAX_CONTEXTS 16
+CanvasContext* contexts[MAX_CONTEXTS] = { 0 };
+
+CanvasContext* Context2D(int handle) {
+  if (handle < 0 || handle >= MAX_CONTEXTS) {
+    return NULL;
+  }
+  return contexts[handle];
+}
+
+CanvasContext::CanvasContext(int handle, int16_t widthp, int16_t heightp)
   : canvas_(NULL),
     width_(widthp),
     height_(heightp),
     imageSmoothingEnabled_(true),
     state_(NULL) {
+  if (handle == 0) {
+    canvas_ = graphics->CreateDisplayCanvas();
+  } else {
+    canvas_ = graphics->CreateBitmapCanvas(widthp, heightp);
+  }
+  state_ = new CanvasState(canvas_);
+  contexts[handle] = this;
 }
 
 CanvasContext::~CanvasContext() {
@@ -22,38 +40,41 @@
   delete canvas_;
 }
 
-void CanvasContext::Create() {
-  canvas_ = graphics->CreateCanvas();
-  state_ = new CanvasState(canvas_);
-}
-
 void CanvasContext::DrawImage(const char* src_url,
                               int sx, int sy,
                               bool has_src_dimensions, int sw, int sh,
                               int dx, int dy,
                               bool has_dst_dimensions, int dw, int dh) {
   SkBitmap bm;
-  // TODO(gram): We need a way to remap URLs to local file names.
-  // For now I am just using the characters after the last '/'.
-  // Note also that if we want to support URLs and network fetches,
-  // then we introduce more complexity; this can't just be an URL.
-  int pos = strlen(src_url);
-  while (--pos >= 0 && src_url[pos] != '/');
-  const char *path = src_url + pos + 1;
-  if (!SkImageDecoder::DecodeFile(path, &bm)) {
-    LOGI("Image decode of %s failed", path);
+  if (strncmp(src_url, "context2d://", 12) == 0) {
+    int handle = atoi(src_url + 12);
+    CanvasContext* otherContext = Context2D(handle);
+    SkDevice* device = otherContext->canvas_->getDevice();
+    bm = device->accessBitmap(false);
   } else {
-    LOGI("Decode image: width=%d,height=%d", bm.width(), bm.height());
-    if (!has_src_dimensions) {
-      sw = bm.width();
-      sh = bm.height();
+    // TODO(gram): We need a way to remap URLs to local file names.
+    // For now I am just using the characters after the last '/'.
+    // Note also that if we want to support URLs and network fetches,
+    // then we introduce more complexity; this can't just be an URL.
+    int pos = strlen(src_url);
+    while (--pos >= 0 && src_url[pos] != '/');
+    const char *path = src_url + pos + 1;
+    if (!SkImageDecoder::DecodeFile(path, &bm)) {
+      LOGI("Image decode of %s failed", path);
+      return;
+    } else {
+      LOGI("Decode image: width=%d,height=%d", bm.width(), bm.height());
     }
-    if (!has_dst_dimensions) {
-      dw = bm.width();
-      dh = bm.height();
-    }
-    state_->DrawImage(bm, sx, sy, sw, sh, dx, dy, dw, dh);
   }
+  if (!has_src_dimensions) {
+    sw = bm.width();
+    sh = bm.height();
+  }
+  if (!has_dst_dimensions) {
+    dw = bm.width();
+    dh = bm.height();
+  }
+  state_->DrawImage(bm, sx, sy, sw, sh, dx, dy, dw, dh);
 }
 
 void CanvasContext::ClearRect(float left, float top,
diff --git a/runtime/embedders/openglui/common/canvas_context.h b/runtime/embedders/openglui/common/canvas_context.h
index 665542f..72b3852 100644
--- a/runtime/embedders/openglui/common/canvas_context.h
+++ b/runtime/embedders/openglui/common/canvas_context.h
@@ -43,7 +43,7 @@
   }
 
  public:
-  CanvasContext(int16_t width, int16_t height);
+  CanvasContext(int handle, int16_t width, int16_t height);
   virtual ~CanvasContext();
 
   inline void setGlobalAlpha(float alpha) {
@@ -292,11 +292,10 @@
 
   virtual void Flush() {
     canvas_->flush();
-    graphics->Flush();
   }
-
-  void Create();
 };
 
+CanvasContext* Context2D(int handle);
+
 #endif  // EMBEDDERS_OPENGLUI_COMMON_CANVAS_CONTEXT_H_
 
diff --git a/runtime/embedders/openglui/common/canvas_state.cc b/runtime/embedders/openglui/common/canvas_state.cc
index 696e5d3..beef8b5 100644
--- a/runtime/embedders/openglui/common/canvas_state.cc
+++ b/runtime/embedders/openglui/common/canvas_state.cc
@@ -140,36 +140,35 @@
 
 void CanvasState::setGlobalCompositeOperation(const char* op) {
   SkXfermode::Mode mode;
-  if (strcmp(op, "source-atop") == 0) {
-    mode = SkXfermode::kSrcATop_Mode;
-  } else if (strcmp(op, "source-in") == 0) {
-    mode = SkXfermode::kSrcIn_Mode;
-  } else if (strcmp(op, "source-out") == 0) {
-    mode = SkXfermode::kSrcOut_Mode;
-  } else if (strcmp(op, "source-over") == 0) {
-    mode = SkXfermode::kSrcOver_Mode;  // Default.
-  } else if (strcmp(op, "destination-atop") == 0) {
-    mode = SkXfermode::kDstATop_Mode;
-  } else if (strcmp(op, "destination-in") == 0) {
-    mode = SkXfermode::kDstIn_Mode;
-  } else if (strcmp(op, "destination-out") == 0) {
-    mode = SkXfermode::kDstOut_Mode;
-  } else if (strcmp(op, "destination-over") == 0) {
-    mode = SkXfermode::kDstOver_Mode;
-  } else if (strcmp(op, "lighter") == 0) {
-    mode = SkXfermode::kLighten_Mode;
-  } else if (strcmp(op, "darker") == 0) {
-    mode = SkXfermode::kDarken_Mode;
-  } else if (strcmp(op, "xor") == 0) {
-    mode = SkXfermode::kXor_Mode;
-  } else if (strcmp(op, "copy") == 0) {
-    mode = SkXfermode::kSrc_Mode;
+  static const struct CompositOpToXfermodeMode {
+    const char* mCompositOp;
+    uint8_t m_xfermodeMode;
+  } gMapCompositOpsToXfermodeModes[] = {
+     { "clear",            SkXfermode::kClear_Mode },
+     { "copy",             SkXfermode::kSrc_Mode },
+     { "source-over",      SkXfermode::kSrcOver_Mode },
+     { "source-in",        SkXfermode::kSrcIn_Mode },
+     { "source-out",       SkXfermode::kSrcOut_Mode },
+     { "source-atop",      SkXfermode::kSrcATop_Mode },
+     { "destination-over", SkXfermode::kDstOver_Mode },
+     { "destination-in",   SkXfermode::kDstIn_Mode },
+     { "destination-out",  SkXfermode::kDstOut_Mode },
+     { "destination-atop", SkXfermode::kDstATop_Mode },
+     { "xor",              SkXfermode::kXor_Mode },
+     { "darker",           SkXfermode::kDarken_Mode },
+     { "lighter",          SkXfermode::kPlus_Mode }
+  };
+  for (unsigned i = 0;
+       i < SK_ARRAY_COUNT(gMapCompositOpsToXfermodeModes);
+       i++) {
+    if (strcmp(op, gMapCompositOpsToXfermodeModes[i].mCompositOp) == 0) {
+      mode = (SkXfermode::Mode)gMapCompositOpsToXfermodeModes[i].m_xfermodeMode;
+      paint_.setXfermodeMode(mode);
+      return;
+    }
   }
-  SkXfermode* m = SkXfermode::Create(mode);
-  // It seems we don't need unref() here. Including it causes
-  // a crash. Maybe Skia has a preallocated long-lived set of
-  // instances.
-  paint_.setXfermode(m);
+  LOGE("Unknown CompositeOperator %s\n", op);
+  paint_.setXfermodeMode(SkXfermode::kSrcOver_Mode);  // fall-back
 }
 
 void CanvasState::Arc(float x, float y, float radius,
@@ -215,10 +214,11 @@
 // See http://www.w3.org/TR/CSS21/syndata.html#color-units.
 // There is also another format: hsl(240,100%,100%) (and hsla)
 // TODO(gram): We probably eventually want to use a table rather
-// than a big if statement.
+// than a big if statement; see setGlobalCompositeOperation for
+// an example.
 ColorRGBA CanvasState::GetColor(const char* color) {
   if (color[0] == '#') {
-    int r, g, b;
+    int r = 0, g = 0, b = 0;
     if (strlen(color) == 7) {
       r = hexDigit(color[1]) * 16 + hexDigit(color[2]);
       g = hexDigit(color[3]) * 16 + hexDigit(color[4]);
diff --git a/runtime/embedders/openglui/common/extension.cc b/runtime/embedders/openglui/common/extension.cc
index 40b92f4..d0c24b3 100644
--- a/runtime/embedders/openglui/common/extension.cc
+++ b/runtime/embedders/openglui/common/extension.cc
@@ -923,19 +923,8 @@
 
 // 2D Canvas.
 
-// TODO(gram): this should be dynamic.
-#define MAX_CONTEXTS 16
-CanvasContext* contexts[MAX_CONTEXTS] = { 0 };
-
 CanvasContext* display_context;
 
-CanvasContext* Context2D(int handle) {
-  if (handle < 0 || handle >= MAX_CONTEXTS) {
-    return NULL;
-  }
-  return contexts[handle];
-}
-
 void C2DCreateNativeContext(Dart_NativeArguments arguments) {
   LOGI("In C2DCreateNativeContext");
   Dart_EnterScope();
@@ -944,14 +933,10 @@
   int width = GetArgAsInt(arguments, 1);
   int height = GetArgAsInt(arguments, 2);
 
-  // ASSERT(handle >= 0 && handle < MAX_CONTEXTS &&
-  //          contexts[handle] == NULL)
-  CanvasContext* rtn = new CanvasContext(width, height);
-  rtn->Create();
+  CanvasContext* rtn = new CanvasContext(handle, width, height);
   if (display_context == NULL) {
     display_context = rtn;
   }
-  contexts[handle] = rtn;
   Dart_ExitScope();
   LOGI("Out C2DCreateNativeContext");
 }
diff --git a/runtime/embedders/openglui/common/gl.dart b/runtime/embedders/openglui/common/gl.dart
index 95c1d2b..071f128 100644
--- a/runtime/embedders/openglui/common/gl.dart
+++ b/runtime/embedders/openglui/common/gl.dart
@@ -14,6 +14,11 @@
   CanvasRenderingContext2D _context2d;
   WebGLRenderingContext _context3d;
 
+  // For use with drawImage, we want to support a src property
+  // like ImageElement, which maps to the context handle in native
+  // code.
+  get src => "context2d://${_context2d.handle}";
+
   CanvasElement({int width, int height}) {
     _width = (width == null) ? getDeviceScreenWidth() : width;
     _height = (height == null) ? getDeviceScreenHeight() : height;
@@ -396,6 +401,7 @@
   // handle 0 being the physical display.
   static int _next_handle = 0;
   int _handle = 0;
+  get handle => _handle;
 
   int _width, _height;
   set width(int w) { _width = C2DSetWidth(_handle, w); }
@@ -410,7 +416,7 @@
   }
 
   double _alpha = 1.0;
-  set globalAlpha(numa) {
+  set globalAlpha(num a) {
     _alpha = C2DSetGlobalAlpha(_handle, a.toDouble());
   }
   get globalAlpha => _alpha;
@@ -574,18 +580,18 @@
     throw new Exception('Unimplemented createRadialGradient');
   }
 
-  void drawImage(ImageElement image, num x1, num y1,
+  void drawImage(element, num x1, num y1,
                 [num w1, num h1, num x2, num y2, num w2, num h2]) {
-    var w = (image.width == null) ? 0 : image.width;
-    var h = (image.height == null) ?  0 : image.height;
-    if (!?w1) { // drawImage(image, dx, dy)
-      C2DDrawImage(_handle, image.src, 0, 0, false, w, h,
+    var w = (element.width == null) ? 0 : element.width;
+    var h = (element.height == null) ?  0 : element.height;
+    if (!?w1) { // drawImage(element, dx, dy)
+      C2DDrawImage(_handle, element.src, 0, 0, false, w, h,
                    x1.toInt(), y1.toInt(), false, 0, 0);
-    } else if (!?x2) {  // drawImage(image, dx, dy, dw, dh)
-      C2DDrawImage(_handle, image.src, 0, 0, false, w, h,
+    } else if (!?x2) {  // drawImage(element, dx, dy, dw, dh)
+      C2DDrawImage(_handle, element.src, 0, 0, false, w, h,
                    x1.toInt(), y1.toInt(), true, w1.toInt(), h1.toInt());
     } else {  // drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)
-      C2DDrawImage(_handle, image.src, 
+      C2DDrawImage(_handle, element.src, 
                    x1.toInt(), y1.toInt(), true, w1.toInt(), h1.toInt(),
                    x2.toInt(), y2.toInt(), true, w2.toInt(), h2.toInt());
     }
diff --git a/runtime/embedders/openglui/common/graphics_handler.cc b/runtime/embedders/openglui/common/graphics_handler.cc
index 05615f0..c68da4b 100644
--- a/runtime/embedders/openglui/common/graphics_handler.cc
+++ b/runtime/embedders/openglui/common/graphics_handler.cc
@@ -35,8 +35,7 @@
   GrGLInterface* fGL = const_cast<GrGLInterface*>(GrGLCreateNativeInterface());
   LOGI("Created native interface %s\n", fGL ? "succeeded" : "failed");
   fGL->fGetString = myGLGetString;
-  grcontext = GrContext::Create(kOpenGL_Shaders_GrEngine,
-                        (GrPlatform3DContext)fGL);
+  grcontext = GrContext::Create(kOpenGL_GrBackend, (GrBackendContext)fGL);
   LOGI("Created GrContext %s\n", grcontext ? "succeeded" : "failed");
   return 0;
 }
@@ -45,18 +44,34 @@
   SkGraphics::Term();
 }
 
-SkCanvas* GraphicsHandler::CreateCanvas() {
-  GrPlatformRenderTargetDesc desc;
+SkCanvas* GraphicsHandler::CreateDisplayCanvas() {
+  GrBackendRenderTargetDesc desc;
   desc.fWidth = width_;
   desc.fHeight = height_;
-  desc.fConfig = kSkia8888_PM_GrPixelConfig;
+  desc.fConfig = kSkia8888_GrPixelConfig;
+  desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
   glGetIntegerv(GL_SAMPLES, &desc.fSampleCnt);
   glGetIntegerv(GL_STENCIL_BITS, &desc.fStencilBits);
+  LOGI("Creating %dx%d display canvas, samples %d, stencil bits %d",
+    width_, height_, desc.fSampleCnt, desc.fStencilBits);
   GrGLint buffer;
   glGetIntegerv(GL_FRAMEBUFFER_BINDING, &buffer);
   desc.fRenderTargetHandle = buffer;
-  GrRenderTarget* fGrRenderTarget = grcontext->createPlatformRenderTarget(desc);
-  return new SkCanvas(new SkGpuDevice(grcontext, fGrRenderTarget));
+  GrRenderTarget* fGrRenderTarget = grcontext->wrapBackendRenderTarget(desc);
+  SkGpuDevice* device = new SkGpuDevice(grcontext, fGrRenderTarget);
+//  fGrRenderTarget->unref(); // TODO(gram):  determine if we need this.
+  SkCanvas* canvas = new SkCanvas(device);
+  device->unref();
+  return canvas;
+}
+
+SkCanvas* GraphicsHandler::CreateBitmapCanvas(int width, int height) {
+  LOGI("Creating %dx%d bitmap canvas", width, height);
+  SkDevice* rasterDevice =
+      new SkDevice(SkBitmap::kARGB_8888_Config, width, height);
+  SkCanvas* canvas = new SkCanvas(rasterDevice);
+  rasterDevice->unref();
+  return canvas;
 }
 
 void GraphicsHandler::SetViewport(int left, int top, int width, int height) {
diff --git a/runtime/embedders/openglui/common/graphics_handler.h b/runtime/embedders/openglui/common/graphics_handler.h
index ba82adc..25fae53 100644
--- a/runtime/embedders/openglui/common/graphics_handler.h
+++ b/runtime/embedders/openglui/common/graphics_handler.h
@@ -36,12 +36,8 @@
 
     void SetViewport(int left, int top, int width, int height);
 
-    SkCanvas* CreateCanvas();
-
-    void Flush() {
-      // TODO(gram): see if we really need this.
-      grcontext->flush();
-    }
+    SkCanvas* CreateDisplayCanvas();
+    SkCanvas* CreateBitmapCanvas(int width, int height);
 
     virtual ~GraphicsHandler() {
     }
diff --git a/runtime/embedders/openglui/openglui_embedder.gypi b/runtime/embedders/openglui/openglui_embedder.gypi
index 01bf521..ab66125 100644
--- a/runtime/embedders/openglui/openglui_embedder.gypi
+++ b/runtime/embedders/openglui/openglui_embedder.gypi
@@ -141,8 +141,14 @@
                   'build_skia.sh'
                 ],
                 'outputs': [
-                  '../../../third_party/skia/trunk/out/config/android-x86/Debug/libskia_core.a'
+                  'dummy' # To force re-execution every time.
                 ],
+                # For now we drive the build from a shell
+                # script, to get us going. Eventually we will
+                # want to either fork Skia or incorporate its 
+                # gclient settings into ours, and include its 
+                # gyp files within ours, so that it gets built
+                # as part of our tree.
                 'action': [
                   'embedders/openglui/build_skia.sh',
                   '--android',
@@ -267,7 +273,7 @@
                   'build_skia.sh'
                 ],
                 'outputs': [
-                  '../../../third_party/skia/trunk/out/Debug/libskia_core.a'
+                  'dummy' # To force re-execution every time.
                 ],
                 'action': [
                   'embedders/openglui/build_skia.sh',
diff --git a/runtime/lib/growable_array.dart b/runtime/lib/growable_array.dart
index bb87177..5bd8987 100644
--- a/runtime/lib/growable_array.dart
+++ b/runtime/lib/growable_array.dart
@@ -99,14 +99,14 @@
   }
 
   factory _GrowableObjectArray(int length) {
-    var data = new _ObjectArray<T>((length == 0) ? 4 : length);
+    var data = new _ObjectArray((length == 0) ? 4 : length);
     var result = new _GrowableObjectArray<T>.withData(data);
     result._setLength(length);
     return result;
   }
 
   factory _GrowableObjectArray.withCapacity(int capacity) {
-    var data = new _ObjectArray<T>((capacity == 0)? 4 : capacity);
+    var data = new _ObjectArray((capacity == 0)? 4 : capacity);
     return new _GrowableObjectArray<T>.withData(data);
   }
 
@@ -116,7 +116,7 @@
     return result;
   }
 
-  factory _GrowableObjectArray.withData(_ObjectArray<T> data)
+  factory _GrowableObjectArray.withData(_ObjectArray data)
     native "GrowableObjectArray_allocate";
 
   int get length native "GrowableObjectArray_getLength";
@@ -136,7 +136,7 @@
 
   void _setLength(int new_length) native "GrowableObjectArray_setLength";
 
-  void _setData(_ObjectArray<T> array) native "GrowableObjectArray_setData";
+  void _setData(_ObjectArray array) native "GrowableObjectArray_setData";
 
   T operator [](int index) native "GrowableObjectArray_getIndexed";
 
@@ -203,7 +203,7 @@
   }
 
   void _grow(int new_length) {
-    var new_data = new _ObjectArray<T>(new_length);
+    var new_data = new _ObjectArray(new_length);
     for (int i = 0; i < length; i++) {
       new_data[i] = this[i];
     }
diff --git a/runtime/lib/string_base.dart b/runtime/lib/string_base.dart
index c4c62d8..2a777c2 100644
--- a/runtime/lib/string_base.dart
+++ b/runtime/lib/string_base.dart
@@ -437,9 +437,7 @@
     throw new UnimplementedError("String.codeUnits");
   }
 
-  Iterable<int> get runes {
-    throw new UnimplementedError("String.runes");
-  }
+  Runes get runes => new Runes(this);
 
   String toUpperCase() native "String_toUpperCase";
 
diff --git a/runtime/lib/timer_patch.dart b/runtime/lib/timer_patch.dart
index d27e4e8..1024915 100644
--- a/runtime/lib/timer_patch.dart
+++ b/runtime/lib/timer_patch.dart
@@ -2,23 +2,40 @@
 // 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.
 
+typedef void _TimerCallback0();
+typedef void _TimerCallback1(Timer timer);
+
 patch class Timer {
-  /* patch */ factory Timer(int milliseconds, void callback(Timer timer)) {
+  /* patch */ factory Timer(var duration, Function callback) {
+    // TODO(floitsch): remove these checks when we remove the deprecated
+    // millisecond argument and the 1-argument callback. Also remove
+    // the int-test below.
+    if (callback is! _TimerCallback0 && callback is! _TimerCallback1) {
+      throw new ArgumentError(callback);
+    }
+    int milliseconds = duration is int ? duration : duration.inMilliseconds;
+    if (milliseconds < 0) milliseconds = 0;
+    _TimerCallback1 oneArgumentCallback =
+        callback is _TimerCallback1 ? callback : (_) { callback(); };
     if (_TimerFactory._factory == null) {
       throw new UnsupportedError("Timer interface not supported.");
     }
-    return _TimerFactory._factory(milliseconds, callback, false);
+    return _TimerFactory._factory(milliseconds, oneArgumentCallback, false);
   }
 
   /**
    * Creates a new repeating timer. The [callback] is invoked every
    * [milliseconds] millisecond until cancelled.
    */
-  /* patch */ factory Timer.repeating(int milliseconds,
+  /* patch */ factory Timer.repeating(var duration,
                                       void callback(Timer timer)) {
     if (_TimerFactory._factory == null) {
       throw new UnsupportedError("Timer interface not supported.");
     }
+    // TODO(floitsch): remove this check when we remove the deprecated
+    // millisecond argument.
+    int milliseconds = duration is int ? duration : duration.inMilliseconds;
+    if (milliseconds < 0) milliseconds = 0;
     return _TimerFactory._factory(milliseconds, callback, true);
   }
 }
diff --git a/runtime/vm/ast.h b/runtime/vm/ast.h
index 4541daa..721de07 100644
--- a/runtime/vm/ast.h
+++ b/runtime/vm/ast.h
@@ -269,12 +269,18 @@
       : AstNode(token_pos),
         type_(type),
         elements_() {
-    ASSERT(type_.IsZoneHandle());
-    ASSERT(!type_.IsNull());
-    ASSERT(type_.IsFinalized());
-    // Type may be uninstantiated when creating a generic list literal.
-    ASSERT((type.arguments() == AbstractTypeArguments::null()) ||
-           ((AbstractTypeArguments::Handle(type.arguments()).Length() == 1)));
+    CheckFields();
+  }
+  ArrayNode(intptr_t token_pos,
+            const AbstractType& type,
+            const GrowableArray<AstNode*>& elements)
+      : AstNode(token_pos),
+        type_(type),
+        elements_(elements.length()) {
+    CheckFields();
+    for (intptr_t i = 0; i < elements.length(); i++) {
+      elements_.Add(elements[i]);
+    }
   }
 
   void VisitChildren(AstNodeVisitor* visitor) const;
@@ -295,6 +301,15 @@
   const AbstractType& type_;
   GrowableArray<AstNode*> elements_;
 
+  void CheckFields() {
+    ASSERT(type_.IsZoneHandle());
+    ASSERT(!type_.IsNull());
+    ASSERT(type_.IsFinalized());
+    // Type may be uninstantiated when creating a generic list literal.
+    ASSERT((type_.arguments() == AbstractTypeArguments::null()) ||
+           ((AbstractTypeArguments::Handle(type_.arguments()).Length() == 1)));
+  }
+
   DISALLOW_IMPLICIT_CONSTRUCTORS(ArrayNode);
 };
 
diff --git a/runtime/vm/benchmark_test.cc b/runtime/vm/benchmark_test.cc
index 648400b..9347fca 100644
--- a/runtime/vm/benchmark_test.cc
+++ b/runtime/vm/benchmark_test.cc
@@ -390,4 +390,68 @@
   benchmark->set_score(elapsed_time);
 }
 
+
+static uint8_t* malloc_allocator(
+    uint8_t* ptr, intptr_t old_size, intptr_t new_size) {
+  return reinterpret_cast<uint8_t*>(realloc(ptr, new_size));
+}
+
+
+BENCHMARK(CoreSnapshotSize) {
+  const char* kScriptChars =
+      "import 'dart:async';\n"
+      "import 'dart:core';\n"
+      "import 'dart:collection';\n"
+      "import 'dart:_collection-dev';\n"
+      "import 'dart:math';\n"
+      "import 'dart:isolate';\n"
+      "import 'dart:mirrors';\n"
+      "import 'dart:scalarlist';\n"
+      "\n";
+
+  // Start an Isolate, load a script and create a full snapshot.
+  uint8_t* buffer;
+  TestCase::LoadTestScript(kScriptChars, NULL);
+  Api::CheckIsolateState(Isolate::Current());
+
+  // Write snapshot with object content.
+  FullSnapshotWriter writer(&buffer, &malloc_allocator);
+  writer.WriteFullSnapshot();
+  const Snapshot* snapshot = Snapshot::SetupFromBuffer(buffer);
+  ASSERT(snapshot->kind() == Snapshot::kFull);
+  benchmark->set_score(snapshot->length());
+}
+
+
+BENCHMARK(StandaloneSnapshotSize) {
+  const char* kScriptChars =
+      "import 'dart:async';\n"
+      "import 'dart:core';\n"
+      "import 'dart:collection';\n"
+      "import 'dart:_collection-dev';\n"
+      "import 'dart:math';\n"
+      "import 'dart:isolate';\n"
+      "import 'dart:mirrors';\n"
+      "import 'dart:scalarlist';\n"
+      "import 'dart:uri';\n"
+      "import 'dart:utf';\n"
+      "import 'dart:json';\n"
+      "import 'dart:crypto';\n"
+      "import 'dart:builtin';\n"
+      "import 'dart:io';\n"
+      "\n";
+
+  // Start an Isolate, load a script and create a full snapshot.
+  uint8_t* buffer;
+  TestCase::LoadTestScript(kScriptChars, NULL);
+  Api::CheckIsolateState(Isolate::Current());
+
+  // Write snapshot with object content.
+  FullSnapshotWriter writer(&buffer, &malloc_allocator);
+  writer.WriteFullSnapshot();
+  const Snapshot* snapshot = Snapshot::SetupFromBuffer(buffer);
+  ASSERT(snapshot->kind() == Snapshot::kFull);
+  benchmark->set_score(snapshot->length());
+}
+
 }  // namespace dart
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index e293c89..1c90a7c 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -2338,7 +2338,8 @@
                                                    intptr_t* len) {
   Isolate* isolate = Isolate::Current();
   DARTSCOPE(isolate);
-  if (!RawObject::IsByteArrayClassId(Api::ClassId(array))) {
+  intptr_t class_id = Api::ClassId(array);
+  if (!RawObject::IsByteArrayClassId(class_id)) {
     RETURN_TYPE_ERROR(isolate, array, 'scalar list');
   }
   if (type == NULL) {
@@ -2350,10 +2351,68 @@
   if (len == NULL) {
     RETURN_NULL_ERROR(len);
   }
-  isolate->IncrementNoGCScopeDepth();
-  START_NO_CALLBACK_SCOPE(isolate);
-
-  UNIMPLEMENTED();
+  // Get the type of typed array.
+  switch (class_id) {
+    case kByteArrayCid :
+      *type = kByteArray;
+      break;
+    case kInt8ArrayCid :
+    case kExternalInt8ArrayCid :
+      *type = kInt8;
+      break;
+    case kUint8ArrayCid :
+    case kExternalUint8ArrayCid :
+      *type = kUint8;
+      break;
+    case kUint8ClampedArrayCid :
+    case kExternalUint8ClampedArrayCid :
+      *type = kUint8Clamped;
+      break;
+    case kInt16ArrayCid :
+    case kExternalInt16ArrayCid :
+      *type = kInt16;
+      break;
+    case kUint16ArrayCid :
+    case kExternalUint16ArrayCid :
+      *type = kUint16;
+      break;
+    case kInt32ArrayCid :
+    case kExternalInt32ArrayCid :
+      *type = kInt32;
+      break;
+    case kUint32ArrayCid :
+    case kExternalUint32ArrayCid :
+      *type = kUint32;
+      break;
+    case kInt64ArrayCid :
+    case kExternalInt64ArrayCid :
+      *type = kInt64;
+      break;
+    case kUint64ArrayCid :
+    case kExternalUint64ArrayCid :
+      *type = kUint64;
+      break;
+    case kFloat32ArrayCid :
+    case kExternalFloat32ArrayCid :
+      *type = kFloat32;
+      break;
+    case kFloat64ArrayCid :
+    case kExternalFloat64ArrayCid :
+      *type = kFloat64;
+      break;
+  }
+  const ByteArray& obj = Api::UnwrapByteArrayHandle(isolate, array);
+  ASSERT(!obj.IsNull());
+  *len = obj.Length();
+  // If it is an external typed array object just return the data field.
+  if (RawObject::IsExternalByteArrayClassId(class_id)) {
+    *data = reinterpret_cast<void*>(obj.ByteAddr(0));
+  } else {
+    // Regular typed array object, set up some GC and API callback guards.
+    isolate->IncrementNoGCScopeDepth();
+    START_NO_CALLBACK_SCOPE(isolate);
+    *data = reinterpret_cast<void*>(obj.ByteAddr(0));
+  }
   return Api::Success(isolate);
 }
 
@@ -2361,13 +2420,14 @@
 DART_EXPORT Dart_Handle Dart_ScalarListReleaseData(Dart_Handle array) {
   Isolate* isolate = Isolate::Current();
   DARTSCOPE(isolate);
-  if (!RawObject::IsByteArrayClassId(Api::ClassId(array))) {
+  intptr_t class_id = Api::ClassId(array);
+  if (!RawObject::IsByteArrayClassId(class_id)) {
     RETURN_TYPE_ERROR(isolate, array, 'scalar list');
   }
-
-  UNIMPLEMENTED();
-  isolate->DecrementNoGCScopeDepth();
-  END_NO_CALLBACK_SCOPE(isolate);
+  if (!RawObject::IsExternalByteArrayClassId(class_id)) {
+    isolate->DecrementNoGCScopeDepth();
+    END_NO_CALLBACK_SCOPE(isolate);
+  }
   return Api::Success(isolate);
 }
 
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index df08036..0dbbccd 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -916,6 +916,82 @@
 }
 
 
+static void TestDirectAccess(Dart_Handle lib,
+                             Dart_Handle array,
+                             Dart_Scalar_Type expected_type) {
+  // Invoke the dart function that sets initial values.
+  Dart_Handle dart_args[1];
+  dart_args[0] = array;
+  Dart_Invoke(lib, NewString("setMain"), 1, dart_args);
+
+  // Now Get a direct access to this typed array and check it's contents.
+  const int kLength = 10;
+  Dart_Handle result;
+  Dart_Scalar_Type type;
+  void* data;
+  intptr_t len;
+  result = Dart_ScalarListAcquireData(array, &type, &data, &len);
+  EXPECT_VALID(result);
+  EXPECT_EQ(expected_type, type);
+  EXPECT_EQ(kLength, len);
+  int8_t* dataP = reinterpret_cast<int8_t*>(data);
+  for (int i = 0; i < kLength; i++) {
+    EXPECT_EQ(i, dataP[i]);
+  }
+
+  // Now modify the values in the directly accessible array and then check
+  // it we see the changes back in dart.
+  for (int i = 0; i < kLength; i++) {
+    dataP[i] += 10;
+  }
+
+  // Release direct accesss to the typed array.
+  result = Dart_ScalarListReleaseData(array);
+  EXPECT_VALID(result);
+
+  // Invoke the dart function in order to check the modified values.
+  Dart_Invoke(lib, NewString("testMain"), 1, dart_args);
+}
+
+
+TEST_CASE(ScalarListDirectAccess1) {
+  const char* kScriptChars =
+      "import 'dart:scalarlist';\n"
+      "void setMain(var a) {"
+      "  for (var i = 0; i < 10; i++) {"
+      "    a[i] = i;"
+      "  }"
+      "}\n"
+      "void testMain(var list) {"
+      "  for (var i = 0; i < 10; i++) {"
+      "    Expect.equals((10 + i), list[i]);"
+      "  }\n"
+      "}\n"
+      "List main() {"
+      "  var a = new Int8List(10);"
+      "  return a;"
+      "}\n";
+  // Create a test library and Load up a test script in it.
+  Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
+
+  // Test with an regular typed array object.
+  Dart_Handle list_access_test_obj;
+  list_access_test_obj = Dart_Invoke(lib, NewString("main"), 0, NULL);
+  EXPECT_VALID(list_access_test_obj);
+  TestDirectAccess(lib, list_access_test_obj, kInt8);
+
+  // Test with an external typed array object.
+  uint8_t data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+  intptr_t data_length = ARRAY_SIZE(data);
+  Dart_Handle ext_list_access_test_obj;
+  ext_list_access_test_obj = Dart_NewExternalByteArray(data,
+                                                       data_length,
+                                                       NULL, NULL);
+  EXPECT_VALID(ext_list_access_test_obj);
+  TestDirectAccess(lib, ext_list_access_test_obj, kUint8);
+}
+
+
 static void ExternalByteArrayAccessTests(Dart_Handle obj,
                                          uint8_t data[],
                                          intptr_t data_length) {
diff --git a/runtime/vm/intermediate_language.cc b/runtime/vm/intermediate_language.cc
index 7b8e997..48edba3 100644
--- a/runtime/vm/intermediate_language.cc
+++ b/runtime/vm/intermediate_language.cc
@@ -1821,7 +1821,16 @@
     Definition* replacement = comparison()->Canonicalize(optimizer);
     if (replacement == comparison() || replacement == NULL) return this;
     ComparisonInstr* comp = replacement->AsComparison();
-    if (comp == NULL) return this;
+    if ((comp == NULL) || comp->CanDeoptimize()) return this;
+
+    // Check that comparison is not serving as a pending deoptimization target
+    // for conversions.
+    for (intptr_t i = 0; i < comp->InputCount(); i++) {
+      if (comp->RequiredInputRepresentation(i) !=
+          comp->InputAt(i)->definition()->representation()) {
+        return this;
+      }
+    }
 
     // Replace the comparison if the replacement is used at this branch,
     // and has exactly one use.
diff --git a/runtime/vm/intrinsifier.h b/runtime/vm/intrinsifier.h
index 3ac1014..5d4bbf4 100644
--- a/runtime/vm/intrinsifier.h
+++ b/runtime/vm/intrinsifier.h
@@ -64,13 +64,13 @@
   V(_ObjectArray, get:length, Array_getLength, 405297088)                      \
   V(_ObjectArray, [], Array_getIndexed, 71937385)                              \
   V(_ObjectArray, []=, Array_setIndexed, 255863719)                            \
-  V(_GrowableObjectArray, .withData, GArray_Allocate, 989879928)               \
+  V(_GrowableObjectArray, .withData, GArray_Allocate, 816132033)               \
   V(_GrowableObjectArray, get:length, GrowableArray_getLength, 725548050)      \
   V(_GrowableObjectArray, get:_capacity, GrowableArray_getCapacity, 725548050) \
   V(_GrowableObjectArray, [], GrowableArray_getIndexed, 581838973)             \
   V(_GrowableObjectArray, []=, GrowableArray_setIndexed, 1048007636)           \
   V(_GrowableObjectArray, _setLength, GrowableArray_setLength, 796709584)      \
-  V(_GrowableObjectArray, _setData, GrowableArray_setData, 477312179)          \
+  V(_GrowableObjectArray, _setData, GrowableArray_setData, 629110947)          \
   V(_GrowableObjectArray, add, GrowableArray_add, 1367698386)                  \
   V(_ImmutableArray, [], ImmutableArray_getIndexed, 486821199)                 \
   V(_ImmutableArray, get:length, ImmutableArray_getLength, 433698233)          \
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index c9fe421..b8f03b5 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -204,7 +204,7 @@
 
   static uword GetSpecifiedStackSize();
 
-  static const intptr_t kStackSizeBuffer = (16 * KB);
+  static const intptr_t kStackSizeBuffer = (4 * KB * kWordSize);
 
   enum {
     kApiInterrupt = 0x1,      // An interrupt from Dart_InterruptIsolate.
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index a3b4559..5ed01da 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -11868,9 +11868,6 @@
   const Array& new_contents =
       Array::Handle(Array::Grow(contents, new_capacity, space));
   StorePointer(&(raw_ptr()->data_), new_contents.raw());
-  ASSERT(AbstractTypeArguments::AreEqual(
-      AbstractTypeArguments::Handle(new_contents.GetTypeArguments()),
-      AbstractTypeArguments::Handle(raw_ptr()->type_arguments_)));
 }
 
 
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index b10e24c..7a4c908 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -4668,9 +4668,6 @@
   RawObject* RemoveLast() const;
 
   virtual RawAbstractTypeArguments* GetTypeArguments() const {
-    ASSERT(AbstractTypeArguments::AreEqual(
-        AbstractTypeArguments::Handle(Array::Handle(data()).GetTypeArguments()),
-        AbstractTypeArguments::Handle(raw_ptr()->type_arguments_)));
     return raw_ptr()->type_arguments_;
   }
   virtual void SetTypeArguments(const AbstractTypeArguments& value) const {
@@ -4750,6 +4747,7 @@
   virtual intptr_t ByteLength() const;
 
   virtual void* GetPeer() const { return NULL; }
+  virtual uint8_t* ByteAddr(intptr_t byte_offset) const;
 
   FinalizablePersistentHandle* AddFinalizer(
       void* peer, Dart_WeakPersistentHandleFinalizer callback) const;
@@ -4771,7 +4769,6 @@
                    intptr_t length);
 
  protected:
-  virtual uint8_t* ByteAddr(intptr_t byte_offset) const;
   virtual void SetPeer(void* peer) const { }
 
   template<typename HandleT, typename RawT>
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
index 34dee42..cee4d82 100644
--- a/runtime/vm/parser.cc
+++ b/runtime/vm/parser.cc
@@ -260,13 +260,11 @@
       is_top_level_(false),
       current_member_(NULL),
       allow_function_literals_(true),
-      current_function_(Function::Handle()),
+      parsed_function_(NULL),
       innermost_function_(Function::Handle()),
       current_class_(Class::Handle()),
       library_(library),
-      try_blocks_list_(NULL),
-      expression_temp_(NULL),
-      saved_current_context_(NULL) {
+      try_blocks_list_(NULL) {
   ASSERT(tokens_iterator_.IsValid());
   ASSERT(!library.IsNull());
 }
@@ -274,7 +272,7 @@
 
 // For parsing a function.
 Parser::Parser(const Script& script,
-               const Function& function,
+               ParsedFunction* parsed_function,
                intptr_t token_position)
     : script_(script),
       tokens_iterator_(TokenStream::Handle(script.tokens()), token_position),
@@ -283,15 +281,13 @@
       is_top_level_(false),
       current_member_(NULL),
       allow_function_literals_(true),
-      current_function_(function),
-      innermost_function_(Function::Handle(function.raw())),
-      current_class_(Class::Handle(current_function_.Owner())),
+      parsed_function_(parsed_function),
+      innermost_function_(Function::Handle(parsed_function->function().raw())),
+      current_class_(Class::Handle(parsed_function->function().Owner())),
       library_(Library::Handle(current_class_.library())),
-      try_blocks_list_(NULL),
-      expression_temp_(NULL),
-      saved_current_context_(NULL) {
+      try_blocks_list_(NULL) {
   ASSERT(tokens_iterator_.IsValid());
-  ASSERT(!function.IsNull());
+  ASSERT(!current_function().IsNull());
   if (FLAG_enable_type_checks) {
     EnsureExpressionTemp();
   }
@@ -306,7 +302,8 @@
 
 
 const Function& Parser::current_function() const {
-  return current_function_;
+  ASSERT(parsed_function() != NULL);
+  return parsed_function()->function();
 }
 
 
@@ -730,7 +727,7 @@
   ASSERT(parsed_function != NULL);
   const Function& func = parsed_function->function();
   const Script& script = Script::Handle(isolate, func.script());
-  Parser parser(script, func, func.token_pos());
+  Parser parser(script, parsed_function, func.token_pos());
   SequenceNode* node_sequence = NULL;
   Array& default_parameter_values = Array::ZoneHandle(isolate, Array::null());
   switch (func.kind()) {
@@ -765,15 +762,10 @@
     // Add implicit return node.
     node_sequence->Add(new ReturnNode(func.end_token_pos()));
   }
-  if (parser.expression_temp_ != NULL) {
-    parsed_function->set_expression_temp_var(parser.expression_temp_);
-  }
   if (parsed_function->has_expression_temp_var()) {
     node_sequence->scope()->AddVariable(parsed_function->expression_temp_var());
   }
-  if (parser.saved_current_context_ != NULL) {
-    parsed_function->set_saved_current_context_var(
-        parser.saved_current_context_);
+  if (parsed_function->has_saved_current_context_var()) {
     node_sequence->scope()->AddVariable(
         parsed_function->saved_current_context_var());
   }
@@ -6783,12 +6775,14 @@
 
 
 const LocalVariable* Parser::GetIncrementTempLocal() {
-  if (expression_temp_ == NULL) {
-    expression_temp_ = ParsedFunction::CreateExpressionTempVar(
+  if (!parsed_function()->has_expression_temp_var()) {
+    LocalVariable* temp = ParsedFunction::CreateExpressionTempVar(
         current_function().token_pos());
-    ASSERT(expression_temp_ != NULL);
+    ASSERT(temp != NULL);
+    parsed_function()->set_expression_temp_var(temp);
   }
-  return expression_temp_;
+  ASSERT(parsed_function()->has_expression_temp_var());
+  return parsed_function()->expression_temp_var();
 }
 
 
@@ -6800,13 +6794,13 @@
 
 void Parser::EnsureSavedCurrentContext() {
   // Used later by the flow_graph_builder to save current context.
-  if (saved_current_context_ == NULL) {
-    // Allocate a local variable to save the current context when we call into
-    // any closure function as the call will destroy the current context.
-    saved_current_context_ =
+  if (!parsed_function()->has_saved_current_context_var()) {
+    LocalVariable* temp =
         new LocalVariable(current_function().token_pos(),
                           Symbols::SavedCurrentContextVar(),
                           Type::ZoneHandle(Type::DynamicType()));
+    ASSERT(temp != NULL);
+    parsed_function()->set_saved_current_context_var(temp);
   }
 }
 
@@ -8223,7 +8217,10 @@
       imported_obj = LookupNameInImport(import, name);
       if (!imported_obj.IsNull()) {
         const Library& lib = Library::Handle(import.library());
-        if (!first_lib_url.IsNull()) {
+        // TODO(8474): Remove the "Expect" special casing.
+        if (!first_lib_url.IsNull()
+            && (strcmp(name.ToCString(), "Expect") != 0)
+            && (strcmp(name.ToCString(), "ExpectException") != 0)) {
           // Found duplicate definition.
           Error& ambiguous_ref_error = Error::Handle();
           if (first_lib_url.raw() == lib.url()) {
@@ -8597,8 +8594,7 @@
       Type::New(array_class, type_arguments, type_pos));
   type ^= ClassFinalizer::FinalizeType(
       current_class(), type, ClassFinalizer::kCanonicalize);
-  ArrayNode* list = new ArrayNode(TokenPos(), type);
-
+  GrowableArray<AstNode*> element_list;
   // Parse the list elements. Note: there may be an optional extra
   // comma after the last element.
   if (!is_empty_literal) {
@@ -8614,7 +8610,7 @@
                                      element_type,
                                      Symbols::ListLiteralElement());
       }
-      list->AddElement(element);
+      element_list.Add(element);
       if (CurrentToken() == Token::kCOMMA) {
         ConsumeToken();
       } else if (CurrentToken() != Token::kRBRACK) {
@@ -8628,12 +8624,12 @@
   if (is_const) {
     // Allocate and initialize the const list at compile time.
     Array& const_list =
-        Array::ZoneHandle(Array::New(list->length(), Heap::kOld));
+        Array::ZoneHandle(Array::New(element_list.length(), Heap::kOld));
     const_list.SetTypeArguments(
         AbstractTypeArguments::Handle(type_arguments.Canonicalize()));
     Error& malformed_error = Error::Handle();
-    for (int i = 0; i < list->length(); i++) {
-      AstNode* elem = list->ElementAt(i);
+    for (int i = 0; i < element_list.length(); i++) {
+      AstNode* elem = element_list[i];
       // Arguments have been evaluated to a literal value already.
       ASSERT(elem->IsLiteralNode());
       if (FLAG_enable_type_checks &&
@@ -8687,6 +8683,7 @@
     }
     factory_type_args = factory_type_args.Canonicalize();
     ArgumentListNode* factory_param = new ArgumentListNode(literal_pos);
+    ArrayNode* list = new ArrayNode(TokenPos(), type, element_list);
     factory_param->Add(list);
     return CreateConstructorCallNode(literal_pos,
                                      factory_type_args,
@@ -8714,7 +8711,7 @@
 }
 
 
-static void AddKeyValuePair(ArrayNode* pairs,
+static void AddKeyValuePair(GrowableArray<AstNode*>* pairs,
                             bool is_const,
                             AstNode* key,
                             AstNode* value) {
@@ -8724,18 +8721,18 @@
     const Instance& new_key = key->AsLiteralNode()->literal();
     for (int i = 0; i < pairs->length(); i += 2) {
       const Instance& key_i =
-          pairs->ElementAt(i)->AsLiteralNode()->literal();
+          (*pairs)[i]->AsLiteralNode()->literal();
       ASSERT(key_i.IsString());
       if (new_key.Equals(key_i)) {
         // Duplicate key found. The new value replaces the previously
         // defined value.
-        pairs->SetElementAt(i + 1, value);
+        (*pairs)[i + 1] = value;
         return;
       }
     }
   }
-  pairs->AddElement(key);
-  pairs->AddElement(value);
+  pairs->Add(key);
+  pairs->Add(value);
 }
 
 
@@ -8778,9 +8775,7 @@
 
   // The kv_pair array is temporary and of element type dynamic. It is passed
   // to the factory to initialize a properly typed map.
-  ArrayNode* kv_pairs = new ArrayNode(
-      TokenPos(), Type::ZoneHandle(Type::ArrayType()));
-
+  GrowableArray<AstNode*> kv_pairs_list;
   // Parse the map entries. Note: there may be an optional extra
   // comma after the last entry.
   while (CurrentToken() != Token::kRBRACE) {
@@ -8806,7 +8801,7 @@
                                  value_type,
                                  Symbols::ListLiteralElement());
     }
-    AddKeyValuePair(kv_pairs, is_const, key, value);
+    AddKeyValuePair(&kv_pairs_list, is_const, key, value);
 
     if (CurrentToken() == Token::kCOMMA) {
       ConsumeToken();
@@ -8814,7 +8809,7 @@
       ErrorMsg("comma or '}' expected");
     }
   }
-  ASSERT(kv_pairs->length() % 2 == 0);
+  ASSERT(kv_pairs_list.length() % 2 == 0);
   ExpectToken(Token::kRBRACE);
 
   if (is_const) {
@@ -8824,10 +8819,10 @@
 
     // First, create the canonicalized key-value pair array.
     Array& key_value_array =
-        Array::ZoneHandle(Array::New(kv_pairs->length(), Heap::kOld));
+        Array::ZoneHandle(Array::New(kv_pairs_list.length(), Heap::kOld));
     Error& malformed_error = Error::Handle();
-    for (int i = 0; i < kv_pairs->length(); i++) {
-      AstNode* arg = kv_pairs->ElementAt(i);
+    for (int i = 0; i < kv_pairs_list.length(); i++) {
+      AstNode* arg = kv_pairs_list[i];
       // Arguments have been evaluated to a literal value already.
       ASSERT(arg->IsLiteralNode());
       if (FLAG_enable_type_checks &&
@@ -8907,6 +8902,8 @@
     }
     factory_type_args = factory_type_args.Canonicalize();
     ArgumentListNode* factory_param = new ArgumentListNode(literal_pos);
+    ArrayNode* kv_pairs = new ArrayNode(
+        TokenPos(), Type::ZoneHandle(Type::ArrayType()), kv_pairs_list);
     factory_param->Add(kv_pairs);
     return CreateConstructorCallNode(literal_pos,
                                      factory_type_args,
@@ -9188,7 +9185,7 @@
 }
 
 
-String& Parser::Interpolate(ArrayNode* values) {
+String& Parser::Interpolate(const GrowableArray<AstNode*>& values) {
   const Class& cls = Class::Handle(LookupCoreClass(Symbols::StringBase()));
   ASSERT(!cls.IsNull());
   const Function& func =
@@ -9197,10 +9194,10 @@
   ASSERT(!func.IsNull());
 
   // Build the array of literal values to interpolate.
-  const Array& value_arr = Array::Handle(Array::New(values->length()));
-  for (int i = 0; i < values->length(); i++) {
-    ASSERT(values->ElementAt(i)->IsLiteralNode());
-    value_arr.SetAt(i, values->ElementAt(i)->AsLiteralNode()->literal());
+  const Array& value_arr = Array::Handle(Array::New(values.length()));
+  for (int i = 0; i < values.length(); i++) {
+    ASSERT(values[i]->IsLiteralNode());
+    value_arr.SetAt(i, values[i]->AsLiteralNode()->literal());
   }
 
   // Build argument array to pass to the interpolation function.
@@ -9240,10 +9237,9 @@
   }
   // String interpolation needed.
   bool is_compiletime_const = true;
-  ArrayNode* values = new ArrayNode(
-      TokenPos(), Type::ZoneHandle(Type::ArrayType()));
+  GrowableArray<AstNode*> values_list;
   while (CurrentToken() == Token::kSTRING) {
-    values->AddElement(new LiteralNode(TokenPos(), *CurrentLiteral()));
+    values_list.Add(new LiteralNode(TokenPos(), *CurrentLiteral()));
     ConsumeToken();
     while ((CurrentToken() == Token::kINTERPOL_VAR) ||
         (CurrentToken() == Token::kINTERPOL_START)) {
@@ -9275,14 +9271,15 @@
           is_compiletime_const = false;
         }
       }
-      values->AddElement(expr);
+      values_list.Add(expr);
     }
   }
   if (is_compiletime_const) {
-    primary = new LiteralNode(literal_start, Interpolate(values));
+    primary = new LiteralNode(literal_start, Interpolate(values_list));
   } else {
-    ArgumentListNode* interpolate_arg =
-        new ArgumentListNode(values->token_pos());
+    ArgumentListNode* interpolate_arg = new ArgumentListNode(TokenPos());
+    ArrayNode* values = new ArrayNode(
+        TokenPos(), Type::ZoneHandle(Type::ArrayType()), values_list);
     interpolate_arg->Add(values);
     primary = MakeStaticCall(Symbols::StringBase(),
                              PrivateCoreLibName(Symbols::Interpolate()),
diff --git a/runtime/vm/parser.h b/runtime/vm/parser.h
index 53b3bfc..6ca97c4 100644
--- a/runtime/vm/parser.h
+++ b/runtime/vm/parser.h
@@ -73,6 +73,9 @@
     ASSERT(saved_current_context_var != NULL);
     saved_current_context_var_ = saved_current_context_var;
   }
+  bool has_saved_current_context_var() const {
+    return saved_current_context_var_ != NULL;
+  }
 
   LocalVariable* saved_entry_context_var() const {
     return saved_entry_context_var_;
@@ -127,7 +130,7 @@
 class Parser : public ValueObject {
  public:
   Parser(const Script& script, const Library& library);
-  Parser(const Script& script, const Function& function, intptr_t token_pos);
+  Parser(const Script& script, ParsedFunction* function, intptr_t token_pos);
 
   // Parse the top level of a whole script file and register declared classes
   // in the given library.
@@ -199,6 +202,11 @@
   const Class& current_class() const;
   void set_current_class(const Class& value);
 
+  // ParsedFunction accessor.
+  ParsedFunction* parsed_function() const {
+    return parsed_function_;
+  }
+
   // Parsing a library or a regular source script.
   bool is_library_source() const {
     return (script_.kind() == RawScript::kScriptTag) ||
@@ -583,7 +591,7 @@
   AstNode* MakeStaticCall(const String& cls_name,
                           const String& func_name,
                           ArgumentListNode* arguments);
-  String& Interpolate(ArrayNode* values);
+  String& Interpolate(const GrowableArray<AstNode*>& values);
   AstNode* MakeAssertCall(intptr_t begin, intptr_t end);
   AstNode* ThrowTypeError(intptr_t type_pos, const AbstractType& type);
   AstNode* ThrowNoSuchMethodError(intptr_t call_pos,
@@ -624,7 +632,7 @@
   bool allow_function_literals_;
 
   // The function currently being compiled.
-  const Function& current_function_;
+  ParsedFunction* parsed_function_;
 
   // The function currently being parsed.
   Function& innermost_function_;
@@ -641,12 +649,6 @@
   // done using 'return', 'break' or 'continue' statements.
   TryBlocks* try_blocks_list_;
 
-  // Allocate temporary only once per function.
-  LocalVariable* expression_temp_;
-
-  // Allocate saved current context only if needed.
-  LocalVariable* saved_current_context_;
-
   DISALLOW_COPY_AND_ASSIGN(Parser);
 };
 
diff --git a/runtime/vm/unit_test.cc b/runtime/vm/unit_test.cc
index 6b0594f..b12fa1f 100644
--- a/runtime/vm/unit_test.cc
+++ b/runtime/vm/unit_test.cc
@@ -86,6 +86,8 @@
       return Builtin::LoadAndCheckLibrary(Builtin::kUtfLibrary);
     } else if (DartUtils::IsDartCryptoLibURL(url_chars)) {
       return Builtin::LoadAndCheckLibrary(Builtin::kCryptoLibrary);
+    } else if (DartUtils::IsDartBuiltinLibURL(url_chars)) {
+      return Builtin::LoadAndCheckLibrary(Builtin::kBuiltinLibrary);
     } else {
       return Dart_Error("Do not know how to load '%s'", url_chars);
     }
diff --git a/sdk/lib/_collection_dev/iterable.dart b/sdk/lib/_collection_dev/iterable.dart
index 89b9e6d..ac490e1 100644
--- a/sdk/lib/_collection_dev/iterable.dart
+++ b/sdk/lib/_collection_dev/iterable.dart
@@ -279,7 +279,9 @@
       throw new ConcurrentModificationError(_list);
     }
     for (int i = start + 1; i < end; i++) {
-      buffer.add(separator);
+      if (separator != null && separator != "") {
+        buffer.add(separator);
+      }
       buffer.add("${_f(_list[i])}");
       if (_list.length != length) {
         throw new ConcurrentModificationError(_list);
@@ -681,3 +683,8 @@
   bool moveNext() => false;
   E get current => null;
 }
+
+/** An [Iterator] that can move in both directions. */
+abstract class BiDirectionalIterator<T> implements Iterator<T> {
+  bool movePrevious();
+}
diff --git a/sdk/lib/_internal/compiler/implementation/compiler.dart b/sdk/lib/_internal/compiler/implementation/compiler.dart
index 4b1fc3b..71c3860 100644
--- a/sdk/lib/_internal/compiler/implementation/compiler.dart
+++ b/sdk/lib/_internal/compiler/implementation/compiler.dart
@@ -233,6 +233,7 @@
   ClassElement listClass;
   ClassElement typeClass;
   ClassElement mapClass;
+  ClassElement mapLiteralClass;
   ClassElement jsInvocationMirrorClass;
   /// Document class from dart:mirrors.
   ClassElement documentClass;
@@ -520,6 +521,8 @@
     listClass = lookupCoreClass('List');
     typeClass = lookupCoreClass('Type');
     mapClass = lookupCoreClass('Map');
+    // TODO: find a proper way to get to the implementation class.
+    mapLiteralClass = lookupCoreClass('LinkedHashMap');
     if (!missingCoreClasses.isEmpty) {
       internalErrorOnElement(coreLibrary,
           'dart:core library does not contain required classes: '
diff --git a/sdk/lib/_internal/compiler/implementation/dart2js.dart b/sdk/lib/_internal/compiler/implementation/dart2js.dart
index ac7d422..5fdcd2c 100644
--- a/sdk/lib/_internal/compiler/implementation/dart2js.dart
+++ b/sdk/lib/_internal/compiler/implementation/dart2js.dart
@@ -54,7 +54,7 @@
   for (OptionHandler handler in handlers) {
     patterns.add(handler.pattern);
   }
-  var pattern = new RegExp('^(${Strings.join(patterns, ")\$|(")})\$');
+  var pattern = new RegExp('^(${patterns.join(")\$|(")})\$');
   OUTER: for (String argument in argv) {
     Match match = pattern.firstMatch(argument);
     assert(match.groupCount == handlers.length);
@@ -115,7 +115,7 @@
   String getDepsOutput(Map<String, SourceFile> sourceFiles) {
     var filenames = new List.from(sourceFiles.keys);
     filenames.sort();
-    return Strings.join(filenames, "\n");
+    return filenames.join("\n");
   }
 
   setStrip(String argument) {
@@ -140,8 +140,7 @@
     if (categories.contains('all')) {
       categories = allowedCategoriesList;
     } else {
-      String allowedCategoriesString =
-          Strings.join(allowedCategoriesList, ', ');
+      String allowedCategoriesString = allowedCategoriesList.join(', ');
       for (String category in categories) {
         if (!allowedCategories.contains(category)) {
           fail('Error: unsupported library category "$category", '
@@ -149,11 +148,11 @@
         }
       }
     }
-    return passThrough('--categories=${Strings.join(categories, ",")}');
+    return passThrough('--categories=${categories.join(",")}');
   }
 
   handleShortOptions(String argument) {
-    var shortOptions = argument.substring(1).splitChars();
+    var shortOptions = argument.substring(1).split("");
     for (var shortOption in shortOptions) {
       switch (shortOption) {
         case 'v':
@@ -227,7 +226,7 @@
   }
   if (arguments.length > 1) {
     var extra = arguments.getRange(1, arguments.length - 1);
-    helpAndFail('Error: Extra arguments: ${Strings.join(extra, " ")}');
+    helpAndFail('Error: Extra arguments: ${extra.join(" ")}');
   }
 
   void handler(Uri uri, int begin, int end, String message,
diff --git a/sdk/lib/_internal/compiler/implementation/dart_types.dart b/sdk/lib/_internal/compiler/implementation/dart_types.dart
index 7c78bd5..1d9f217 100644
--- a/sdk/lib/_internal/compiler/implementation/dart_types.dart
+++ b/sdk/lib/_internal/compiler/implementation/dart_types.dart
@@ -801,6 +801,6 @@
       reasons.add(message.toString());
       return true;
     });
-    return Strings.join(reasons, ', ');
+    return reasons.join(', ');
   }
 }
diff --git a/sdk/lib/_internal/compiler/implementation/elements/elements.dart b/sdk/lib/_internal/compiler/implementation/elements/elements.dart
index 2798f95..0a06ad4 100644
--- a/sdk/lib/_internal/compiler/implementation/elements/elements.dart
+++ b/sdk/lib/_internal/compiler/implementation/elements/elements.dart
@@ -126,6 +126,50 @@
   toString() => id;
 }
 
+/**
+ * A declared element of a program.
+ *
+ * The declared elements of a program include classes, methods,
+ * fields, variables, parameters, etc.
+ *
+ * Sometimes it makes sense to construct "synthetic" elements that
+ * have not been declared anywhere in a program, for example, there
+ * are elements corresponding to "dynamic", "null", and unresolved
+ * references.
+ *
+ * Elements are distinct from types ([DartType]). For example, there
+ * is one declaration of the class List, but several related types,
+ * for example, List, List<int>, List<String>, etc.
+ *
+ * Elements are distinct from AST nodes ([Node]), and there normally is a
+ * one-to-one correspondence between an AST node and an element
+ * (except that not all kinds of AST nodes have an associated
+ * element).
+ *
+ * AST nodes represent precisely what is written in source code, for
+ * example, when a user writes "class MyClass {}", the corresponding
+ * AST node does not have a superclass. On the other hand, the
+ * corresponding element (once fully resolved) will record the
+ * information about the implicit superclass as defined by the
+ * language semantics.
+ *
+ * Generally, the contents of a method are represented as AST nodes
+ * without additional elements, but things like local functions, local
+ * variables, and labels have a corresponding element.
+ *
+ * We generally say that scanning, parsing, resolution, and type
+ * checking comprise the "front-end" of the compiler. The "back-end"
+ * includes things like SSA graph construction, optimizations, and
+ * code generation.
+ *
+ * The front-end data structures are designed to be reusable by
+ * several back-ends.  For example, we may want to support emitting
+ * minified Dart and JavaScript code in one go.  Also, we're planning
+ * on adding an incremental compilation server that should be able to
+ * reuse elements between compilations.  So to keep things simple, it
+ * is best if the backends avoid setting state directly in elements.
+ * It is better to keep such state in a table on the side.
+ */
 abstract class Element implements Spannable {
   SourceString get name;
   ElementKind get kind;
diff --git a/sdk/lib/_internal/compiler/implementation/elements/modelx.dart b/sdk/lib/_internal/compiler/implementation/elements/modelx.dart
index f007946..f53bfef 100644
--- a/sdk/lib/_internal/compiler/implementation/elements/modelx.dart
+++ b/sdk/lib/_internal/compiler/implementation/elements/modelx.dart
@@ -600,6 +600,11 @@
   void addImport(Element element, DiagnosticListener listener) {
     Element existing = importScope[element.name];
     if (existing != null) {
+      // TODO(8474): Remove the "Expect" special casing.
+      if (element.name == const SourceString("Expect") ||
+          element.name == const SourceString("ExpectException")) {
+        return;
+      }
       // TODO(johnniwinther): Provide access to the import tags from which
       // the elements came.
       importScope[element.name] = new AmbiguousElementX(
diff --git a/sdk/lib/_internal/compiler/implementation/enqueue.dart b/sdk/lib/_internal/compiler/implementation/enqueue.dart
index 4a4fea8..5ea86e3 100644
--- a/sdk/lib/_internal/compiler/implementation/enqueue.dart
+++ b/sdk/lib/_internal/compiler/implementation/enqueue.dart
@@ -127,9 +127,6 @@
     }
 
     String memberName = member.name.slowToString();
-    Link<Element> members = instanceMembersByName.putIfAbsent(
-        memberName, () => const Link<Element>());
-    instanceMembersByName[memberName] = members.prepend(member);
 
     if (member.kind == ElementKind.FUNCTION) {
       if (member.name == Compiler.NO_SUCH_METHOD) {
@@ -174,13 +171,21 @@
         // registered during codegen on the handleUnseenSelector path, and cause
         // the set of codegen elements to include unresolved elements.
         nativeEnqueuer.registerFieldStore(member);
+        return;
       }
       if (universe.hasInvokedSetter(member, compiler)) {
         nativeEnqueuer.registerFieldStore(member);
         // See comment after registerFieldLoad above.
         nativeEnqueuer.registerFieldLoad(member);
+        return;
       }
     }
+
+    // The element is not yet used. Add it to the list of instance
+    // members to still be processed. 
+    Link<Element> members = instanceMembersByName.putIfAbsent(
+        memberName, () => const Link<Element>());
+    instanceMembersByName[memberName] = members.prepend(member);
   }
 
   void enableNoSuchMethod(Element element) {}
@@ -349,26 +354,12 @@
     registerInvokedSetter(methodName, selector);
   }
 
-  void registerFieldGetter(SourceString getterName,
-                           LibraryElement library,
-                           DartType type) {
-    task.measure(() {
-      Selector getter = new Selector.getter(getterName, library);
-      registerNewSelector(getterName,
-                          new TypedSelector(type, getter),
-                          universe.fieldGetters);
-    });
+  void registerFieldGetter(Element element) {
+    universe.fieldGetters.add(element);
   }
 
-  void registerFieldSetter(SourceString setterName,
-                           LibraryElement library,
-                           DartType type) {
-    task.measure(() {
-      Selector setter = new Selector.setter(setterName, library);
-      registerNewSelector(setterName,
-                          new TypedSelector(type, setter),
-                          universe.fieldSetters);
-    });
+  void registerFieldSetter(Element element) {
+    universe.fieldSetters.add(element);
   }
 
   void registerIsCheck(DartType type) {
diff --git a/sdk/lib/_internal/compiler/implementation/js/printer.dart b/sdk/lib/_internal/compiler/implementation/js/printer.dart
index 504933d..8595174 100644
--- a/sdk/lib/_internal/compiler/implementation/js/printer.dart
+++ b/sdk/lib/_internal/compiler/implementation/js/printer.dart
@@ -48,7 +48,7 @@
   int get lastCharCode {
     if (lastAddedString == null) return 0;
     assert(lastAddedString.length != "");
-    return lastAddedString.charCodeAt(lastAddedString.length - 1);
+    return lastAddedString.codeUnitAt(lastAddedString.length - 1);
   }
 
   void out(String str) {
@@ -685,7 +685,7 @@
     // Ignore the leading and trailing string-delimiter.
     for (int i = 1; i < field.length - 1; i++) {
       // TODO(floitsch): allow more characters.
-      int charCode = field.charCodeAt(i);
+      int charCode = field.codeUnitAt(i);
       if (!(charCodes.$a <= charCode && charCode <= charCodes.$z ||
             charCodes.$A <= charCode && charCode <= charCodes.$Z ||
             charCode == charCodes.$$ ||
@@ -743,7 +743,7 @@
   }
 
   visitLiteralNumber(LiteralNumber node) {
-    int charCode = node.value.charCodeAt(0);
+    int charCode = node.value.codeUnitAt(0);
     if (charCode == charCodes.$MINUS && lastCharCode == charCodes.$MINUS) {
       out(" ");
     }
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart b/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart
index 2a681d8..e60f8c9 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart
@@ -864,10 +864,8 @@
     // make sure all [noSuchMethod] methods know they might take a
     // [JsInvocationMirror] as parameter.
     HTypeList types = new HTypeList(1);
-    types[0] = new HType.fromBoundedType(
-        compiler.jsInvocationMirrorClass.computeType(compiler),
-        compiler,
-        false);
+    types[0] = new HBoundedType.exact(
+        compiler.jsInvocationMirrorClass.computeType(compiler));
     argumentTypes.registerDynamicInvocation(types, new Selector.noSuchMethod());
   }
 
@@ -911,6 +909,7 @@
       // of the map.
       if (enqueuer.isResolutionQueue) {
         enqueuer.registerInstantiatedClass(compiler.listClass); 
+        enqueuer.registerInstantiatedClass(compiler.mapLiteralClass);
       }
     }
   }
@@ -1247,6 +1246,10 @@
     return compiler.findHelper(const SourceString('getRuntimeTypeInfo'));
   }
 
+  Element getGetRuntimeTypeArgument() {
+    return compiler.findHelper(const SourceString('getRuntimeTypeArgument'));
+  }
+
   /**
    * Remove [element] from the set of generated code, and put it back
    * into the worklist.
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart b/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
index f401235..b238df6 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
@@ -194,11 +194,13 @@
     }
     if (needsGetter) {
       var getterString = "return this." + field + ";";
-      prototype["get\$" + accessorName] = new Function(getterString);
+      prototype["${namer.getterPrefix}" + accessorName] =
+          new Function(getterString);
     }
     if (needsSetter) {
       var setterString = "this." + field + " = v;";
-      prototype["set\$" + accessorName] = new Function("v", setterString);
+      prototype["${namer.setterPrefix}" + accessorName] =
+          new Function("v", setterString);
     }
   }
   return field;
@@ -684,15 +686,24 @@
 
   bool instanceFieldNeedsGetter(Element member) {
     assert(member.isField());
+    if (fieldAccessNeverThrows(member)) return false;
     return compiler.codegenWorld.hasInvokedGetter(member, compiler);
   }
 
   bool instanceFieldNeedsSetter(Element member) {
     assert(member.isField());
+    if (fieldAccessNeverThrows(member)) return false;
     return (!member.modifiers.isFinalOrConst())
         && compiler.codegenWorld.hasInvokedSetter(member, compiler);
   }
 
+  // We never access a field in a closure (a captured variable) without knowing
+  // that it is there.  Therefore we don't need to use a getter (that will throw
+  // if the getter method is missing), but can always access the field directly.
+  static bool fieldAccessNeverThrows(Element element) {
+    return element is ClosureFieldElement;
+  }
+
   String compiledFieldName(Element member) {
     assert(member.isField());
     return member.hasFixedBackendName()
@@ -776,7 +787,7 @@
         includeBackendMembers: true,
         includeSuperMembers: false);
 
-    generateIsTestsOn(classElement, (Element other) {
+    void generateIsTest(Element other) {
       js.Expression code;
       if (compiler.objectClass == other) return;
       if (nativeEmitter.requiresNativeIsCheck(other)) {
@@ -785,7 +796,32 @@
         code = new js.LiteralBool(true);
       }
       builder.addProperty(namer.operatorIs(other), code);
-    });
+    }
+
+    void generateSubstitution(Element other, {bool emitNull: false}) {
+      RuntimeTypeInformation rti = backend.rti;
+      // TODO(karlklose): support typedefs with variables.
+      js.Expression expression;
+      bool needsNativeCheck = nativeEmitter.requiresNativeIsCheck(other);
+      if (other.kind == ElementKind.CLASS) {
+        String substitution = rti.getSupertypeSubstitution(classElement, other,
+            alwaysGenerateFunction: true);
+        if (substitution != null) {
+          expression = new js.LiteralExpression(substitution);
+        } else if (emitNull || needsNativeCheck) {
+          expression = new js.LiteralNull();
+        }
+      }
+      if (expression != null) {
+        if (needsNativeCheck) {
+          expression =
+              new js.Fun([], new js.Block([new js.Return(expression)]));
+        }
+        builder.addProperty(namer.substitutionName(other), expression);
+      }
+    }
+
+    generateIsTestsOn(classElement, generateIsTest, generateSubstitution);
 
     if (identical(classElement, compiler.objectClass)
         && compiler.enabledNoSuchMethod) {
@@ -818,23 +854,24 @@
   void emitRuntimeClassesAndTests(CodeBuffer buffer) {
     JavaScriptBackend backend = compiler.backend;
     RuntimeTypeInformation rti = backend.rti;
-
-    TypeChecks typeChecks = rti.computeRequiredChecks();
+    TypeChecks typeChecks = rti.getRequiredChecks();
 
     bool needsHolder(ClassElement cls) {
       return !neededClasses.contains(cls) || cls.isNative() ||
           rti.isJsNative(cls);
     }
 
+    /**
+     * Generates a holder object if it is needed.  A holder is a JavaScript
+     * object literal with a field [builtin$cls] that contains the name of the
+     * class as a string (just like object constructors do).  The is-checks for
+     * the class are are added to the holder object later.
+     */
     void maybeGenerateHolder(ClassElement cls) {
       if (!needsHolder(cls)) return;
-
       String holder = namer.isolateAccess(cls);
       String name = namer.getName(cls);
       buffer.add("$holder$_=$_{builtin\$cls:$_'$name'");
-      for (ClassElement check in typeChecks[cls]) {
-        buffer.add(',$_${namer.operatorIs(check)}:${_}true');
-      };
       buffer.add('}$N');
     }
 
@@ -844,16 +881,16 @@
       maybeGenerateHolder(cls);
     }
 
-    // Add checks to the constructors of instantiated classes.
+    // Add checks to the constructors of instantiated classes or to the created
+    // holder object.
     for (ClassElement cls in typeChecks) {
-      if (needsHolder(cls)) {
-        // We already emitted the is-checks in the object definition for this
-        // class.
-        continue;
-      }
       String holder = namer.isolateAccess(cls);
       for (ClassElement check in typeChecks[cls]) {
         buffer.add('$holder.${namer.operatorIs(check)}$_=${_}true$N');
+        String body = rti.getSupertypeSubstitution(cls, check);
+        if (body != null) {
+          buffer.add('$holder.${namer.substitutionName(check)}$_=${_}$body$N');
+        }
       };
     }
   }
@@ -1220,13 +1257,54 @@
 
   /**
    * Generate "is tests" for [cls]: itself, and the "is tests" for the
-   * classes it implements. We don't need to add the "is tests" of the
-   * super class because they will be inherited at runtime.
+   * classes it implements and type argument substitution functions for these
+   * tests.   We don't need to add the "is tests" of the super class because
+   * they will be inherited at runtime, but we may need to generate the
+   * substitutions, because they may have changed.
    */
   void generateIsTestsOn(ClassElement cls,
-                         void emitIsTest(Element element)) {
+                         void emitIsTest(Element element),
+                         void emitSubstitution(Element element, {emitNull})) {
     if (checkedClasses.contains(cls)) {
       emitIsTest(cls);
+      emitSubstitution(cls);
+    }
+
+    JavaScriptBackend jsBackend = compiler.backend;
+    RuntimeTypeInformation rti = jsBackend.rti;
+    ClassElement superclass = cls.superclass;
+
+    bool haveSameTypeVariables(ClassElement a, ClassElement b) {
+      if (a.isClosure()) return true;
+      return a.typeVariables == b.typeVariables;
+    }
+
+    if (superclass != null && superclass != compiler.objectClass &&
+        !haveSameTypeVariables(cls, superclass)) {
+      // We cannot inherit the generated substitutions, because the type
+      // variable layout for this class is different.  Instead we generate
+      // substitutions for all checks and make emitSubstitution a NOP for the
+      // rest of this function.
+      Set<ClassElement> emitted = new Set<ClassElement>();
+      // TODO(karlklose): move the computation of these checks to
+      // RuntimeTypeInformation.
+      if (compiler.world.needsRti(cls)) {
+        emitSubstitution(superclass, emitNull: true);
+        emitted.add(superclass);
+      }
+      for (DartType supertype in cls.allSupertypes) {
+        for (ClassElement check in checkedClasses) {
+          if (supertype.element == check && !emitted.contains(check)) {
+            // Generate substitution.  If no substitution is necessary, emit
+            // [:null:] to overwrite a (possibly) existing substitution from the
+            // super classes.
+            emitSubstitution(check, emitNull: true);
+            emitted.add(check);
+          }
+        }
+      }
+      void emitNothing(_, {emitNull}) {};
+      emitSubstitution = emitNothing;
     }
 
     Set<Element> generated = new Set<Element>();
@@ -1242,13 +1320,15 @@
       if (call != null) {
         generateInterfacesIsTests(compiler.functionClass,
                                   emitIsTest,
+                                  emitSubstitution,
                                   generated);
         getTypedefChecksOn(call.computeType(compiler)).forEach(emitIsTest);
       }
     }
 
     for (DartType interfaceType in cls.interfaces) {
-      generateInterfacesIsTests(interfaceType.element, emitIsTest, generated);
+      generateInterfacesIsTests(interfaceType.element, emitIsTest,
+                                emitSubstitution, generated);
     }
 
     // For native classes, we also have to run through their mixin
@@ -1257,7 +1337,8 @@
     visitNativeMixins(cls, (MixinApplicationElement mixin) {
       for (DartType interfaceType in mixin.interfaces) {
         ClassElement interfaceElement = interfaceType.element;
-        generateInterfacesIsTests(interfaceType.element, emitIsTest, generated);
+        generateInterfacesIsTests(interfaceType.element, emitIsTest,
+                                  emitSubstitution, generated);
       }
     });
   }
@@ -1267,11 +1348,13 @@
    */
   void generateInterfacesIsTests(ClassElement cls,
                                  void emitIsTest(ClassElement element),
+                                 void emitSubstitution(ClassElement element),
                                  Set<Element> alreadyGenerated) {
-    void tryEmitTest(ClassElement cls) {
-      if (!alreadyGenerated.contains(cls) && checkedClasses.contains(cls)) {
-        alreadyGenerated.add(cls);
-        emitIsTest(cls);
+    void tryEmitTest(ClassElement check) {
+      if (!alreadyGenerated.contains(check) && checkedClasses.contains(check)) {
+        alreadyGenerated.add(check);
+        emitIsTest(check);
+        emitSubstitution(check);
       }
     };
 
@@ -1280,14 +1363,16 @@
     for (DartType interfaceType in cls.interfaces) {
       Element element = interfaceType.element;
       tryEmitTest(element);
-      generateInterfacesIsTests(element, emitIsTest, alreadyGenerated);
+      generateInterfacesIsTests(element, emitIsTest, emitSubstitution,
+                                alreadyGenerated);
     }
 
     // We need to also emit "is checks" for the superclass and its supertypes.
     ClassElement superclass = cls.superclass;
     if (superclass != null) {
       tryEmitTest(superclass);
-      generateInterfacesIsTests(superclass, emitIsTest, alreadyGenerated);
+      generateInterfacesIsTests(superclass, emitIsTest, emitSubstitution,
+                                alreadyGenerated);
     }
   }
 
@@ -1467,7 +1552,7 @@
                                    List<String> fieldNames,
                                    ClassBuilder builder) {
     builder.addProperty('',
-        js.string("$superName;${Strings.join(fieldNames,',')}"));
+        js.string("$superName;${fieldNames.join(',')}"));
   }
 
   /**
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/minify_namer.dart b/sdk/lib/_internal/compiler/implementation/js_backend/minify_namer.dart
index 98344a1..547a3e4 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/minify_namer.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/minify_namer.dart
@@ -16,6 +16,9 @@
   String get isolatePropertiesName => 'p';
   bool get shouldMinify => true;
 
+  final String getterPrefix = 'g';
+  final String setterPrefix = 's';
+
   const ALPHABET_CHARACTERS = 52;  // a-zA-Z.
   const ALPHANUMERIC_CHARACTERS = 62;  // a-zA-Z0-9.
 
@@ -69,11 +72,17 @@
         'refY', 'RGBA', 'root', 'rows', 'save', 'seed', 'seek', 'self', 'send',
         'show', 'SINE', 'size', 'span', 'stat', 'step', 'stop', 'tags', 'text',
         'Text', 'time', 'type', 'view', 'warn', 'wrap', 'ZERO'];
+
     for (var name in reservedNativeProperties) {
       if (name.length < 2) {
         instanceNameMap[name] = name;
       }
       usedInstanceNames.add(name);
+      // Getter and setter names are autogenerated by prepending 'g' and 's' to
+      // field names.  Therefore there are some field names we don't want to
+      // use.  It is implicit in the next line that the banned prefix is
+      // only one character.
+      if (_hasBannedPrefix(name)) usedInstanceNames.add(name.substring(1));
     }
 
     // This list of popular instance variable names generated with:
@@ -98,7 +107,7 @@
         usedGlobalNames,
         const <String>[
             r'Object', r'$throw', r'$eq', r'S', r'ioore', r'UnsupportedError$',
-            r'length', r'$sub', r'getInterceptor$JSStringJSArray', r'$add',
+            r'length', r'$sub', r'getInterceptor$JSArrayJSString', r'$add',
             r'$gt', r'$ge', r'$lt', r'$le', r'add', r'getInterceptor$JSNumber',
             r'iterator', r'$index', r'iae', r'getInterceptor$JSArray',
             r'ArgumentError$', r'BoundClosure', r'StateError$',
@@ -152,7 +161,9 @@
           h2 ~/= ALPHANUMERIC_CHARACTERS;
         }
         final candidate = new String.fromCharCodes(codes);
-        if (!usedNames.contains(candidate) && !jsReserved.contains(candidate)) {
+        if (!usedNames.contains(candidate) &&
+            !jsReserved.contains(candidate) &&
+            !_hasBannedPrefix(candidate)) {
           return candidate;
         }
         // Try again with a slightly different hash.  After around 10 turns
@@ -168,13 +179,23 @@
     while (usedNames.contains("$startLetter$i")) {
       i++;
     }
+    // We don't need to check for banned prefix because the name is in the form
+    // xnnn, where nnn is a number.  There can be no getter or setter called
+    // gnnn since that would imply a numeric field name.
     return "$startLetter$i";
   }
 
+  /// Instance members starting with g and s are reserved for getters and
+  /// setters.
+  bool _hasBannedPrefix(String name) {
+    int code = name.codeUnitAt(0);
+    return code == $g || code == $s;
+  }
+
   int _calculateHash(String name) {
     int h = 0;
     for (int i = 0; i < name.length; i++) {
-      h += name.charCodeAt(i);
+      h += name.codeUnitAt(i);
       h &= 0xffffffff;
       h += h << 10;
       h &= 0xffffffff;
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/namer.dart b/sdk/lib/_internal/compiler/implementation/js_backend/namer.dart
index e5bb967..aca3e61 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/namer.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/namer.dart
@@ -174,6 +174,9 @@
 
   final String CURRENT_ISOLATE = r'$';
 
+  final String getterPrefix = r'get$';
+  final String setterPrefix = r'set$';
+
   /**
    * Map from top-level or static elements to their unique identifiers provided
    * by [getName].
@@ -344,10 +347,10 @@
   String invocationName(Selector selector) {
     if (selector.isGetter()) {
       String proposedName = privateName(selector.library, selector.name);
-      return 'get\$${getMappedInstanceName(proposedName)}';
+      return '$getterPrefix${getMappedInstanceName(proposedName)}';
     } else if (selector.isSetter()) {
       String proposedName = privateName(selector.library, selector.name);
-      return 'set\$${getMappedInstanceName(proposedName)}';
+      return '$setterPrefix${getMappedInstanceName(proposedName)}';
     } else {
       SourceString name = selector.name;
       if (selector.kind == SelectorKind.OPERATOR
@@ -411,26 +414,26 @@
     // therefore be derived from the instance field-name.
     LibraryElement library = element.getLibrary();
     String name = getMappedInstanceName(privateName(library, element.name));
-    return 'set\$$name';
+    return '$setterPrefix$name';
   }
 
   String setterNameFromAccessorName(String name) {
     // We dynamically create setters from the field-name. The setter name must
     // therefore be derived from the instance field-name.
-    return 'set\$$name';
+    return '$setterPrefix$name';
   }
 
   String publicGetterName(SourceString name) {
     // We dynamically create getters from the field-name. The getter name must
     // therefore be derived from the instance field-name.
     String fieldName = getMappedInstanceName(name.slowToString());
-    return 'get\$$fieldName';
+    return '$getterPrefix$fieldName';
   }
 
   String getterNameFromAccessorName(String name) {
     // We dynamically create getters from the field-name. The getter name must
     // therefore be derived from the instance field-name.
-    return 'get\$$name';
+    return '$getterPrefix$name';
   }
 
   String getterName(Element element) {
@@ -438,7 +441,7 @@
     // therefore be derived from the instance field-name.
     LibraryElement library = element.getLibrary();
     String name = getMappedInstanceName(privateName(library, element.name));
-    return 'get\$$name';
+    return '$getterPrefix$name';
   }
 
   String getMappedGlobalName(String proposedName) {
@@ -539,11 +542,13 @@
     }
     // Use the unminified names here to construct the interceptor names.  This
     // helps ensure that they don't all suddenly change names due to a name
-    // clash in the minifier, which would affect the diff size.
+    // clash in the minifier, which would affect the diff size.  Sort the names
+    // of the classes to ensure name is stable and predicatble for the suggested
+    // names.
     StringBuffer buffer = new StringBuffer('${element.name.slowToString()}\$');
-    for (ClassElement cls in classes) {
-      buffer.add(cls.name.slowToString());
-    }
+    List<String> names = classes.map((cls) => cls.name.slowToString()).toList();
+    names.sort();
+    names.forEach(buffer.add);
     return getMappedGlobalName(buffer.toString());
   }
 
@@ -647,7 +652,7 @@
 
   String getLazyInitializerName(Element element) {
     assert(Elements.isStaticOrTopLevelField(element));
-    return getMappedGlobalName("get\$${getName(element)}");
+    return getMappedGlobalName("$getterPrefix${getName(element)}");
   }
 
   String isolatePropertiesAccess(Element element) {
@@ -669,6 +674,8 @@
 
   String operatorIsPrefix() => r'$is';
 
+  String operatorAsPrefix() => r'$as';
+
   String operatorIs(Element element) {
     // TODO(erikcorry): Reduce from $isx to ix when we are minifying.
     return '${operatorIsPrefix()}${getName(element)}';
@@ -686,6 +693,10 @@
     return name;
   }
 
+  String substitutionName(Element element) {
+    return '${operatorAsPrefix()}${getName(element)}';
+  }
+
   String safeName(String name) => _safeName(name, jsReserved);
   String safeVariableName(String name) => _safeName(name, jsVariableReserved);
 
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart b/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart
index 9400f5f..0343457 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart
@@ -366,7 +366,7 @@
       walk(classElement);
 
       if (!subtags.isEmpty) {
-        expressions.add(js.string(Strings.join(subtags, '|')));
+        expressions.add(js.string(subtags.join('|')));
       }
       js.Expression expression;
       if (expressions.length == 1) {
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/runtime_types.dart b/sdk/lib/_internal/compiler/implementation/js_backend/runtime_types.dart
index a3a0339..b1cb9eb 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/runtime_types.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/runtime_types.dart
@@ -8,7 +8,7 @@
 abstract class TypeChecks {
   /// Get the set of checks required for class [element].
   Iterable<ClassElement> operator[](ClassElement element);
-  // Get the iterator for all classes that need type checks.
+  /// Get the iterator for all classes that need type checks.
   Iterator<ClassElement> get iterator;
 }
 
@@ -27,25 +27,27 @@
             element == compiler.numClass ||
             element == compiler.doubleClass ||
             element == compiler.stringClass ||
-            element == compiler.listClass ||
-            element == compiler.objectClass ||
-            element == compiler.dynamicClass);
+            element == compiler.listClass);
   }
 
-  TypeChecks computeRequiredChecks() {
-    Set<ClassElement> instantiatedArguments = new Set<ClassElement>();
-    for (DartType type in compiler.codegenWorld.instantiatedTypes) {
-      addAllInterfaceTypeArguments(type, instantiatedArguments);
-    }
+  TypeChecks cachedRequiredChecks;
 
-    Set<ClassElement> checkedArguments = new Set<ClassElement>();
-    for (DartType type in compiler.enqueuer.codegen.universe.isChecks) {
-      addAllInterfaceTypeArguments(type, checkedArguments);
-    }
+  TypeChecks getRequiredChecks() {
+    if (cachedRequiredChecks != null) return cachedRequiredChecks;
 
+    // Get all types used in type arguments of instantiated types.
+    Set<ClassElement> instantiatedArguments = getInstantiatedArguments();
+
+    // Collect all type arguments used in is-checks.
+    Set<ClassElement> checkedArguments = getCheckedArguments();
+
+    // Precompute the set of all seen type arguments for use in the emitter.
     allArguments = new Set<ClassElement>.from(instantiatedArguments)
         ..addAll(checkedArguments);
 
+    // Finally, run through the combination of instantiated and checked
+    // arguments and record all combination where the element of a checked
+    // argument is a superclass of the element of an instantiated type.
     TypeCheckMapping requiredChecks = new TypeCheckMapping();
     for (ClassElement element in instantiatedArguments) {
       if (element == compiler.dynamicClass) continue;
@@ -60,8 +62,48 @@
         }
       }
     }
+    return cachedRequiredChecks = requiredChecks;
+  }
 
-    return requiredChecks;
+  /**
+   * Collects all types used in type arguments of instantiated types.
+   *
+   * This includes type arguments used in supertype relations, because we may
+   * have a type check against this supertype that includes a check against
+   * the type arguments.
+   */
+  Set<ClassElement> getInstantiatedArguments() {
+    Set<ClassElement> instantiatedArguments = new Set<ClassElement>();
+    for (DartType type in instantiatedTypes) {
+      addAllInterfaceTypeArguments(type, instantiatedArguments);
+      ClassElement cls = type.element;
+      for (DartType type in cls.allSupertypes) {
+        addAllInterfaceTypeArguments(type, instantiatedArguments);
+      }
+    }
+    for (ClassElement cls in instantiatedArguments) {
+      for (DartType type in cls.allSupertypes) {
+        addAllInterfaceTypeArguments(type, instantiatedArguments);
+      }
+    }
+    return instantiatedArguments;
+  }
+
+  /// Collects all type arguments used in is-checks.
+  Set<ClassElement> getCheckedArguments() {
+    Set<ClassElement> checkedArguments = new Set<ClassElement>();
+    for (DartType type in isChecks) {
+      addAllInterfaceTypeArguments(type, checkedArguments);
+    }
+    return checkedArguments;
+  }
+
+  Iterable<DartType> get isChecks {
+    return compiler.enqueuer.resolution.universe.isChecks;
+  }
+
+  Iterable<DartType> get instantiatedTypes {
+    return compiler.codegenWorld.instantiatedTypes;
   }
 
   void addAllInterfaceTypeArguments(DartType type, Set<ClassElement> classes) {
@@ -69,7 +111,7 @@
     for (DartType argument in type.typeArguments) {
       forEachInterfaceType(argument, (InterfaceType t) {
         ClassElement cls = t.element;
-        if (cls != compiler.dynamicClass && cls != compiler.objectClass) {
+        if (cls != compiler.dynamicClass) {
           classes.add(cls);
         }
       });
@@ -106,31 +148,113 @@
     InterfaceType interface = type;
     Link<DartType> variables = interface.element.typeVariables;
     if (variables.isEmpty) return name;
-    List<String> arguments = [];
-    variables.forEach((_) => arguments.add('dynamic'));
-    return '$name<${Strings.join(arguments, ', ')}>';
+    String arguments = variables.map((_) => 'dynamic').join(', ');
+    return '$name<$arguments>';
+  }
+
+  // TODO(karlklose): maybe precompute this value and store it in typeChecks?
+  bool isTrivialSubstitution(ClassElement cls, ClassElement check) {
+    if (cls.isClosure()) {
+      // TODO(karlklose): handle closures.
+      return true;
+    }
+
+    // If there are no type variables or the type is the same, we do not need
+    // a substitution.
+    if (check.typeVariables.isEmpty || cls == check) {
+      return true;
+    }
+
+    InterfaceType originalType = cls.computeType(compiler);
+    InterfaceType type = originalType.asInstanceOf(check);
+    // [type] is not a subtype of [check]. we do not generate a check and do not
+    // need a substitution.
+    if (type == null) return true;
+
+    // Run through both lists of type variables and check if the type variables
+    // are identical at each position. If they are not, we need to calculate a
+    // substitution function.
+    Link<DartType> variables = cls.typeVariables;
+    Link<DartType> arguments = type.typeArguments;
+    while (!variables.isEmpty && !arguments.isEmpty) {
+      if (variables.head.element != arguments.head.element) {
+        return false;
+      }
+      variables = variables.tail;
+      arguments = arguments.tail;
+    }
+    return (variables.isEmpty == arguments.isEmpty);
+  }
+
+  // TODO(karlklose): rewrite to use js.Expressions.
+  /**
+   * Compute a JavaScript expression that describes the necessary substitution
+   * for type arguments in a subtype test.
+   *
+   * The result can be:
+   *  1) [:null:], if no substituted check is necessary, because the
+   *     type variables are the same or there are no type variables in the class
+   *     that is checked for.
+   *  2) A list expression describing the type arguments to be used in the
+   *     subtype check, if the type arguments to be used in the check do not
+   *     depend on the type arguments of the object.
+   *  3) A function mapping the type variables of the object to be checked to
+   *     a list expression.
+   */
+  String getSupertypeSubstitution(ClassElement cls, ClassElement check,
+                                  {alwaysGenerateFunction: false}) {
+    if (isTrivialSubstitution(cls, check)) return null;
+
+    // TODO(karlklose): maybe precompute this value and store it in typeChecks?
+    InterfaceType type = cls.computeType(compiler);
+    InterfaceType target = type.asInstanceOf(check);
+    String substitution = target.typeArguments
+        .map((type) => _getTypeRepresentation(type, (v) => v.toString()))
+        .join(', ');
+    substitution = '[$substitution]';
+    if (cls.typeVariables.isEmpty && !alwaysGenerateFunction) {
+      return substitution;
+    } else {
+      String parameters = cls.typeVariables.toList().join(', ');
+      return 'function ($parameters) { return $substitution; }';
+    }
   }
 
   String getTypeRepresentation(DartType type, void onVariable(variable)) {
+    // Create a type representation.  For type variables call the original
+    // callback for side effects and return a template placeholder.
+    return _getTypeRepresentation(type, (variable) {
+      onVariable(variable);
+      return '#';
+    });
+  }
+
+  // TODO(karlklose): rewrite to use js.Expressions.
+  String _getTypeRepresentation(DartType type, String onVariable(variable)) {
     StringBuffer builder = new StringBuffer();
     void build(DartType part) {
       if (part is TypeVariableType) {
-        builder.add('#');
-        onVariable(part);
+        builder.add(onVariable(part));
       } else {
         bool hasArguments = part is InterfaceType && !part.isRaw;
         Element element = part.element;
-        if (hasArguments) {
-          builder.add('[');
+        if (element == compiler.dynamicClass) {
+          builder.add('null');
+        } else {
+          String name = getJsName(element);
+          if (!hasArguments) {
+            builder.add(name);
+          } else {
+            builder.add('[');
+            builder.add(name);
+            InterfaceType interface = part;
+            for (DartType argument in interface.typeArguments) {
+              builder.add(', ');
+              build(argument);
+            }
+            builder.add(']');
+          }
         }
-        builder.add(getJsName(element));
-        if (!hasArguments) return;
-        InterfaceType interface = part;
-        for (DartType argument in interface.typeArguments) {
-          builder.add(', ');
-          build(argument);
-        }
-        builder.add(']');
       }
     }
     build(type);
diff --git a/sdk/lib/_internal/compiler/implementation/lib/async_patch.dart b/sdk/lib/_internal/compiler/implementation/lib/async_patch.dart
index 0fccce2..0e0a70d 100644
--- a/sdk/lib/_internal/compiler/implementation/lib/async_patch.dart
+++ b/sdk/lib/_internal/compiler/implementation/lib/async_patch.dart
@@ -6,16 +6,35 @@
 
 import 'dart:_isolate_helper' show TimerImpl;
 
+typedef void _TimerCallback0();
+typedef void _TimerCallback1(Timer timer);
+
 patch class Timer {
-  patch factory Timer(int milliseconds, void callback(Timer timer)) {
-    return new TimerImpl(milliseconds, callback);
+  patch factory Timer(var duration, var callback) {
+    // TODO(floitsch): remove these checks when we remove the deprecated
+    // millisecond argument and the 1-argument callback. Also remove the
+    // int-test below.
+    if (callback is! _TimerCallback0 && callback is! _TimerCallback1) {
+      throw new ArgumentError(callback);
+    }
+    int milliseconds = duration is int ? duration : duration.inMilliseconds;
+    if (milliseconds < 0) milliseconds = 0;
+    Timer timer;
+    _TimerCallback0 zeroArgumentCallback =
+        callback is _TimerCallback0 ? callback : () => callback(timer);
+    timer = new TimerImpl(milliseconds, zeroArgumentCallback);
+    return timer;
   }
 
   /**
    * Creates a new repeating timer. The [callback] is invoked every
    * [milliseconds] millisecond until cancelled.
    */
-  patch factory Timer.repeating(int milliseconds, void callback(Timer timer)) {
+  patch factory Timer.repeating(var duration, void callback(Timer timer)) {
+    // TODO(floitsch): remove this check when we remove the deprecated
+    // millisecond argument.
+    int milliseconds = duration is int ? duration : duration.inMilliseconds;
+    if (milliseconds < 0) milliseconds = 0;
     return new TimerImpl.repeating(milliseconds, callback);
   }
 }
diff --git a/sdk/lib/_internal/compiler/implementation/lib/foreign_helper.dart b/sdk/lib/_internal/compiler/implementation/lib/foreign_helper.dart
index 2ad4a1e..177295b 100644
--- a/sdk/lib/_internal/compiler/implementation/lib/foreign_helper.dart
+++ b/sdk/lib/_internal/compiler/implementation/lib/foreign_helper.dart
@@ -136,3 +136,8 @@
  * Returns the prefix used for generated is checks on classes.
  */
 String JS_OPERATOR_IS_PREFIX() {}
+
+/**
+ * Returns the prefix used for generated type argument substitutions on classes.
+ */
+String JS_OPERATOR_AS_PREFIX() {}
diff --git a/sdk/lib/_internal/compiler/implementation/lib/isolate_helper.dart b/sdk/lib/_internal/compiler/implementation/lib/isolate_helper.dart
index 82a80fe..54bbdc1 100644
--- a/sdk/lib/_internal/compiler/implementation/lib/isolate_helper.dart
+++ b/sdk/lib/_internal/compiler/implementation/lib/isolate_helper.dart
@@ -313,7 +313,7 @@
       // Run each iteration from the browser's top event loop.
       void next() {
         if (!runIteration()) return;
-        new Timer(0, (_) => next());
+        Timer.run(next);
       }
       next();
     } else {
@@ -1264,7 +1264,7 @@
   bool _inEventLoop = false;
   int _handle;
 
-  TimerImpl(int milliseconds, void callback(Timer timer))
+  TimerImpl(int milliseconds, void callback())
       : _once = true {
     if (milliseconds == 0 && (!hasTimer() || _globalState.isWorker)) {
       // This makes a dependency between the async library and the
@@ -1273,14 +1273,13 @@
       // TODO(7907): In case of web workers, we need to use the event
       // loop instead of setTimeout, to make sure the futures get executed in
       // order.
-      _globalState.topEventLoop.enqueue(_globalState.currentContext, () {
-        callback(this);
-      }, 'timer');
+      _globalState.topEventLoop.enqueue(
+          _globalState.currentContext, callback, 'timer');
       _inEventLoop = true;
     } else if (hasTimer()) {
       _globalState.topEventLoop.activeTimerCount++;
       void internalCallback() {
-        callback(this);
+        callback();
         _handle = null;
         _globalState.topEventLoop.activeTimerCount--;
       }
diff --git a/sdk/lib/_internal/compiler/implementation/lib/js_helper.dart b/sdk/lib/_internal/compiler/implementation/lib/js_helper.dart
index 0fbff9c..61f532e 100644
--- a/sdk/lib/_internal/compiler/implementation/lib/js_helper.dart
+++ b/sdk/lib/_internal/compiler/implementation/lib/js_helper.dart
@@ -10,6 +10,7 @@
                                    JS_CALL_IN_ISOLATE,
                                    JS_CURRENT_ISOLATE,
                                    JS_OPERATOR_IS_PREFIX,
+                                   JS_OPERATOR_AS_PREFIX,
                                    JS_HAS_EQUALS,
                                    RAW_DART_FUNCTION_REF,
                                    UNINTERCEPTED;
@@ -1062,12 +1063,12 @@
 
 getRuntimeTypeInfo(target) {
   if (target == null) return null;
-  var res = JS('var', r'#.$builtinTypeInfo', target);
-  // If the object does not have runtime type information, return an
-  // empty literal, to avoid null checks.
-  // TODO(ngeoffray): Make the object a top-level field to avoid
-  // allocating a new object every single time.
-  return (res == null) ? JS('var', '{}') : res;
+  return JS('var', r'#.$builtinTypeInfo', target);
+}
+
+getRuntimeTypeArgument(target, substitution, index) {
+  var arguments = substitute(substitution, getRuntimeTypeInfo(target));
+  return (arguments == null) ? null : getField(arguments, index);
 }
 
 /**
@@ -1429,7 +1430,7 @@
   return JS('String', r'#.constructor.builtin$cls', object);
 }
 
-String getTypeArgumentAsString(List runtimeType) {
+String getRuntimeTypeAsString(List runtimeType) {
   String className = getConstructorName(runtimeType[0]);
   if (runtimeType.length == 1) return className;
   return '$className<${joinArguments(runtimeType, 1)}>';
@@ -1442,7 +1443,7 @@
     return 'dynamic';
   } else if (isJsArray(type)) {
     // A list representing a type with arguments.
-    return getTypeArgumentAsString(type);
+    return getRuntimeTypeAsString(type);
   } else {
     // A reference to the constructor.
     return getConstructorName(type);
@@ -1471,6 +1472,54 @@
   return "$className<${joinArguments(typeInfo, 0)}>";
 }
 
+bool isJsFunction(var o) => JS('bool', r"typeof # == 'function'", o);
+
+Object invoke(function, arguments) {
+  return JS('var', r'#.apply(null, #)', function, arguments);
+}
+
+substitute(var substitution, var arguments) {
+  if (isJsArray(substitution)) {
+    arguments = substitution;
+  } else if (isJsFunction(substitution)) {
+    arguments = invoke(substitution, arguments);
+  }
+  return arguments;
+}
+
+/**
+ * Check that the types in the list [arguments] are subtypes of the types in
+ * list [checks] (at the respective positions), possibly applying [substitution]
+ * to the arguments before the check.
+ *
+ * See [:RuntimeTypes.getSubtypeSubstitution:] for a description of the possible
+ * values for [substitution].
+ */
+bool checkArguments(var substitution, var arguments, var checks) {
+  return areSubtypes(substitute(substitution, arguments), checks);
+}
+
+bool areSubtypes(List s, List t) {
+  // [:null:] means a raw type.
+  if (s == null || t == null) return true;
+
+  assert(isJsArray(s));
+  assert(isJsArray(t));
+  assert(s.length == t.length);
+
+  int len = s.length;
+  for (int i = 0; i < len; i++) {
+    if (!isSubtype(s[i], t[i])) {
+      return false;
+    }
+  }
+  return true;
+}
+
+getArguments(var type) => JS('var', r'#.slice(1)', type);
+
+getField(var object, var name) => JS('var', r'#[#]', object, name);
+
 /**
  * Check whether the type represented by [s] is a subtype of the type
  * represented by [t].
@@ -1494,20 +1543,20 @@
   // constructed from the type of [t].
   var typeOfS = isJsArray(s) ? s[0] : s;
   var typeOfT = isJsArray(t) ? t[0] : t;
+  // Check for a subtyping flag.
   var test = '${JS_OPERATOR_IS_PREFIX()}${runtimeTypeToString(typeOfT)}';
-  if (JS('var', r'#[#]', typeOfS, test) == null) return false;
+  if (getField(typeOfS, test) == null) return false;
   // The class of [s] is a subclass of the class of [t]. If either of the types
   // is raw, [s] is a subtype of [t].
   if (!isJsArray(s) || !isJsArray(t)) return true;
-  // Recursively check the type arguments.
-  int len = s.length;
-  if (len != t.length) return false;
-  for (int i = 1; i < len; i++) {
-    if (!isSubtype(s[i], t[i])) {
-      return false;
-    }
+  // Get the necessary substitution of the type arguments, if there is one.
+  var substitution;
+  if (JS('bool', '# !== #', typeOfT, typeOfS)) {
+    var field = '${JS_OPERATOR_AS_PREFIX()}${runtimeTypeToString(typeOfT)}';
+    substitution = getField(typeOfS, field);
   }
-  return true;
+  // Recursively check the type arguments.
+  return checkArguments(substitution, getArguments(s), getArguments(t));
 }
 
 createRuntimeType(String name) => new TypeImpl(name);
diff --git a/sdk/lib/_internal/compiler/implementation/lib/js_string.dart b/sdk/lib/_internal/compiler/implementation/lib/js_string.dart
index 381a690..dcb5b4e 100644
--- a/sdk/lib/_internal/compiler/implementation/lib/js_string.dart
+++ b/sdk/lib/_internal/compiler/implementation/lib/js_string.dart
@@ -153,9 +153,7 @@
     throw new UnimplementedError("String.codeUnits");
   }
 
-  Iterable<int> get runes {
-    throw new UnimplementedError("String.runes");
-  }
+  Runes get runes => new Runes(this);
 
   int indexOf(String other, [int start = 0]) {
     checkNull(other);
diff --git a/sdk/lib/_internal/compiler/implementation/lib/mirrors_patch.dart b/sdk/lib/_internal/compiler/implementation/lib/mirrors_patch.dart
index 1236507..0baf9a8 100644
--- a/sdk/lib/_internal/compiler/implementation/lib/mirrors_patch.dart
+++ b/sdk/lib/_internal/compiler/implementation/lib/mirrors_patch.dart
@@ -82,7 +82,7 @@
     var completer = new Completer<InstanceMirror>();
     // TODO(ahe): [Completer] or [Future] should have API to create a
     // delayed action.  Simulating with a [Timer].
-    new Timer(0, (timer) {
+    Timer.run(() {
       if (JS('String', 'typeof #', method) == 'function') {
         var result =
             JS('var', '#.apply(#, #)', method, reflectee, jsList);
diff --git a/sdk/lib/_internal/compiler/implementation/native_handler.dart b/sdk/lib/_internal/compiler/implementation/native_handler.dart
index 373f16e..894a605 100644
--- a/sdk/lib/_internal/compiler/implementation/native_handler.dart
+++ b/sdk/lib/_internal/compiler/implementation/native_handler.dart
@@ -874,7 +874,7 @@
       arguments.add('#');
     });
 
-    String foreignParameters = Strings.join(arguments, ',');
+    String foreignParameters = arguments.join(',');
     String nativeMethodCall;
     if (element.kind == ElementKind.FUNCTION) {
       nativeMethodCall = '$receiver$nativeMethodName($foreignParameters)';
diff --git a/sdk/lib/_internal/compiler/implementation/scanner/keyword.dart b/sdk/lib/_internal/compiler/implementation/scanner/keyword.dart
index 5d3c9a8..e9f5930 100644
--- a/sdk/lib/_internal/compiler/implementation/scanner/keyword.dart
+++ b/sdk/lib/_internal/compiler/implementation/scanner/keyword.dart
@@ -126,9 +126,11 @@
  * Abstract state in a state machine for scanning keywords.
  */
 abstract class KeywordState {
+  KeywordState(this.keyword);
+
   bool isLeaf();
   KeywordState next(int c);
-  Keyword get keyword;
+  final Keyword keyword;
 
   static KeywordState _KEYWORD_STATE;
   static KeywordState get KEYWORD_STATE {
@@ -156,7 +158,7 @@
         isLeaf = true;
       }
       if (strings[i].length > start) {
-        int c = strings[i].charCodeAt(start);
+        int c = strings[i].codeUnitAt(start);
         if (chunk != c) {
           if (chunkStart != -1) {
             assert(result[chunk - $a] == null);
@@ -191,10 +193,9 @@
  */
 class ArrayKeywordState extends KeywordState {
   final List<KeywordState> table;
-  final Keyword keyword;
 
   ArrayKeywordState(List<KeywordState> this.table, String syntax)
-    : keyword = (syntax == null) ? null : Keyword.keywords[syntax];
+    : super((syntax == null) ? null : Keyword.keywords[syntax]);
 
   bool isLeaf() => false;
 
@@ -223,9 +224,7 @@
  * A state that has no outgoing transitions.
  */
 class LeafKeywordState extends KeywordState {
-  final Keyword keyword;
-
-  LeafKeywordState(String syntax) : keyword = Keyword.keywords[syntax];
+  LeafKeywordState(String syntax) : super(Keyword.keywords[syntax]);
 
   bool isLeaf() => true;
 
diff --git a/sdk/lib/_internal/compiler/implementation/scanner/string_scanner.dart b/sdk/lib/_internal/compiler/implementation/scanner/string_scanner.dart
index 0fa3489e..10ad12a 100644
--- a/sdk/lib/_internal/compiler/implementation/scanner/string_scanner.dart
+++ b/sdk/lib/_internal/compiler/implementation/scanner/string_scanner.dart
@@ -19,7 +19,7 @@
   int peek() => charAt(byteOffset + 1);
 
   int charAt(index)
-      => (string.length > index) ? string.charCodeAt(index) : $EOF;
+      => (string.length > index) ? string.codeUnitAt(index) : $EOF;
 
   SourceString asciiString(int start, int offset) {
     return new SubstringWrapper(string, start, byteOffset + offset);
@@ -102,5 +102,5 @@
 
   bool get isEmpty => begin == end;
 
-  bool isPrivate() => !isEmpty && identical(internalString.charCodeAt(begin), $_);
+  bool isPrivate() => !isEmpty && internalString.codeUnitAt(begin) == $_;
 }
diff --git a/sdk/lib/_internal/compiler/implementation/scanner/token.dart b/sdk/lib/_internal/compiler/implementation/scanner/token.dart
index a236eae..c4fda3c 100644
--- a/sdk/lib/_internal/compiler/implementation/scanner/token.dart
+++ b/sdk/lib/_internal/compiler/implementation/scanner/token.dart
@@ -236,7 +236,7 @@
 
   bool get isEmpty => stringValue.isEmpty;
 
-  bool isPrivate() => !isEmpty && identical(stringValue.charCodeAt(0), $_);
+  bool isPrivate() => !isEmpty && stringValue.codeUnitAt(0) == $_;
 }
 
 class StringCodeIterator implements Iterator<int> {
@@ -259,7 +259,7 @@
   bool moveNext() {
     _current = null;
     if (index >= end) return false;
-    _current = string.charCodeAt(index++);
+    _current = string.codeUnitAt(index++);
     return true;
   }
 }
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/builder.dart b/sdk/lib/_internal/compiler/implementation/ssa/builder.dart
index ac19fea..8b44913 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/builder.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/builder.dart
@@ -248,6 +248,25 @@
     updateLocal(boxElement, newBox);
   }
 
+  HType cachedTypeOfThis;
+
+  HType computeTypeOfThis() {
+    Element element = closureData.thisElement;
+    ClassElement cls = element.enclosingElement.getEnclosingClass();
+    Compiler compiler = builder.compiler;
+    DartType type = cls.computeType(compiler);
+    if (compiler.world.isUsedAsMixin(cls)) {
+      // If the enclosing class is used as a mixin, [:this:] can be
+      // of the class that mixins the enclosing class. These two
+      // classes do not have a subclass relationship, so, for
+      // simplicity, we mark the type as an interface type.
+      cachedTypeOfThis = new HType.nonNullSubtype(type, compiler);
+    } else {
+      cachedTypeOfThis = new HType.nonNullSubclass(type, compiler);
+    }
+    return cachedTypeOfThis;
+  }
+
   /**
    * Documentation wanted -- johnniwinther
    *
@@ -301,10 +320,8 @@
       // Once closures have been mapped to classes their instance members might
       // not have any thisElement if the closure was created inside a static
       // context.
-      ClassElement cls = element.getEnclosingClass();
-      DartType type = cls.computeType(builder.compiler);
-      HThis thisInstruction = new HThis(closureData.thisElement,
-                                        new HBoundedType.nonNull(type));
+      HThis thisInstruction = new HThis(
+          closureData.thisElement, computeTypeOfThis());
       builder.graph.thisInstruction = thisInstruction;
       builder.graph.entry.addAtEntry(thisInstruction);
       directLocals[closureData.thisElement] = thisInstruction;
@@ -416,17 +433,11 @@
     }
   }
 
-  HType cachedTypeOfThis;
-
   HInstruction readThis() {
     HInstruction res = readLocal(closureData.thisElement);
     if (res.guaranteedType == null) {
       if (cachedTypeOfThis == null) {
-        assert(closureData.isClosure());
-        Element element = closureData.thisElement;
-        ClassElement cls = element.enclosingElement.getEnclosingClass();
-        DartType type = cls.computeType(builder.compiler);
-        cachedTypeOfThis = new HBoundedType.nonNull(type);
+        computeTypeOfThis();
       }
       res.guaranteedType = cachedTypeOfThis;
     }
@@ -2188,7 +2199,6 @@
         isAnd: (const SourceString("&&") == op.source));
   }
 
-
   void visitLogicalNot(Send node) {
     assert(node.argumentsNode is Prefix);
     visit(node.receiver);
@@ -2656,43 +2666,46 @@
       HInstruction instruction;
       if (type.element.isTypeVariable() ||
           RuntimeTypeInformation.hasTypeArguments(type)) {
-        HInstruction typeInfo = getRuntimeTypeInfo(expression);
-        // TODO(karlklose): make isSubtype a HInstruction to enable
-        // optimizations?
-        Element helper = compiler.findHelper(const SourceString('isSubtype'));
-        HInstruction isSubtype = new HStatic(helper);
-        add(isSubtype);
-        // Build a list of representations for the type arguments.
-        List<HInstruction> representations =
-            buildTypeArgumentRepresentations(type);
-        // For each type argument, build a call to isSubtype, with the type
-        // argument as first and the representation of the tested type as
-        // second argument.
-        List<HInstruction> checks = <HInstruction>[];
-        int index = 0;
-        representations.forEach((HInstruction representation) {
-          HInstruction position = graph.addConstantInt(index, constantSystem);
-          // Get the index'th type argument from the runtime type information.
-          HInstruction typeArgument =
-              createForeign('#[#]', HType.UNKNOWN, [typeInfo, position]);
-          add(typeArgument);
-          // Create the call to isSubtype.
-          List<HInstruction> inputs =
-              <HInstruction>[isSubtype, typeArgument, representation];
-          HInstruction call = new HInvokeStatic(inputs, HType.BOOLEAN);
-          add(call);
-          checks.add(call);
-          index++;
-        });
-        instruction = new HIs(type, <HInstruction>[expression]..addAll(checks));
+
+        void argumentsCheck() {
+          HInstruction typeInfo = getRuntimeTypeInfo(expression);
+          Element helper =
+              compiler.findHelper(const SourceString('checkArguments'));
+          HInstruction helperCall = new HStatic(helper);
+          add(helperCall);
+          List<HInstruction> representations =
+              buildTypeArgumentRepresentations(type);
+          Element element = type.element;
+          String substitution = backend.namer.substitutionName(element);
+          if (backend.emitter.nativeEmitter.requiresNativeIsCheck(element)) {
+            substitution = '$substitution()';
+          }
+          HInstruction fieldGet =
+              createForeign('#.$substitution', HType.UNKNOWN, [expression]);
+          HInstruction representationList = new HLiteralList(representations);
+          add(fieldGet);
+          add(representationList);
+          List<HInstruction> inputs = <HInstruction>[helperCall,
+                                                     fieldGet,
+                                                     typeInfo,
+                                                     representationList];
+          push(new HInvokeStatic(inputs, HType.UNKNOWN));
+        }
+
+        void classCheck() { push(new HIs(type, <HInstruction>[expression])); }
+
+        SsaBranchBuilder branchBuilder = new SsaBranchBuilder(this, node);
+        branchBuilder.handleLogicalAndOr(classCheck, argumentsCheck, isAnd: true);
+        instruction = pop();
       } else {
         instruction = new HIs(type, <HInstruction>[expression]);
+        add(instruction);
       }
       if (isNot) {
-        add(instruction);
         instruction = new HNot(instruction);
+        add(instruction);
       }
-      push(instruction);
+      stack.add(instruction);
     } else if (const SourceString("as") == op.source) {
       visit(node.receiver);
       HInstruction expression = pop();
@@ -3030,6 +3043,8 @@
       handleForeignCreateIsolate(node);
     } else if (name == const SourceString('JS_OPERATOR_IS_PREFIX')) {
       stack.add(addConstantString(node, backend.namer.operatorIsPrefix()));
+    } else if (name == const SourceString('JS_OPERATOR_AS_PREFIX')) {
+      stack.add(addConstantString(node, backend.namer.operatorAsPrefix()));
     } else {
       throw "Unknown foreign: ${selector}";
     }
@@ -3149,14 +3164,13 @@
       return graph.addConstantNull(constantSystem);
     }
 
-    // These variables are shared between invocations of the helper.
-    HInstruction typeInfo;
+    // The inputs are shared between invocations of the helper.
     List<HInstruction> inputs = <HInstruction>[];
 
     /**
      * Helper to create an instruction that gets the value of a type variable.
      */
-    void addTypeVariableReference(TypeVariableType type) {
+    String addTypeVariableReference(TypeVariableType type) {
       Element member = currentElement;
       if (member.enclosingElement.isClosure()) {
         ClosureClassElement closureClass = member.enclosingElement;
@@ -3164,22 +3178,30 @@
         member = member.getOutermostEnclosingMemberOrTopLevel();
       }
       if (member.isFactoryConstructor()) {
-        // The type variable is stored in a parameter of the factory.
+        // The type variable is stored in a parameter of the method.
         inputs.add(localsHandler.readLocal(type.element));
-      } else if (member.isInstanceMember()
-                 || member.isGenerativeConstructor()) {
-        // The type variable is stored in [this].
-        if (typeInfo == null) {
-          pushInvokeHelper1(backend.getGetRuntimeTypeInfo(),
-                            localsHandler.readThis(),
-                            HType.UNKNOWN);
-          typeInfo = pop();
-        }
+      } else if (member.isInstanceMember() ||
+                 member.isGenerativeConstructor()) {
+        // The type variable is stored on the object.  Generate code to extract
+        // the type arguments from the object, substitute them as an instance
+        // of the type we are testing against (if necessary), and extract the
+        // type argument by the index of the variable in the list of type
+        // variables for that class.
         int index = RuntimeTypeInformation.getTypeVariableIndex(type);
-        HInstruction foreign = createForeign('#[$index]', HType.STRING,
-                                             <HInstruction>[typeInfo]);
-        add(foreign);
-        inputs.add(foreign);
+        HInstruction thisObject = localsHandler.readThis();
+        String substitutionNameString =
+            backend.namer.substitutionName(member.getEnclosingClass());
+        HInstruction substitutionName = graph.addConstantString(
+            new LiteralDartString(substitutionNameString), null, constantSystem);
+        HInstruction substitution = createForeign('#[#]', HType.UNKNOWN,
+            <HInstruction>[thisObject, substitutionName]);
+        add(substitution);
+        pushInvokeHelper3(backend.getGetRuntimeTypeArgument(),
+                          thisObject,
+                          substitution,
+                          graph.addConstantInt(index, constantSystem),
+                          HType.UNKNOWN);
+        inputs.add(pop());
       } else {
         // TODO(ngeoffray): Match the VM behavior and throw an
         // exception at runtime.
@@ -3199,13 +3221,13 @@
                              Node currentNode,
                              HInstruction newObject) {
     if (!compiler.world.needsRti(type.element)) return;
-    List<HInstruction> inputs = <HInstruction>[];
     if (!type.isRaw) {
+      List<HInstruction> inputs = <HInstruction>[];
       type.typeArguments.forEach((DartType argument) {
         inputs.add(analyzeTypeArgument(argument, currentNode));
       });
+      callSetRuntimeTypeInfo(type.element, inputs, newObject);
     }
-    callSetRuntimeTypeInfo(type.element, inputs, newObject);
   }
 
   void callSetRuntimeTypeInfo(ClassElement element,
@@ -4020,10 +4042,11 @@
     }
     HLiteralList keyValuePairs = new HLiteralList(inputs);
     add(keyValuePairs);
+    DartType mapType = compiler.mapLiteralClass.computeType(compiler);
+    // TODO(ngeoffray): Use the actual implementation type of a map
+    // literal.
     pushInvokeHelper1(backend.getMapMaker(), keyValuePairs,
-        new HType.fromBoundedType(compiler.mapClass.computeType(compiler),
-                                  compiler,
-                                  false));
+        new HType.nonNullSubtype(mapType, compiler));
   }
 
   visitLiteralMapEntry(LiteralMapEntry node) {
@@ -4611,8 +4634,20 @@
   HType mapBaseType(BaseType baseType) {
     if (!baseType.isClass()) return HType.UNKNOWN;
     ClassBaseType classBaseType = baseType;
-    return new HType.fromBoundedType(
-        classBaseType.element.computeType(compiler), compiler, false);
+    ClassElement cls = classBaseType.element;
+    // Special case the list and map classes that are used as types
+    // for literals in the type inferrer.
+    if (cls == compiler.listClass) {
+      return HType.READABLE_ARRAY;
+    } else if (cls == compiler.mapClass) {
+      // TODO(ngeoffray): get the actual implementation of a map
+      // literal.
+      return new HType.nonNullSubtype(
+          compiler.mapLiteralClass.computeType(compiler), compiler);
+    } else {
+      return new HType.nonNullExactClass(
+          cls.computeType(compiler), compiler);
+    }
   }
 
   HType mapInferredType(ConcreteType concreteType) {
@@ -4625,14 +4660,16 @@
     return ssaType;
   }
 
+  // [type] is either an instance of [DartType] or special objects
+  // like [native.SpecialType.JsObject], or [native.SpecialType.JsArray].
   HType mapNativeType(type) {
     if (type == native.SpecialType.JsObject) {
-      return new HBoundedType.exact(
-          compiler.objectClass.computeType(compiler));
+      return new HType.nonNullExactClass(
+          compiler.objectClass.computeType(compiler), compiler);
     } else if (type == native.SpecialType.JsArray) {
       return HType.READABLE_ARRAY;
     } else {
-      return new HType.fromBoundedType(type, compiler, false);
+      return new HType.nonNullSubclass(type, compiler);
     }
   }
 
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/codegen.dart b/sdk/lib/_internal/compiler/implementation/ssa/codegen.dart
index e5caac1..d9407c0 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/codegen.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/codegen.dart
@@ -1567,9 +1567,14 @@
     int receiverIndex = node.isInterceptorCall ? 1 : 0;
     HType receiverHType = types[node.inputs[receiverIndex]];
     DartType receiverType = receiverHType.computeType(compiler);
-    if (receiverType != null &&
-        !identical(receiverType.kind, TypeKind.MALFORMED_TYPE)) {
-      return new TypedSelector(receiverType, defaultSelector);
+    if (receiverType != null && !receiverType.isMalformed) {
+      if (receiverHType.isExact()) {
+        return new TypedSelector.exact(receiverType, defaultSelector);
+      } else if (receiverHType.isInterfaceType()) {
+        return new TypedSelector.subtype(receiverType, defaultSelector);
+      } else {
+        return new TypedSelector.subclass(receiverType, defaultSelector);
+      }
     } else {
       return defaultSelector;
     }
@@ -1709,35 +1714,32 @@
 
   visitFieldGet(HFieldGet node) {
     use(node.receiver);
-    if (node.element == backend.jsArrayLength
-        || node.element == backend.jsStringLength) {
+    Element element = node.element;
+    if (element == backend.jsArrayLength || element == backend.jsStringLength) {
       // We're accessing a native JavaScript property called 'length'
       // on a JS String or a JS array. Therefore, the name of that
       // property should not be mangled.
       push(new js.PropertyAccess.field(pop(), 'length'), node);
     } else {
-      String name = _fieldPropertyName(node.element);
+      String name = _fieldPropertyName(element);
       push(new js.PropertyAccess.field(pop(), name), node);
-      HType receiverHType = types[node.receiver];
-      DartType type = receiverHType.computeType(compiler);
+      DartType type = types[node.receiver].computeType(compiler);
       if (type != null && !identical(type.kind, TypeKind.MALFORMED_TYPE)) {
-        world.registerFieldGetter(
-            node.element.name, node.element.getLibrary(), type);
+        world.registerFieldGetter(element);
       }
     }
   }
 
   visitFieldSet(HFieldSet node) {
-    String name = _fieldPropertyName(node.element);
+    Element element = node.element;
+    String name = _fieldPropertyName(element);
     DartType type = types[node.receiver].computeType(compiler);
     if (type != null && !identical(type.kind, TypeKind.MALFORMED_TYPE)) {
       // Field setters in the generative constructor body are handled in a
       // step "SsaConstructionFieldTypes" in the ssa optimizer.
       if (!work.element.isGenerativeConstructorBody()) {
-        world.registerFieldSetter(
-            node.element.name, node.element.getLibrary(), type);
-        backend.registerFieldSetter(
-            work.element, node.element, types[node.value]);
+        world.registerFieldSetter(element);
+        backend.registerFieldSetter(work.element, element, types[node.value]);
       }
     }
     use(node.receiver);
@@ -2358,17 +2360,6 @@
       checkType(input, type);
       attachLocationToLast(node);
     }
-    if (node.hasArgumentChecks()) {
-      InterfaceType interfaceType = type;
-      ClassElement cls = type.element;
-      Link<DartType> arguments = interfaceType.typeArguments;
-      js.Expression result = pop();
-      for (int i = 0; i < node.checkCount; i++) {
-        use(node.getCheck(i));
-        result = new js.Binary('&&', result, pop());
-      }
-      push(result, node);
-    }
     if (node.nullOk) {
       checkNull(input);
       push(new js.Binary('||', pop(), pop()), node);
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/nodes.dart b/sdk/lib/_internal/compiler/implementation/ssa/nodes.dart
index afbd2e1..5d7a29c 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/nodes.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/nodes.dart
@@ -1152,8 +1152,7 @@
 
     // If the original can't be null, type conversion also can't produce null.
     bool canBeNull = this.guaranteedType.canBeNull();
-    HType convertedType =
-        new HType.fromBoundedType(type, compiler, canBeNull);
+    HType convertedType = new HType.subtype(type, compiler);
 
     // No need to convert if we know the instruction has
     // [convertedType] as a bound.
@@ -2282,10 +2281,9 @@
   }
 
   HInstruction get expression => inputs[0];
-  HInstruction getCheck(int index) => inputs[index + 1];
-  int get checkCount => inputs.length - 1;
+  HInstruction get checkCall => inputs[1];
 
-  bool hasArgumentChecks() => inputs.length > 1;
+  bool hasArgumentsCheck() => inputs.length > 1;
 
   HType get guaranteedType => HType.BOOLEAN;
 
@@ -2302,7 +2300,7 @@
 }
 
 class HTypeConversion extends HCheck {
-  HType type;
+  final HType type;
   final int kind;
 
   static const int NO_CHECK = 0;
@@ -2313,6 +2311,7 @@
 
   HTypeConversion(this.type, HInstruction input, [this.kind = NO_CHECK])
       : super(<HInstruction>[input]) {
+    assert(type != null);
     sourceElement = input.sourceElement;
   }
   HTypeConversion.checkedModeCheck(HType type, HInstruction input)
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart b/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart
index 35ba2cb..0166ac0 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart
@@ -386,8 +386,7 @@
     if (node.isInterceptorCall) return handleInterceptorCall(node);
     HType receiverType = types[node.receiver];
     if (receiverType.isExact()) {
-      HBoundedType type = receiverType;
-      Element element = type.lookupMember(node.selector.name);
+      Element element = receiverType.lookupMember(node.selector.name, compiler);
       // TODO(ngeoffray): Also fold if it's a getter or variable.
       if (element != null && element.isFunction()) {
         if (node.selector.applies(element, compiler)) {
@@ -1276,8 +1275,8 @@
 
   void visitIs(HIs instruction) {
     HInstruction input = instruction.expression;
-    HType convertedType =
-        new HType.fromBoundedType(instruction.typeExpression, compiler);
+    HType convertedType = new HType.nonNullSubtype(
+        instruction.typeExpression, compiler);
 
     List<HInstruction> ifUsers = <HInstruction>[];
     List<HInstruction> notIfUsers = <HInstruction>[];
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/types.dart b/sdk/lib/_internal/compiler/implementation/ssa/types.dart
index b6d4746..3e01f1a 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/types.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/types.dart
@@ -13,12 +13,17 @@
    */
   factory HType.fromBoundedType(DartType type,
                                 Compiler compiler,
-                                [bool canBeNull = false]) {
+                                {bool canBeNull: true,
+                                 bool isExact: false,
+                                 bool isInterfaceType: true}) {
     Element element = type.element;
     if (element.kind == ElementKind.TYPE_VARIABLE) {
       // TODO(ngeoffray): Replace object type with [type].
       return new HBoundedPotentialPrimitiveType(
-          compiler.objectClass.computeType(compiler), canBeNull, true);
+          compiler.objectClass.computeType(compiler),
+          true,
+          canBeNull: canBeNull,
+          isInterfaceType: isInterfaceType);
     }
 
     if (element == compiler.intClass) {
@@ -33,20 +38,72 @@
       return canBeNull ? HType.BOOLEAN_OR_NULL : HType.BOOLEAN;
     } else if (element == compiler.nullClass) {
       return HType.NULL;
-    } else if (element == compiler.listClass
-        || Elements.isListSupertype(element, compiler)) {
-      return new HBoundedPotentialPrimitiveArray(type, canBeNull);
-    } else if (Elements.isNumberOrStringSupertype(element, compiler)) {
-      return new HBoundedPotentialPrimitiveNumberOrString(type, canBeNull);
-    } else if (Elements.isStringOnlySupertype(element, compiler)) {
-      return new HBoundedPotentialPrimitiveString(type, canBeNull);
-    } else if (element == compiler.objectClass) {
-      return new HBoundedPotentialPrimitiveType(
-          compiler.objectClass.computeType(compiler), canBeNull, true);
-    } else {
-      return canBeNull ? new HBoundedType.withNull(type)
-                       : new HBoundedType.nonNull(type);
+    } else if (!isExact) {
+      if (element == compiler.listClass
+          || Elements.isListSupertype(element, compiler)) {
+        return new HBoundedPotentialPrimitiveArray(
+            type,
+            canBeNull: canBeNull,
+            isInterfaceType: isInterfaceType);
+      } else if (Elements.isNumberOrStringSupertype(element, compiler)) {
+        return new HBoundedPotentialPrimitiveNumberOrString(
+            type,
+            canBeNull: canBeNull,
+            isInterfaceType: isInterfaceType);
+      } else if (Elements.isStringOnlySupertype(element, compiler)) {
+        return new HBoundedPotentialPrimitiveString(
+            type,
+            canBeNull: canBeNull,
+            isInterfaceType: isInterfaceType);
+      } else if (element == compiler.objectClass) {
+        return new HBoundedPotentialPrimitiveType(
+            compiler.objectClass.computeType(compiler),
+            true,
+            canBeNull: canBeNull,
+            isInterfaceType: isInterfaceType);
+      }
     }
+    return new HBoundedType(
+        type,
+        canBeNull: canBeNull,
+        isExact: isExact,
+        isInterfaceType: isInterfaceType);
+  }
+
+  factory HType.nonNullExactClass(DartType type, Compiler compiler) {
+    return new HType.fromBoundedType(
+        type,
+        compiler,
+        canBeNull: false,
+        isExact: true,
+        isInterfaceType: false);
+  }
+
+  factory HType.nonNullSubclass(DartType type, Compiler compiler) {
+    return new HType.fromBoundedType(
+        type,
+        compiler,
+        canBeNull: false,
+        isExact: false,
+        isInterfaceType: false);
+  }
+
+  factory HType.subtype(DartType type, Compiler compiler) {
+    return new HType.fromBoundedType(
+        type,
+        compiler,
+        canBeNull: true,
+        isExact: false,
+        isInterfaceType: true);
+  }
+
+  factory HType.nonNullSubtype(DartType type, Compiler compiler) {
+    return new HType.fromBoundedType(
+        type,
+        compiler,
+        canBeNull: false,
+        isExact: false,
+        isInterfaceType: true);
   }
 
   static const HType CONFLICTING = const HConflictingType();
@@ -91,6 +148,7 @@
   bool isExact() => false;
   bool isPrimitiveOrNull() => false;
   bool isTop() => false;
+  bool isInterfaceType() => false;
 
   bool canBePrimitive() => false;
   bool canBeNull() => false;
@@ -100,6 +158,14 @@
   /** Alias for isReadableArray. */
   bool isArray() => isReadableArray();
 
+  Element lookupMember(SourceString name, Compiler compiler) {
+    if (!isExact()) return null;
+    DartType type = computeType(compiler);
+    if (type == null) return null;
+    ClassElement cls = type.element;
+    return cls.lookupMember(name);
+  }
+
   DartType computeType(Compiler compiler);
 
   /**
@@ -164,6 +230,7 @@
   bool isPrimitive() => true;
   bool canBePrimitive() => true;
   bool isPrimitiveOrNull() => true;
+  bool isExact() => true;
 }
 
 class HNullType extends HPrimitiveType {
@@ -271,6 +338,8 @@
   const HNumberOrNullType();
   bool isNumberOrNull() => true;
   String toString() => "number or null";
+  bool isExact() => false;
+  bool isInterfaceType() => true;
 
   DartType computeType(Compiler compiler) {
     JavaScriptBackend backend = compiler.backend;
@@ -308,6 +377,8 @@
   bool isNumber() => true;
   bool isNumberOrNull() => true;
   String toString() => "number";
+  bool isExact() => false;
+  bool isInterfaceType() => true;
 
   DartType computeType(Compiler compiler) {
     JavaScriptBackend backend = compiler.backend;
@@ -540,7 +611,10 @@
         return other;
       } else {
         HBoundedType boundedType = other;
-        return new HBoundedPotentialPrimitiveString(boundedType.type, true);
+        return new HBoundedPotentialPrimitiveString(
+            boundedType.type,
+            canBeNull: true,
+            isInterfaceType: other.isInterfaceType());
       }
     }
     if (other.isNull()) return HType.STRING_OR_NULL;
@@ -618,7 +692,9 @@
     if (other.isNull()) {
       // TODO(ngeoffray): This should be readable array or null.
       return new HBoundedPotentialPrimitiveArray(
-          compiler.listClass.computeType(compiler), true);
+          compiler.listClass.computeType(compiler),
+          canBeNull: true,
+          isInterfaceType: true);
     }
     return HType.UNKNOWN;
   }
@@ -649,7 +725,9 @@
     if (other.isNull()) {
       // TODO(ngeoffray): This should be mutable array or null.
       return new HBoundedPotentialPrimitiveArray(
-          compiler.listClass.computeType(compiler), true);
+          compiler.listClass.computeType(compiler),
+          canBeNull: true,
+          isInterfaceType: true);
     }
     return HType.UNKNOWN;
   }
@@ -681,7 +759,9 @@
     if (other.isNull()) {
       // TODO(ngeoffray): This should be fixed array or null.
       return new HBoundedPotentialPrimitiveArray(
-          compiler.listClass.computeType(compiler), true);
+          compiler.listClass.computeType(compiler),
+          canBeNull: true,
+          isInterfaceType: true);
     }
     return HType.UNKNOWN;
   }
@@ -714,7 +794,9 @@
     if (other.isNull()) {
       // TODO(ngeoffray): This should be extendable array or null.
       return new HBoundedPotentialPrimitiveArray(
-          compiler.listClass.computeType(compiler), true);
+          compiler.listClass.computeType(compiler),
+          canBeNull: true,
+          isInterfaceType: true);
     }
     return HType.UNKNOWN;
   }
@@ -735,57 +817,51 @@
   final DartType type;
   final bool _canBeNull;
   final bool _isExact;
+  final bool _isInterfaceType;
 
   String toString() {
     return 'BoundedType($type, canBeNull: $_canBeNull, isExact: $_isExact)';
   }
 
   bool canBeNull() => _canBeNull;
-
   bool isExact() => _isExact;
+  bool isInterfaceType() => _isInterfaceType;
 
   const HBoundedType(DartType this.type,
-                     [bool canBeNull = false, isExact = false])
-      : _canBeNull = canBeNull, _isExact = isExact;
-  const HBoundedType.exact(DartType type) : this(type, false, true);
-  const HBoundedType.withNull(DartType type) : this(type, true, false);
-  const HBoundedType.nonNull(DartType type) : this(type);
+                     {bool canBeNull: true,
+                      bool isExact: false,
+                      bool isInterfaceType: true})
+      : _canBeNull = canBeNull,
+        _isExact = isExact,
+        _isInterfaceType = isInterfaceType;
+
+  const HBoundedType.exact(DartType this.type)
+      : _canBeNull = false, _isExact = true, _isInterfaceType = false;
 
   DartType computeType(Compiler compiler) => type;
 
-  Element lookupMember(SourceString name) {
-    if (!isExact()) return null;
-    ClassElement classElement = type.element;
-    return classElement.lookupMember(name);
-  }
-
   HType intersection(HType other, Compiler compiler) {
-    assert(!(isExact() && canBeNull()));
+    if (this == other) return this;
     if (other.isConflicting()) return HType.CONFLICTING;
     if (other.isNull()) return canBeNull() ? HType.NULL : HType.CONFLICTING;
 
     if (other is HBoundedType) {
       HBoundedType temp = other;
-      if (identical(this.type, temp.type)) {
-        // If the types are the same, we return the [HBoundedType]
-        // that has the most restrictive representation: if it's exact
-        // (eg cannot be a subtype), and if it cannot be null.
-        if (isExact()) {
-          return this;
-        } else if (other.isExact()) {
-          return other;
-        } else if (canBeNull()) {
-          return other;
-        } else {
-          return this;
+      DartType otherType = temp.type;
+      if (!type.isMalformed && !otherType.isMalformed) {
+        DartType intersectionType;
+        if (type == otherType || compiler.types.isSubtype(type, otherType)) {
+          intersectionType = type;
+        } else if (compiler.types.isSubtype(otherType, type)) {
+          intersectionType = otherType;
         }
-      // If one type is a subtype of the other, we return the former,
-      // which is the narrower type.
-      } else if (!type.isMalformed && !other.type.isMalformed) {
-        if (compiler.types.isSubtype(type, other.type)) {
-          return this;
-        } else if (compiler.types.isSubtype(other.type, type)) {
-          return other;
+        if (intersectionType != null) {
+          return new HType.fromBoundedType(
+              intersectionType,
+              compiler,
+              canBeNull: canBeNull() && temp.canBeNull(),
+              isExact: isExact() || other.isExact(),
+              isInterfaceType: isInterfaceType() && other.isInterfaceType());
         }
       }
     }
@@ -797,25 +873,35 @@
   bool operator ==(HType other) {
     if (other is !HBoundedType) return false;
     HBoundedType bounded = other;
-    return (identical(type, bounded.type)
-            && identical(canBeNull(), bounded.canBeNull())
-            && identical(isExact(), other.isExact()));
+    return type == bounded.type
+            && canBeNull() == bounded.canBeNull()
+            && isExact() == bounded.isExact()
+            && isInterfaceType() == bounded.isInterfaceType();
   }
 
   HType union(HType other, Compiler compiler) {
+    if (this == other) return this;
     if (other.isNull()) {
       if (canBeNull()) {
         return this;
       } else {
-        return new HBoundedType.withNull(type);
+        return new HBoundedType(
+            type,
+            canBeNull: true,
+            isExact: this.isExact(),
+            isInterfaceType: this.isInterfaceType());
       }
     }
     if (other is HBoundedType) {
       HBoundedType temp = other;
-      if (!identical(type, temp.type)) return HType.UNKNOWN;
-      if (isExact()) return other;
-      if (other.isExact()) return this;
-      return canBeNull() ? this : other;
+      if (type != temp.type) return HType.UNKNOWN;
+      return new HType.fromBoundedType(
+          type,
+          compiler,
+          canBeNull: canBeNull() || other.canBeNull(),
+          isInterfaceType: isInterfaceType() || other.isInterfaceType(),
+          isExact: isExact() && other.isExact());
+
     }
     if (other.isConflicting()) return this;
     return HType.UNKNOWN;
@@ -825,9 +911,13 @@
 class HBoundedPotentialPrimitiveType extends HBoundedType {
   final bool _isObject;
   const HBoundedPotentialPrimitiveType(DartType type,
-                                       bool canBeNull,
-                                       this._isObject)
-      : super(type, canBeNull, false);
+                                       this._isObject,
+                                       {bool canBeNull: true,
+                                        bool isInterfaceType: true})
+      : super(type,
+              canBeNull: canBeNull,
+              isExact: false,
+              isInterfaceType: isInterfaceType);
 
   String toString() {
     return 'BoundedPotentialPrimitiveType($type, canBeNull: $_canBeNull)';
@@ -840,7 +930,8 @@
     if (isTop()) {
       // The union of the top type and another type is the top type.
       if (!canBeNull() && other.canBeNull()) {
-        return new HBoundedPotentialPrimitiveType(type, true, true);
+        return new HBoundedPotentialPrimitiveType(
+            type, true, canBeNull: canBeNull(), isInterfaceType: true);
       } else {
         return this;
       }
@@ -862,25 +953,33 @@
 
 class HBoundedPotentialPrimitiveNumberOrString
     extends HBoundedPotentialPrimitiveType {
-  const HBoundedPotentialPrimitiveNumberOrString(DartType type, bool canBeNull)
-      : super(type, canBeNull, false);
+  const HBoundedPotentialPrimitiveNumberOrString(DartType type,
+                                                 {bool canBeNull: true,
+                                                  bool isInterfaceType: true})
+      : super(type,
+              false,
+              canBeNull: canBeNull,
+              isInterfaceType: isInterfaceType);
 
   HType union(HType other, Compiler compiler) {
     if (other.isNumber()) return this;
     if (other.isNumberOrNull()) {
       if (canBeNull()) return this;
-      return new HBoundedPotentialPrimitiveNumberOrString(type, true);
+      return new HBoundedPotentialPrimitiveNumberOrString(
+          type, canBeNull: true, isInterfaceType: this.isInterfaceType());
     }
 
     if (other.isString()) return this;
     if (other.isStringOrNull()) {
       if (canBeNull()) return this;
-      return new HBoundedPotentialPrimitiveNumberOrString(type, true);
+      return new HBoundedPotentialPrimitiveNumberOrString(
+          type, canBeNull: true, isInterfaceType: this.isInterfaceType());
     }
 
     if (other.isNull()) {
       if (canBeNull()) return this;
-      return new HBoundedPotentialPrimitiveNumberOrString(type, true);
+      return new HBoundedPotentialPrimitiveNumberOrString(
+          type, canBeNull: true, isInterfaceType: this.isInterfaceType());
     }
 
     return super.union(other, compiler);
@@ -902,8 +1001,13 @@
 }
 
 class HBoundedPotentialPrimitiveArray extends HBoundedPotentialPrimitiveType {
-  const HBoundedPotentialPrimitiveArray(DartType type, bool canBeNull)
-      : super(type, canBeNull, false);
+  const HBoundedPotentialPrimitiveArray(DartType type,
+                                        {bool canBeNull: true,
+                                         bool isInterfaceType: true})
+      : super(type,
+              false,
+              canBeNull: canBeNull,
+              isInterfaceType: isInterfaceType);
 
   HType union(HType other, Compiler compiler) {
     if (other.isString()) return HType.UNKNOWN;
@@ -914,7 +1018,8 @@
       if (canBeNull()) {
         return this;
       } else {
-        return new HBoundedPotentialPrimitiveArray(type, true);
+        return new HBoundedPotentialPrimitiveArray(
+            type, canBeNull: true, isInterfaceType: isInterfaceType());
       }
     }
     return super.union(other, compiler);
@@ -930,8 +1035,13 @@
 }
 
 class HBoundedPotentialPrimitiveString extends HBoundedPotentialPrimitiveType {
-  const HBoundedPotentialPrimitiveString(DartType type, bool canBeNull)
-      : super(type, canBeNull, false);
+  const HBoundedPotentialPrimitiveString(DartType type,
+                                         {bool canBeNull: true,
+                                          bool isInterfaceType: true})
+      : super(type,
+              false,
+              canBeNull: canBeNull,
+              isInterfaceType: isInterfaceType);
 
   bool isPrimitiveOrNull() => true;
 
@@ -941,14 +1051,16 @@
       if (canBeNull()) {
         return this;
       } else {
-        return new HBoundedPotentialPrimitiveString(type, true);
+        return new HBoundedPotentialPrimitiveString(
+            type, canBeNull: true, isInterfaceType: isInterfaceType());
       }
     }
     if (other.isNull()) {
       if (canBeNull()) {
         return this;
       } else {
-        return new HBoundedPotentialPrimitiveString(type, true);
+        return new HBoundedPotentialPrimitiveString(
+            type, canBeNull: true, isInterfaceType: isInterfaceType());
       }
     }
     // TODO(ngeoffray): implement union types.
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/variable_allocator.dart b/sdk/lib/_internal/compiler/implementation/ssa/variable_allocator.dart
index 34f960d..c1243d2 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/variable_allocator.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/variable_allocator.dart
@@ -62,7 +62,7 @@
   String toString() {
     List<String> res = new List<String>();
     for (final interval in ranges) res.add(interval.toString());
-    return '(${Strings.join(res, ', ')})';
+    return '(${res.join(", ")})';
   }
 }
 
diff --git a/sdk/lib/_internal/compiler/implementation/universe/function_set.dart b/sdk/lib/_internal/compiler/implementation/universe/function_set.dart
index 23bc782..6afdf09 100644
--- a/sdk/lib/_internal/compiler/implementation/universe/function_set.dart
+++ b/sdk/lib/_internal/compiler/implementation/universe/function_set.dart
@@ -7,45 +7,47 @@
 // TODO(kasperl): This actually holds getters and setters just fine
 // too and stricly they aren't functions. Maybe this needs a better
 // name -- something like ElementSet seems a bit too generic.
-class FunctionSet extends PartialTypeTree {
+class FunctionSet {
+  final Compiler compiler;
+  final Map<SourceString, FunctionSetNode> nodes =
+      new Map<SourceString, FunctionSetNode>();
+  FunctionSet(this.compiler);
 
-  FunctionSet(Compiler compiler) : super(compiler);
-
-  FunctionSetNode newSpecializedNode(ClassElement type)
-      => new FunctionSetNode(type);
-
-  // TODO(kasperl): Allow static members too?
   void add(Element element) {
     assert(element.isMember());
-    FunctionSetNode node = findNode(element.getEnclosingClass(), true);
-    node.membersByName[element.name] = element;
+    SourceString name = element.name;
+    FunctionSetNode node = nodes.putIfAbsent(
+        name, () => new FunctionSetNode(name));
+    node.add(element);
   }
 
-  // TODO(kasperl): Allow static members too?
   void remove(Element element) {
     assert(element.isMember());
-    FunctionSetNode node = findNode(element.getEnclosingClass(), false);
-    if (node != null) node.membersByName.remove(element.name);
+    SourceString name = element.name;
+    FunctionSetNode node = nodes[name];
+    if (node != null) {
+      node.remove(element);
+    }
   }
 
-  // TODO(kasperl): Allow static members too?
   bool contains(Element element) {
     assert(element.isMember());
-    FunctionSetNode node = findNode(element.getEnclosingClass(), false);
+    SourceString name = element.name;
+    FunctionSetNode node = nodes[name];
     return (node != null)
-        ? node.membersByName.containsKey(element.name)
+        ? node.contains(element)
         : false;
   }
 
   /**
    * Returns all elements that may be invoked with the given [selector].
    */
-  Set<Element> filterBySelector(Selector selector) {
-    // TODO(kasperl): For now, we use a different implementation for
-    // filtering if the tree contains interface subtypes.
-    return containsInterfaceSubtypes
-        ? filterAllBySelector(selector)
-        : filterHierarchyBySelector(selector);
+  Iterable<Element> filterBySelector(Selector selector) {
+    SourceString name = selector.name;
+    FunctionSetNode node = nodes[name];
+    return (node != null)
+        ? node.filter(selector, compiler)
+        : const <Element>[];
   }
 
   /**
@@ -53,88 +55,92 @@
    * [selector].
    */
   bool hasAnyElementMatchingSelector(Selector selector) {
-    // TODO(kasperl): For now, we use a different implementation for
-    // filtering if the tree contains interface subtypes.
-    return containsInterfaceSubtypes
-        ? hasAnyInAll(selector)
-        : hasAnyInHierarchy(selector);
+    return !filterBySelector(selector).isEmpty;
   }
 
-  Set<Element> filterAllBySelector(Selector selector) {
-    Set<Element> result = new Set<Element>();
-    if (root == null) return result;
-    root.visitRecursively((FunctionSetNode node) {
-      Element member = node.membersByName[selector.name];
-      // Since we're running through the entire tree we have to use
-      // the applies method that takes types into account.
-      if (member != null && selector.appliesUnnamed(member, compiler)) {
-        result.add(member);
-      }
-      return true;
-    });
-    return result;
-  }
-
-  Set<Element> filterHierarchyBySelector(Selector selector) {
-    Set<Element> result = new Set<Element>();
-    if (root == null) return result;
-    visitHierarchy(selectorType(selector), (FunctionSetNode node) {
-      Element member = node.membersByName[selector.name];
-      if (member != null && selector.appliesUntyped(member, compiler)) {
-        result.add(member);
-      }
-      return true;
-    });
-    return result;
-  }
-
-  bool hasAnyInAll(Selector selector) {
-    bool result = false;
-    if (root == null) return result;
-    root.visitRecursively((FunctionSetNode node) {
-      Element member = node.membersByName[selector.name];
-      // Since we're running through the entire tree we have to use
-      // the applies method that takes types into account.
-      if (member != null && selector.appliesUnnamed(member, compiler)) {
-        result = true;
-        // End the traversal.
-        return false;
-      }
-      return true;
-    });
-    return result;
-  }
-
-  bool hasAnyInHierarchy(Selector selector) {
-    bool result = false;
-    if (root == null) return result;
-    visitHierarchy(selectorType(selector), (FunctionSetNode node) {
-      Element member = node.membersByName[selector.name];
-      if (member != null && selector.appliesUntyped(member, compiler)) {
-        result = true;
-        // End the traversal.
-        return false;
-      }
-      return true;
-    });
-    return result;
-  }
-
-  void forEach(Function f) {
-    if (root == null) return;
-    root.visitRecursively((FunctionSetNode node) {
-      node.membersByName.forEach(
-          (SourceString _, Element element) => f(element));
-      return true;
+  void forEach(Function action) {
+    nodes.forEach((SourceString name, FunctionSetNode node) {
+      node.forEach(action);
     });
   }
 }
 
-class FunctionSetNode extends PartialTypeTreeNode {
+class FunctionSetNode {
+  final SourceString name;
+  final Map<Selector, List<Element>> cache = new Map<Selector, List<Element>>();
 
-  final Map<SourceString, Element> membersByName;
+  // Initially, we keep the elements in a list because it is more
+  // compact than a hash set. Once we get enough elements, we change
+  // the representation to be a set to get faster contains checks.
+  static const int MAX_ELEMENTS_IN_LIST = 8;
+  Collection<Element> elements = <Element>[];
+  bool isList = true;
 
-  FunctionSetNode(ClassElement type) : super(type),
-      membersByName = new Map<SourceString, Element>();
+  FunctionSetNode(this.name);
 
+  void add(Element element) {
+    assert(element.name == name);
+    // We try to avoid clearing the cache unless we have to. For that
+    // reason we keep the explicit contains check even though the add
+    // method ends up doing the work again (for sets).
+    if (!elements.contains(element)) {
+      if (isList && elements.length >= MAX_ELEMENTS_IN_LIST) {
+        elements = elements.toSet();
+        isList = false;
+      }
+      elements.add(element);
+      cache.clear();
+    }
+  }
+
+  void remove(Element element) {
+    assert(element.name == name);
+    if (isList) {
+      List list = elements;
+      int index = list.indexOf(element);
+      if (index < 0) return;
+      Element last = list.removeLast();
+      if (index != list.length) {
+        list[index] = last;
+      }
+      cache.clear();
+    } else {
+      Set set = elements;
+      if (set.remove(element)) {
+        // To avoid wobbling between the two representations, we do
+        // not transition back to the list representation even if we
+        // end up with few enough elements at this point.
+        cache.clear();
+      }
+    }
+  }
+
+  bool contains(Element element) {
+    assert(element.name == name);
+    return elements.contains(element);
+  }
+
+  void forEach(Function action) {
+    elements.forEach(action);
+  }
+
+  Iterable<Element> filter(Selector selector, Compiler compiler) {
+    assert(selector.name == name);
+    List<Element> result = cache[selector];
+    if (result != null) return result;
+    for (Element element in elements) {
+      if (selector.appliesUnnamed(element, compiler)) {
+        if (result == null) {
+          // Defer the allocation of the resulting list until we are
+          // sure we need it. This allows us to return immutable empty
+          // lists when the filtering produced no results.
+          result = <Element>[];
+        }
+        result.add(element);
+      }
+    }
+    if (result == null) result = const <Element>[];
+    cache[selector] = result;
+    return result;
+  }
 }
diff --git a/sdk/lib/_internal/compiler/implementation/universe/partial_type_tree.dart b/sdk/lib/_internal/compiler/implementation/universe/partial_type_tree.dart
index f310174..b0ed8dc 100644
--- a/sdk/lib/_internal/compiler/implementation/universe/partial_type_tree.dart
+++ b/sdk/lib/_internal/compiler/implementation/universe/partial_type_tree.dart
@@ -5,66 +5,18 @@
 part of universe;
 
 abstract class PartialTypeTree {
-
   final Compiler compiler;
   PartialTypeTreeNode root;
 
-  // TODO(kasperl): This should be final but the VM will not allow
-  // that without making the map a compile-time constant.
-  Map<ClassElement, PartialTypeTreeNode> nodes =
-      new Map<ClassElement, PartialTypeTreeNode>();
-
-  // TODO(kasperl): For now, we keep track of whether or not the tree
-  // contains two classes with a subtype relationship that isn't a
-  // subclass relationship.
-  bool containsInterfaceSubtypes = false;
-
-  // TODO(kasperl): This should be final but the VM will not allow
-  // that without making the set a compile-time constant.
-  Set<ClassElement> unseenInterfaceSubtypes =
-      new Set<ClassElement>();
-
   PartialTypeTree(this.compiler);
 
-  PartialTypeTreeNode newSpecializedNode(ClassElement type);
+  PartialTypeTreeNode newNode(ClassElement type);
 
-  PartialTypeTreeNode newNode(ClassElement type) {
-    PartialTypeTreeNode node = newSpecializedNode(type);
-    nodes[type] = node;
-    if (containsInterfaceSubtypes) return node;
-
-    // Check if the implied interface of the new class is implemented
-    // by another class that is already in the tree.
-    if (unseenInterfaceSubtypes.contains(type)) {
-      containsInterfaceSubtypes = true;
-      unseenInterfaceSubtypes.clear();
-      return node;
-    }
-
-    // Run through all the implied interfaces the class that we're
-    // adding implements and see if any of them are already in the
-    // tree. If so, we have a tree with interface subtypes. If not,
-    // keep track of them so we can deal with it if the interface is
-    // added to the tree later.
-    for (Link link = type.interfaces; !link.isEmpty; link = link.tail) {
-      InterfaceType superType = link.head;
-      ClassElement superTypeElement = superType.element;
-      if (nodes.containsKey(superTypeElement)) {
-        containsInterfaceSubtypes = true;
-        unseenInterfaceSubtypes.clear();
-        break;
-      } else {
-        unseenInterfaceSubtypes.add(superTypeElement);
-      }
-    }
-    return node;
-  }
-
-  // TODO(kasperl): Move this to the Selector class?
   /**
    * Returns a [ClassElement] that is an upper bound of the receiver type on
    * [selector].
    */
+  // TODO(kasperl): Move this to the Selector class?
   ClassElement selectorType(Selector selector) {
     // TODO(ngeoffray): Should the tree be specialized with DartType?
     DartType type = selector.receiverType;
@@ -145,7 +97,6 @@
    * the [visit] function ever returns false, we abort the traversal.
    */
   void visitHierarchy(ClassElement type, bool visit(PartialTypeTreeNode node)) {
-    assert(!containsInterfaceSubtypes);
     PartialTypeTreeNode current = root;
     L: while (!identical(current.type, type)) {
       assert(type.isSubclassOf(current.type));
diff --git a/sdk/lib/_internal/compiler/implementation/universe/selector_map.dart b/sdk/lib/_internal/compiler/implementation/universe/selector_map.dart
index cf98a10..36b51399 100644
--- a/sdk/lib/_internal/compiler/implementation/universe/selector_map.dart
+++ b/sdk/lib/_internal/compiler/implementation/universe/selector_map.dart
@@ -4,12 +4,13 @@
 
 part of universe;
 
+// TODO(kasperl): It seems possible to rewrite this class to be more
+// like the FunctionSet abstraction which is a lot simpler.
 class SelectorMap<T> extends PartialTypeTree {
 
   SelectorMap(Compiler compiler) : super(compiler);
 
-  SelectorMapNode<T> newSpecializedNode(ClassElement type)
-      => new SelectorMapNode<T>(type);
+  SelectorMapNode<T> newNode(ClassElement type) => new SelectorMapNode<T>(type);
 
   T operator [](Selector selector) {
     SelectorMapNode<T> node = findNode(selectorType(selector), false);
@@ -23,32 +24,29 @@
     return null;
   }
 
+  // TODO(kasperl): Do we need to support removing selectors by
+  // passing null as the value?
   void operator []=(Selector selector, T value) {
-    SelectorMapNode<T> node = findNode(selectorType(selector), true);
-    Link<SelectorValue<T>> selectors = node.selectorsByName[selector.name];
-    if (selectors == null) {
-      // No existing selectors with the given name. Create a new
-      // linked list.
-      SelectorValue<T> head = new SelectorValue<T>(selector, value);
-      node.selectorsByName[selector.name] =
-          new Link<SelectorValue<T>>().prepend(head);
-    } else {
-      // Run through the linked list of selectors with the same name. If
-      // we find one that matches, we update the value in the mapping.
-      for (Link link = selectors; !link.isEmpty; link = link.tail) {
-        SelectorValue<T> existing = link.head;
-        // It is safe to ignore the type here, because all selector
-        // mappings that are stored in a single node have the same type.
-        if (existing.selector.equalsUntyped(selector)) {
-          existing.value = value;
-          return;
-        }
+    ClassElement type = selectorType(selector);
+    SelectorMapNode<T> node = findNode(type, true);
+    SourceString name = selector.name;
+    Link<SelectorValue<T>> selectors = node.selectorsByName.putIfAbsent(
+        name, () => const Link());
+    // Run through the linked list of selectors with the same name. If
+    // we find one that matches, we update the value in the mapping.
+    for (Link link = selectors; !link.isEmpty; link = link.tail) {
+      SelectorValue<T> existing = link.head;
+      // It is safe to ignore the type here, because all selector
+      // mappings that are stored in a single node have the same type.
+      if (existing.selector.equalsUntyped(selector)) {
+        existing.value = value;
+        return;
       }
-      // We could not find an existing mapping for the selector, so
-      // we add a new one to the existing linked list.
-      SelectorValue<T> head = new SelectorValue<T>(selector, value);
-      node.selectorsByName[selector.name] = selectors.prepend(head);
     }
+    // We could not find an existing mapping for the selector, so
+    // we add a new one to the existing linked list.
+    SelectorValue<T> head = new SelectorValue<T>(selector, value);
+    node.selectorsByName[name] = selectors.prepend(head);
   }
 
   // TODO(kasperl): Share code with the [] operator?
@@ -72,13 +70,10 @@
   void visitMatching(Element member, bool visit(Selector selector, T value)) {
     assert(member.isMember());
     if (root == null) return;
-    // TODO(kasperl): For now, we use a different implementation for
-    // visiting if the tree contains interface subtypes.
-    if (containsInterfaceSubtypes) {
-      visitAllMatching(member, visit);
-    } else {
-      visitHierarchyMatching(member, visit);
-    }
+    // TODO(kasperl): Use visitHierachyMatching when possible. It is
+    // currently broken in subtle ways when it comes to finding typed
+    // selectors where we only know the interface of the receiver.
+    visitAllMatching(member, visit);
   }
 
   void visitAllMatching(Element member, bool visit(selector, value)) {
@@ -116,12 +111,9 @@
 }
 
 class SelectorMapNode<T> extends PartialTypeTreeNode {
-
-  final Map<SourceString, Link<SelectorValue<T>>> selectorsByName;
-
-  SelectorMapNode(ClassElement type) : super(type),
-      selectorsByName = new Map<SourceString, Link<SelectorValue<T>>>();
-
+  final Map<SourceString, Link<SelectorValue<T>>> selectorsByName =
+      new Map<SourceString, Link<SelectorValue<T>>>();
+  SelectorMapNode(ClassElement type) : super(type);
 }
 
 class SelectorValue<T> {
diff --git a/sdk/lib/_internal/compiler/implementation/universe/universe.dart b/sdk/lib/_internal/compiler/implementation/universe/universe.dart
index 6b64382..3840965 100644
--- a/sdk/lib/_internal/compiler/implementation/universe/universe.dart
+++ b/sdk/lib/_internal/compiler/implementation/universe/universe.dart
@@ -41,12 +41,12 @@
    * information. The resolver is too conservative when seeing a
    * getter and only registers an invoked getter.
    */
-  final Map<SourceString, Set<Selector>> fieldGetters;
+  final Set<Element> fieldGetters;
 
   /**
    * Fields set. See comment in [fieldGetters].
    */
-  final Map<SourceString, Set<Selector>> fieldSetters;
+  final Set<Element> fieldSetters;
   final Set<DartType> isChecks;
 
   Universe() : instantiatedClasses = new Set<ClassElement>(),
@@ -54,9 +54,9 @@
                staticFunctionsNeedingGetter = new Set<FunctionElement>(),
                invokedNames = new Map<SourceString, Set<Selector>>(),
                invokedGetters = new Map<SourceString, Set<Selector>>(),
-               fieldGetters = new Map<SourceString, Set<Selector>>(),
-               fieldSetters = new Map<SourceString, Set<Selector>>(),
                invokedSetters = new Map<SourceString, Set<Selector>>(),
+               fieldGetters = new Set<Element>(),
+               fieldSetters = new Set<Element>(),
                isChecks = new Set<DartType>();
 
   bool hasMatchingSelector(Set<Selector> selectors,
@@ -82,11 +82,11 @@
   }
 
   bool hasFieldGetter(Element member, Compiler compiler) {
-    return hasMatchingSelector(fieldGetters[member.name], member, compiler);
+    return fieldGetters.contains(member);
   }
 
   bool hasFieldSetter(Element member, Compiler compiler) {
-    return hasMatchingSelector(fieldSetters[member.name], member, compiler);
+    return fieldSetters.contains(member);
   }
 }
 
@@ -100,7 +100,31 @@
   static const SelectorKind OPERATOR = const SelectorKind('operator');
   static const SelectorKind INDEX = const SelectorKind('index');
 
-  toString() => name;
+  String toString() => name;
+}
+
+class TypedSelectorKind {
+  final String name;
+  const TypedSelectorKind(this.name);
+
+  /// Unknown type: used for untyped selector.
+  static const TypedSelectorKind UNKNOWN = const TypedSelectorKind('unknown');
+
+  // Exact type: the selector knows the exact type of the receiver.
+  // For example [:new Map():].
+  static const TypedSelectorKind EXACT = const TypedSelectorKind('exact');
+
+  // Subclass type: the receiver type is in a subclass hierarchy.
+  // For example [:this:].
+  static const TypedSelectorKind SUBCLASS = const TypedSelectorKind('subclass');
+
+  // Interface type: any type that implements the receiver type.
+  // For example [(foo as Foo).bar()], or any type annotation in
+  // checked mode.
+  static const TypedSelectorKind INTERFACE =
+      const TypedSelectorKind('interface');
+
+  String toString() => name;
 }
 
 class Selector {
@@ -112,6 +136,7 @@
   final int argumentCount;
   final List<SourceString> namedArguments;
   final List<SourceString> orderedNamedArguments;
+  TypedSelectorKind get typeKind => TypedSelectorKind.UNKNOWN;
 
   Selector(
       this.kind,
@@ -368,6 +393,7 @@
   bool equalsUntyped(Selector other) {
     return name == other.name
            && kind == other.kind
+           && typeKind == other.typeKind
            && identical(library, other.library)
            && argumentCount == other.argumentCount
            && namedArguments.length == other.namedArguments.length
@@ -401,7 +427,7 @@
     String named = '';
     String type = '';
     if (namedArgumentCount > 0) named = ', named=${namedArgumentsToString()}';
-    if (receiverType != null) type = ', type=$receiverType';
+    if (receiverType != null) type = ', type=$typeKind $receiverType';
     return 'Selector($kind, ${name.slowToString()}, '
            'arity=$argumentCount$named$type)';
   }
@@ -409,14 +435,14 @@
 
 class TypedSelector extends Selector {
   /**
-   * The type of the receiver. Any subtype of that type can be the
-   * target of the invocation.
+   * The type of the receiver.
    */
   final DartType receiverType;
+  final TypedSelectorKind typeKind;
 
   final Selector asUntyped;
 
-  TypedSelector(DartType this.receiverType, Selector selector)
+  TypedSelector(DartType this.receiverType, this.typeKind, Selector selector)
       : asUntyped = selector.asUntyped,
         super(selector.kind,
               selector.name,
@@ -426,8 +452,18 @@
     // Invariant: Typed selector can not be based on a malformed type.
     assert(!identical(receiverType.kind, TypeKind.MALFORMED_TYPE));
     assert(asUntyped.receiverType == null);
+    assert(typeKind != TypedSelectorKind.UNKNOWN);
   }
 
+  TypedSelector.subclass(DartType receiverType, Selector selector)
+      : this(receiverType, TypedSelectorKind.SUBCLASS, selector);
+
+  TypedSelector.exact(DartType receiverType, Selector selector)
+      : this(receiverType, TypedSelectorKind.EXACT, selector);
+
+  TypedSelector.subtype(DartType receiverType, Selector selector)
+      : this(receiverType, TypedSelectorKind.INTERFACE, selector);
+
   /**
    * Check if [element] will be the one used at runtime when being
    * invoked on an instance of [cls].
@@ -443,10 +479,11 @@
       if (element == field.getter || element == field.setter) {
         return true;
       } else {
-        ClassElement otherCls = field.getEnclosingClass();
         // We have not found a match, but another class higher in the
         // hierarchy may define the getter or the setter.
-        return hasElementIn(otherCls.superclass, element);
+        ClassElement otherCls = field.getEnclosingClass().superclass;
+        if (otherCls == null) return false;
+        return hasElementIn(otherCls, element);
       }
     }
     return false;
@@ -475,19 +512,27 @@
       return false;
     }
 
-    if (other.implementsInterface(self)
-        || other.isSubclassOf(self)
-        || compiler.world.hasAnySubclassThatImplements(other, receiverType)) {
-      return appliesUntyped(element, compiler);
-    }
-
-    // If [self] is a subclass of [other], it inherits the
-    // implementation of [element].
-    ClassElement cls = self;
-    if (cls.isSubclassOf(other)) {
-      // Resolve an invocation of [element.name] on [self]. If it
-      // is found, this selector is a candidate.
+    if (typeKind == TypedSelectorKind.EXACT) {
       return hasElementIn(self, element) && appliesUntyped(element, compiler);
+    } else if (typeKind == TypedSelectorKind.SUBCLASS) {
+      return (hasElementIn(self, element) || other.isSubclassOf(self))
+          && appliesUntyped(element, compiler);
+    } else {
+      assert(typeKind == TypedSelectorKind.INTERFACE);
+      if (other.implementsInterface(self)
+          || other.isSubclassOf(self)
+          || compiler.world.hasAnySubclassThatImplements(other, receiverType)) {
+        return appliesUntyped(element, compiler);
+      }
+
+      // If [self] is a subclass of [other], it inherits the
+      // implementation of [element].
+      ClassElement cls = self;
+      if (cls.isSubclassOf(other)) {
+        // Resolve an invocation of [element.name] on [self]. If it
+        // is found, this selector is a candidate.
+        return hasElementIn(self, element) && appliesUntyped(element, compiler);
+      }
     }
 
     return false;
diff --git a/sdk/lib/_internal/compiler/implementation/util/util.dart b/sdk/lib/_internal/compiler/implementation/util/util.dart
index 8a07687..0c94d40 100644
--- a/sdk/lib/_internal/compiler/implementation/util/util.dart
+++ b/sdk/lib/_internal/compiler/implementation/util/util.dart
@@ -58,7 +58,7 @@
 
   void writeEscapedOn(String string, var buffer) {
     for (int i = 0; i < string.length; i++) {
-      int code = string.charCodeAt(i);
+      int code = string.codeUnitAt(i);
       if (code == $DQ) {
         buffer.add(r'\"');
       } else if (code == $TAB) {
@@ -98,7 +98,7 @@
   }
 
   for (int i = 0; i < string.length; i++) {
-    int code = string.charCodeAt(i);
+    int code = string.codeUnitAt(i);
     if (code < 0x20 || code == $DEL || code == $DQ || code == $LS ||
         code == $PS || code == $BACKSLASH || code >= 0x80) {
       writeEscapedOn(string, buffer);
diff --git a/sdk/lib/_internal/compiler/implementation/world.dart b/sdk/lib/_internal/compiler/implementation/world.dart
index a40050e..4230708 100644
--- a/sdk/lib/_internal/compiler/implementation/world.dart
+++ b/sdk/lib/_internal/compiler/implementation/world.dart
@@ -91,10 +91,6 @@
     });
   }
 
-  bool needsRti(ClassElement cls) {
-    return classesNeedingRti.contains(cls) || compiler.enabledRuntimeType;
-  }
-
   void registerMixinUse(MixinApplicationElement mixinApplication,
                         ClassElement mixin) {
     Set<MixinApplicationElement> users =
@@ -103,6 +99,12 @@
     users.add(mixinApplication);
   }
 
+  bool isUsedAsMixin(ClassElement cls) {
+    Set<MixinApplicationElement> uses = mixinUses[cls];
+    return uses != null && !uses.isEmpty;
+  }
+
+
   void registerRtiDependency(Element element, Element dependency) {
     // We're not dealing with typedef for now.
     if (!element.isClass() || !dependency.isClass()) return;
@@ -111,6 +113,10 @@
     classes.add(dependency);
   }
 
+  bool needsRti(ClassElement cls) {
+    return classesNeedingRti.contains(cls) || compiler.enabledRuntimeType;
+  }
+
   void recordUserDefinedGetter(Element element) {
     assert(element.isGetter());
     userDefinedGetters.add(element);
diff --git a/sdk/lib/_internal/dartdoc/lib/dartdoc.dart b/sdk/lib/_internal/dartdoc/lib/dartdoc.dart
index e9882ff..7163496 100644
--- a/sdk/lib/_internal/dartdoc/lib/dartdoc.dart
+++ b/sdk/lib/_internal/dartdoc/lib/dartdoc.dart
@@ -143,6 +143,51 @@
   return completer.future;
 }
 
+/**
+ * Package manifest containing all information required to render the main page
+ * for a package.
+ *
+ * The manifest specifies where to load the [LibraryElement]s describing each
+ * library rather than including them directly.
+ * For our purposes we treat the core Dart libraries as a package.
+ */
+class PackageManifest {
+  /** Package name. */
+  final name;
+  /** Package description */
+  final description;
+  /** Libraries contained in this package. */
+  final List<String> libraries = <String>[];
+  /**
+   * Descriptive string describing the version# of the package.
+   *
+   * The current format for dart-sdk versions is
+   * $MAJOR.$MINOR.$BUILD.$PATCH$revisionString$userNameString
+   * For example: 0.1.2.0_r18233_johndoe
+   */
+  final String fullVersion;
+  /**
+   * Source control revision number for the package. For SVN this is a number
+   * while for GIT it is a hash.
+   */
+  final String revision;
+  /**
+   * Path to the directory containing data files for each library.
+   *
+   * Currently this is the serialized json version of the LibraryElement for
+   * the library. 
+   */
+  String location;
+  /**
+   * Packages depended on by this package.
+   * We currently store the entire manifest for the depency here the manifests
+   * are small.  We may want to change this to a reference in the future.
+   */
+  final List<PackageManifest> dependencies = <PackageManifest>[];
+
+  PackageManifest(this.name, this.description, this.fullVersion, this.revision);
+}
+
 class Dartdoc {
 
   /** Set to `false` to not include the source code in the generated docs. */
@@ -207,6 +252,9 @@
   /** Set this to inherit from Object. */
   bool inheritFromObject = false;
 
+  /** Version of the sdk or package docs are being generated for. */
+  String version;
+
   /** Set this to select the libraries to include in the documentation. */
   List<String> includedLibraries = const <String>[];
 
@@ -356,11 +404,39 @@
       generateAppCacheManifest();
     }
 
+    // TODO(jacobr): handle arbitrary pub packages rather than just the system
+    // libraries.
+    var revision = '0';
+    if (version != null) {
+      var match = new RegExp(r"_r(\d+)").firstMatch(version);
+      if (match != null) {
+        revision = match.group(1);
+      } else {
+        print("Warning: could not parse version: $version");
+      }
+    }
+    var packageManifest = new PackageManifest('dart:', 'Dart System Libraries',
+        version, revision);
+
+    for (final lib in _sortedLibraries) {
+      var libraryElement = new LibraryElement(lib.qualifiedName, lib)
+          ..stripDuplicateUris(null, null);
+      packageManifest.libraries.add(libraryElement.id);
+      startFile("$revision/${libraryElement.id}.json");
+
+      write(json_serializer.serialize(libraryElement));
+      endFile();
+    }
+
+    startFile("$revision/apidoc.json");
+    write(json_serializer.serialize(packageManifest));
+    endFile();
+
+    // Write out top level json file with a relative path to the library json
+    // files.
     startFile("apidoc.json");
-    var libraries = _sortedLibraries.map(
-        (lib) => new LibraryElement(lib.qualifiedName, lib))
-        .toList();
-    write(json_serializer.serialize(libraries));
+    packageManifest.location = revision;
+    write(json_serializer.serialize(packageManifest));
     endFile();
   }
 
diff --git a/sdk/lib/_internal/dartdoc/lib/src/json_serializer.dart b/sdk/lib/_internal/dartdoc/lib/src/json_serializer.dart
index bcfdf68..01e456e 100755
--- a/sdk/lib/_internal/dartdoc/lib/src/json_serializer.dart
+++ b/sdk/lib/_internal/dartdoc/lib/src/json_serializer.dart
@@ -114,7 +114,7 @@
   bool _inSet = false;
 
   bool _prettyPrint;
-  JsonPrinter({this._prettyPrint: true}) {
+  JsonPrinter({this._prettyPrint: false}) {
     _sb = new StringBuffer();
   }
 
diff --git a/sdk/lib/_internal/dartdoc/lib/universe_serializer.dart b/sdk/lib/_internal/dartdoc/lib/universe_serializer.dart
index d69b8a0..f2ffbcf 100755
--- a/sdk/lib/_internal/dartdoc/lib/universe_serializer.dart
+++ b/sdk/lib/_internal/dartdoc/lib/universe_serializer.dart
@@ -14,8 +14,18 @@
 import '../../libraries.dart';
 import 'dartdoc.dart';
 
+String _stripUri(String uri) {
+  String prefix = "/dart/";
+  int start = uri.indexOf(prefix);
+  if (start != -1) {
+    return uri.substring(start + prefix.length);
+  } else {
+    return uri;
+  }
+}
+
 /**
- * Base class for all nodes.
+ * Base class for all elements in the AST.
  */
 class Element {
   /** Human readable type name for the node. */
@@ -28,8 +38,22 @@
   final String comment;
   /** Children of the node. */
   List<Element> children;
+  /** Whether the element is private. */
+  final bool isPrivate;
 
-  Element(this.kind, this.name, this.id, this.comment);
+  /**
+   * Uri containing the definition of the element.
+   */
+  String uri;
+  /**
+   * Line in the original source file that starts the definition of the element.
+   */
+  String line;
+
+  Element(Mirror mirror, this.kind, this.name, this.id, this.comment)
+      : line = mirror.location.line.toString(),
+        isPrivate = _optionalBool(mirror.isPrivate),
+        uri = _stripUri(mirror.location.sourceUri.toString());
 
   void addChild(Element child) {
     if (children == null) {
@@ -37,6 +61,24 @@
     }
     children.add(child);
   }
+
+  /**
+   * Remove all URIs that exactly match the parent node's URI.
+   * This reduces output file size by about 20%.
+   */
+  void stripDuplicateUris(String parentUri, parentLine) {
+    if (children != null) {
+      for (var child in children) {
+        child.stripDuplicateUris(uri, line);
+      }
+    }
+    if (parentUri == uri) {
+      uri = null;
+    }
+    if (line == parentLine) {
+      line = null;
+    }
+  }
 }
 
 /**
@@ -45,12 +87,18 @@
  */
 bool _optionalBool(bool value) => value == true ? true : null;
 
+Reference _optionalReference(Mirror mirror) {
+  return (mirror != null && mirror.simpleName != "Dynamic_" &&
+      mirror.simpleName != "dynamic") ?
+        new Reference(mirror) : null;
+}
+
 /**
  * [Element] describing a Dart library.
  */
 class LibraryElement extends Element {
   LibraryElement(String name, LibraryMirror mirror)
-      : super('library', name, mirror.uri.toString(), computeComment(mirror)) {
+      : super(mirror, 'library', name, name, computeComment(mirror)) {
 
     mirror.functions.forEach((childName, childMirror) {
       addChild(new MethodElement(childName, childMirror));
@@ -82,22 +130,26 @@
 class ClassElement extends Element {
   /** Base class.*/
   final Reference superclass;
+  /** Whether the class is abstract. */
+  final bool isAbstract;
   /** Interfaces the class implements. */
   List<Reference> interfaces;
 
   ClassElement(String name, ClassMirror mirror)
-      : super('class', mirror.simpleName, name, computeComment(mirror)),
-        superclass = mirror.superclass != null ?
-            new Reference(mirror.superclass) : null {
+      : super(mirror, 'class', mirror.simpleName, name, computeComment(mirror)),
+        superclass = _optionalReference(mirror.superclass),
+        isAbstract = _optionalBool(mirror.isAbstract) {
     for (var interface in mirror.superinterfaces) {
       if (this.interfaces == null) {
         this.interfaces = <Reference>[];
       }
-      this.interfaces.add(new Reference(interface));
+      this.interfaces.add(_optionalReference(interface));
     }
 
     mirror.methods.forEach((childName, childMirror) {
-      addChild(new MethodElement(childName, childMirror));
+      if (!childMirror.isConstructor && !childMirror.isGetter) {
+        addChild(new MethodElement(childName, childMirror));
+      }
     });
 
     mirror.getters.forEach((childName, childMirror) {
@@ -111,6 +163,10 @@
     mirror.constructors.forEach((constructorName, methodMirror) {
       addChild(new MethodElement(constructorName, methodMirror, 'constructor'));
     });
+
+    for (var typeVariable in mirror.originalDeclaration.typeVariables) {
+      addChild(new TypeParameterElement(typeVariable));
+    }
   }
 }
 
@@ -123,9 +179,8 @@
   final bool isStatic;
 
   GetterElement(String name, MethodMirror mirror)
-      : super('property', mirror.simpleName, name, computeComment(mirror)),
-        ref = mirror.returnType != null ?
-            new Reference(mirror.returnType) : null,
+      : super(mirror, 'property', mirror.simpleName, name, computeComment(mirror)),
+        ref = _optionalReference(mirror.returnType),
         isStatic = _optionalBool(mirror.isStatic);
 }
 
@@ -140,10 +195,9 @@
   final bool isStatic;
 
   MethodElement(String name, MethodMirror mirror, [String kind = 'method'])
-      : super(kind, name, '$name${mirror.parameters.length}()',
+      : super(mirror, kind, name, '$name${mirror.parameters.length}()',
               computeComment(mirror)),
-        returnType = mirror.returnType != null ?
-            new Reference(mirror.returnType) : null,
+        returnType = _optionalReference(mirror.returnType),
         isSetter = _optionalBool(mirror.isSetter),
         isOperator = _optionalBool(mirror.isOperator),
         isStatic = _optionalBool(mirror.isStatic) {
@@ -164,32 +218,67 @@
   final bool isOptional;
 
   ParameterElement(ParameterMirror mirror)
-      : super('param', mirror.simpleName, mirror.simpleName, null),
-        ref = new Reference(mirror.type),
+      : super(mirror, 'param', mirror.simpleName, mirror.simpleName, null),
+        ref = _optionalReference(mirror.type),
         isOptional = _optionalBool(mirror.isOptional) {
   }
 }
 
 /**
+ * Element describing a generic type parameter.
+ */
+class TypeParameterElement extends Element {
+  /**
+   * Upper bound for the parameter.
+   * 
+   * In the following code sample, [:Bar:] is an upper bound:
+   * [: class Bar<T extends Foo> { } :]
+   */
+  Reference upperBound;
+
+  TypeParameterElement(TypeMirror mirror)
+      : super(mirror, 'typeparam', mirror.simpleName, mirror.simpleName, null),
+        upperBound = mirror.upperBound != null && !mirror.upperBound.isObject ?
+            new Reference(mirror.upperBound) : null;
+}
+
+/**
  * Element describing a variable.
  */
 class VariableElement extends Element {
   /** Type of the variable. */
   final Reference ref;
+  /** Whether the variable is static. */
   final bool isStatic;
+  /** Whether the variable is final. */
+  final bool isFinal;
 
   VariableElement(String name, VariableMirror mirror)
-      : super('property', mirror.simpleName, name, null),
-        ref = new Reference(mirror.type),
-        isStatic = _optionalBool(mirror.isStatic);
+      : super(mirror, 'variable', mirror.simpleName, name, null),
+        ref = _optionalReference(mirror.type),
+        isStatic = _optionalBool(mirror.isStatic),
+        isFinal = _optionalBool(mirror.isFinal);
 }
-// TODO(jacobr): this seems incomplete.
+
 /**
- * Element describing a typedef element.
+ * Element describing a typedef.
  */
+
 class TypedefElement extends Element {
+  /** Return type of the typedef. */
+  final Reference returnType;
+
   TypedefElement(String name, TypedefMirror mirror)
-      : super('typedef', mirror.simpleName, name, computeComment(mirror));
+      : super(mirror, 'typedef', mirror.simpleName, name,
+               computeComment(mirror)),
+        returnType = _optionalReference(mirror.value.returnType) {
+    for (var param in mirror.value.parameters) {
+      addChild(new ParameterElement(param));
+    }
+    for (var typeVariable in mirror.originalDeclaration.typeVariables) {
+      addChild(new TypeParameterElement(typeVariable));
+    }
+  }
 }
 
 /**
@@ -201,14 +290,13 @@
   List<Reference> arguments;
 
   Reference(Mirror mirror)
-      : name = mirror.simpleName,
+      : name = mirror.displayName,
         refId = getId(mirror) {
     if (mirror is ClassMirror) {
-      if (mirror is !TypedefMirror
-          && mirror.typeArguments.length > 0) {
+      if (mirror is !TypedefMirror && !mirror.typeArguments.isEmpty) {
         arguments = <Reference>[];
         for (var typeArg in mirror.typeArguments) {
-          arguments.add(new Reference(typeArg));
+          arguments.add(_optionalReference(typeArg));
         }
       }
     }
@@ -216,11 +304,8 @@
 
   static String getId(Mirror mirror) {
     String id = mirror.simpleName;
-    if (mirror is MemberMirror) {
-      MemberMirror memberMirror = mirror;
-      if (memberMirror.owner != null) {
-        id = '${getId(memberMirror.owner)}/$id';
-      }
+    if (mirror.owner != null) {
+      id = '${getId(mirror.owner)}/$id';
     }
     return id;
   }
diff --git a/sdk/lib/_internal/libraries.dart b/sdk/lib/_internal/libraries.dart
index 5c35483..8d5a548 100644
--- a/sdk/lib/_internal/libraries.dart
+++ b/sdk/lib/_internal/libraries.dart
@@ -27,11 +27,8 @@
       dart2jsPatchPath: "_internal/compiler/implementation/lib/async_patch.dart"),
 
   "chrome": const LibraryInfo(
-      "chrome/dartium/chrome_dartium.dart",
-      category: "Client",
-      dart2jsPath: "chrome/dart2js/chrome_dart2js.dart",
-      documented: false,
-      implementation: true), // Not really, just hiding it for now.
+      "chrome/dart2js/chrome_dart2js.dart",
+      category: "Client"),
 
   "collection": const LibraryInfo("collection/collection.dart"),
 
diff --git a/sdk/lib/async/async_error.dart b/sdk/lib/async/async_error.dart
index fc46e32..81a1e04 100644
--- a/sdk/lib/async/async_error.dart
+++ b/sdk/lib/async/async_error.dart
@@ -59,7 +59,7 @@
     }
 
     try {
-      new Timer(0, (_) {
+      Timer.run(() {
         reportError();
         // TODO(floitsch): we potentially want to call the global error handler
         // directly so that we can pass the stack trace.
diff --git a/sdk/lib/async/future.dart b/sdk/lib/async/future.dart
index a36ffa1..efa4144 100644
--- a/sdk/lib/async/future.dart
+++ b/sdk/lib/async/future.dart
@@ -42,7 +42,7 @@
  * this potential bug:
  *
  *     var future = getFuture();
- *     new Timer(5, (_) {
+ *     new Timer(new Duration(milliseconds: 5), () {
  *       // The error-handler is only attached 5ms after the future has been
  *       // received. If the future fails in the mean-time it will forward the
  *       // error to the global error-handler, even though there is code (just
diff --git a/sdk/lib/async/future_impl.dart b/sdk/lib/async/future_impl.dart
index 53ba3f3..8df3d4e 100644
--- a/sdk/lib/async/future_impl.dart
+++ b/sdk/lib/async/future_impl.dart
@@ -179,14 +179,14 @@
       _addListener(whenFuture);
     } else if (_hasValue) {
       T value = _resultOrListeners;
-      new Timer(0, (_) {
+      Timer.run(() {
         whenFuture._sendValue(value);
       });
     } else {
       assert(_hasError);
       _clearUnhandledError();
       AsyncError error = _resultOrListeners;
-      new Timer(0, (_) {
+      Timer.run(() {
         whenFuture._sendError(error);
       });
     }
@@ -198,7 +198,7 @@
     assert(_hasValue);
     _ThenFuture thenFuture = new _ThenFuture(onValue);
     T value = _resultOrListeners;
-    new Timer(0, (_) { thenFuture._sendValue(value); });
+    Timer.run(() { thenFuture._sendValue(value); });
     return thenFuture;
   }
 
@@ -208,7 +208,7 @@
     _clearUnhandledError();
     AsyncError error = _resultOrListeners;
     _CatchErrorFuture errorFuture = new _CatchErrorFuture(onError, test);
-    new Timer(0, (_) { errorFuture._sendError(error); });
+    Timer.run(() { errorFuture._sendError(error); });
     return errorFuture;
   }
 
@@ -248,7 +248,7 @@
     _state |= _UNHANDLED_ERROR;
     // Wait for the rest of the current event's duration to see
     // if a subscriber is added to handle the error.
-    new Timer(0, (_) {
+    Timer.run(() {
       if (_hasUnhandledError) {
         // No error handler has been added since the error was set.
         _clearUnhandledError();
diff --git a/sdk/lib/async/stream_impl.dart b/sdk/lib/async/stream_impl.dart
index caa6318..af68d43 100644
--- a/sdk/lib/async/stream_impl.dart
+++ b/sdk/lib/async/stream_impl.dart
@@ -1026,7 +1026,7 @@
 
   void schedule(_StreamImpl stream) {
     if (isScheduled) return;
-    scheduleTimer = new Timer(0, (_) {
+    scheduleTimer = Timer.run(() {
       scheduleTimer = null;
       stream._handlePendingEvents();
     });
@@ -1084,7 +1084,7 @@
 
   void _delayDone() {
     assert(_timer == null && _pauseCount == 0);
-    _timer = new Timer(0, (_) {
+    _timer = Timer.run(() {
       if (_handler != null) _handler();
     });
   }
diff --git a/sdk/lib/async/timer.dart b/sdk/lib/async/timer.dart
index e5b0516..39fe4c2 100644
--- a/sdk/lib/async/timer.dart
+++ b/sdk/lib/async/timer.dart
@@ -6,19 +6,56 @@
 
 abstract class Timer {
   /**
-   * Creates a new timer. The [callback] callback is invoked after
-   * [milliseconds] milliseconds.
+   * Creates a new timer.
+   *
+   * The [callback] callback is invoked after the given [duration]
+   * (a [Duration]) has passed. A negative duration is treated similar to
+   * a duration of 0.
+   *
+   * If the [duration] is statically known to be 0, consider using [run].
+   *
+   * Frequently the [duration] is either a constant or computed as in the
+   * following example (taking advantage of the multiplication operator of
+   * the Duration class):
+   *
+   *     const TIMEOUT = const Duration(seconds: 3);
+   *     const ms = const Duration(milliseconds: 1);
+   *
+   *     startTimeout([int milliseconds]) {
+   *       var duration = milliseconds == null ? TIMEOUT : ms * milliseconds;
+   *       return new Timer(duration, handleTimeout);
+   *     }
+   *
+   * *Deprecation warning*: this constructor used to take an [int] (the time
+   * in milliseconds) and a callback with one argument (the timer). This has
+   * changed to a [Duration] and a callback without arguments.
    */
-  external factory Timer(int milliseconds, void callback(Timer timer));
+  // TODO(floitsch): add types.
+  external factory Timer(var duration, Function callback);
 
   /**
-   * Creates a new repeating timer. The [callback] is invoked every
-   * [milliseconds] millisecond until cancelled.
+   * Creates a new repeating timer.
+   *
+   * The [callback] is invoked repeatedly with [duration] intervals until
+   * canceled. A negative duration is treated similar to a duration of 0.
+   *
+   * *Deprecation warning*: this constructor used to take an [int] (the time
+   * in milliseconds). This has changed to a [Duration].
    */
-  external factory Timer.repeating(int milliseconds,
+  external factory Timer.repeating(var duration,
                                    void callback(Timer timer));
 
   /**
+   * Runs the given [callback] asynchronously as soon as possible.
+   *
+   * Returns a [Timer] that can be cancelled if the callback is not necessary
+   * anymore.
+   */
+  static Timer run(void callback()) {
+    return new Timer(const Duration(), callback);
+  }
+
+  /**
    * Cancels the timer.
    */
   void cancel();
diff --git a/sdk/lib/chrome/dart2js/chrome_dart2js.dart b/sdk/lib/chrome/dart2js/chrome_dart2js.dart
index 7c982f4..ec23fde 100644
--- a/sdk/lib/chrome/dart2js/chrome_dart2js.dart
+++ b/sdk/lib/chrome/dart2js/chrome_dart2js.dart
@@ -1,8 +1,10 @@
 library chrome;
 
 import 'dart:_foreign_helper' show JS;
+import 'dart:_js_helper';
 import 'dart:html_common';
 import 'dart:html';
+
 // 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.
@@ -10,9 +12,18 @@
 // DO NOT EDIT
 // Auto-generated dart:chrome library.
 
+/// Native wrappers for the Chrome Packaged App APIs.
+///
+/// These functions allow direct access to the Packaged App APIs, allowing
+/// Chrome Packaged Apps to be written using Dart.
+///
+/// For more information on these APIs, see the Chrome.* APIs Documentation:
+///   http://developer.chrome.com/extensions/api_index.html
+
+/* TODO(sashab): Add "show convertDartClosureToJS" once 'show' works. */
 
 
-
+// Generated files below this line.
 // 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.
@@ -322,8 +333,8 @@
   /*
    * Members
    */
-  API_ChromeAppWindow window;
-  API_ChromeAppRuntime runtime;
+  API_app_window window;
+  API_app_runtime runtime;
 
   /*
    * Constructor
@@ -332,12 +343,12 @@
     var window_object = JS('', '#.window', this._jsObject);
     if (window_object == null)
       throw new UnsupportedError('Not supported by current browser.');
-    window = new API_ChromeAppWindow(window_object);
+    window = new API_app_window(window_object);
 
     var runtime_object = JS('', '#.runtime', this._jsObject);
     if (runtime_object == null)
       throw new UnsupportedError('Not supported by current browser.');
-    runtime = new API_ChromeAppRuntime(runtime_object);
+    runtime = new API_app_runtime(runtime_object);
   }
 }
 
@@ -374,305 +385,69 @@
 // 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.
 
-// from app_runtime.idl
+// Generated from namespace: app.window
+
 
 /**
  * Types
  */
 
-/// A WebIntents intent object. Deprecated.
-class Intent extends ChromeObject {
-  /*
-   * Public (Dart) constructor
-   */
-  Intent({String action, String type, var data}) {
-    this.action = action;
-    this.type = type;
-    this.data = data;
-  }
-
-  /*
-   * Private (JS) constructor
-   */
-  Intent._proxy(_jsObject) : super._proxy(_jsObject);
-
-  /*
-   * Public accessors
-   */
-
-  /// The WebIntent being invoked.
-  String get action => JS('String', '#.action', this._jsObject);
-
-  void set action(String action) {
-    JS('void', '#.action = #', this._jsObject, action);
-  }
-
-  /// The MIME type of the data.
-  String get type => JS('String', '#.type', this._jsObject);
-
-  void set type(String type) {
-    JS('void', '#.type = #', this._jsObject, type);
-  }
-
-  /// Data associated with the intent.
-  // TODO(sashab): What is the best way to serialize/return generic JS objects?
-  Object get data => JS('Object', '#.data', this._jsObject);
-
-  void set data(Object data) {
-    JS('void', '#.data = #', this._jsObject, convertArgument(data));
-  }
-
-  /*
-   * TODO(sashab): What is a NullCallback() type?
-   * Add postResult and postFailure once understanding what this type is, and
-   * once there is a way to pass functions back from JS.
-   */
-}
-
-class LaunchItem extends ChromeObject {
+class AppWindowCreateWindowOptions extends ChromeObject {
   /*
    * Public constructor
    */
-  LaunchItem({FileEntry entry, String type}) {
-    this.entry = entry;
-    this.type = type;
+  AppWindowCreateWindowOptions({String id, int defaultWidth, int defaultHeight, int defaultLeft, int defaultTop, int width, int height, int left, int top, int minWidth, int minHeight, int maxWidth, int maxHeight, String type, String frame, AppWindowBounds bounds, bool transparentBackground, bool hidden, bool singleton}) {
+    if (?id)
+      this.id = id;
+    if (?defaultWidth)
+      this.defaultWidth = defaultWidth;
+    if (?defaultHeight)
+      this.defaultHeight = defaultHeight;
+    if (?defaultLeft)
+      this.defaultLeft = defaultLeft;
+    if (?defaultTop)
+      this.defaultTop = defaultTop;
+    if (?width)
+      this.width = width;
+    if (?height)
+      this.height = height;
+    if (?left)
+      this.left = left;
+    if (?top)
+      this.top = top;
+    if (?minWidth)
+      this.minWidth = minWidth;
+    if (?minHeight)
+      this.minHeight = minHeight;
+    if (?maxWidth)
+      this.maxWidth = maxWidth;
+    if (?maxHeight)
+      this.maxHeight = maxHeight;
+    if (?type)
+      this.type = type;
+    if (?frame)
+      this.frame = frame;
+    if (?bounds)
+      this.bounds = bounds;
+    if (?transparentBackground)
+      this.transparentBackground = transparentBackground;
+    if (?hidden)
+      this.hidden = hidden;
+    if (?singleton)
+      this.singleton = singleton;
   }
 
   /*
    * Private constructor
    */
-  LaunchItem._proxy(_jsObject) : super._proxy(_jsObject);
+  AppWindowCreateWindowOptions._proxy(_jsObject) : super._proxy(_jsObject);
 
   /*
    * Public accessors
    */
-
-  /// FileEntry for the file.
-  FileEntry get entry => JS('FileEntry', '#.entry', this._jsObject);
-
-  void set entry(FileEntry entry) {
-    JS('void', '#.entry = #', this._jsObject, entry);
-  }
-
-  /// The MIME type of the file.
-  String get type => JS('String', '#.type', this._jsObject);
-
-  void set type(String type) {
-    JS('void', '#.type = #', this._jsObject, type);
-  }
-}
-
-/// Optional data for the launch.
-class LaunchData extends ChromeObject {
-  /*
-   * Public constructor
-   */
-  LaunchData({Intent intent, String id, List<LaunchItem> items}) {
-    this.intent = intent;
-    this.id = id;
-    this.items = items;
-  }
-
-  /*
-   * Private constructor
-   */
-  LaunchData._proxy(_jsObject) : super._proxy(_jsObject);
-
-  /*
-   * Public accessors
-   */
-  Intent get intent => new Intent._proxy(JS('', '#.intent', this._jsObject));
-
-  void set intent(Intent intent) {
-    JS('void', '#.intent = #', this._jsObject, convertArgument(intent));
-  }
-
-  /// The id of the file handler that the app is being invoked with.
-  String get id => JS('String', '#.id', this._jsObject);
-
-  void set id(String id) {
-    JS('void', '#.id = #', this._jsObject, id);
-  }
-
-  List<LaunchItem> get items() {
-    List<LaunchItem> items_final = new List<LaunchItem>();
-    for (var o in JS('List', '#.items', this._jsObject)) {
-      items_final.add(new LaunchItem._proxy(o));
-    }
-    return items_final;
-  }
-
-  void set items(List<LaunchItem> items) {
-    JS('void', '#.items = #', this._jsObject, convertArgument(items));
-  }
-}
-
-class IntentResponse extends ChromeObject {
-  /*
-   * Public constructor
-   */
-  IntentResponse({int intentId, bool success, Object data}) {
-    this.intentId = intentId;
-    this.success = success;
-    this.data = data;
-  }
-
-  /*
-   * Private constructor
-   */
-  IntentResponse._proxy(_jsObject) : super._proxy(_jsObject);
-
-  /*
-   * Public accessors
-   */
-
-  /// Identifies the intent.
-  int get intentId => JS('int', '#.intentId', this._jsObject);
-
-  void set intentId(int intentId) {
-    JS('void', '#.intentId = #', this._jsObject, intentId);
-  }
-
-  /// Was this intent successful? (i.e., postSuccess vs postFailure).
-  bool get success => JS('bool', '#.success', this._jsObject);
-
-  void set success(bool success) {
-    JS('void', '#.success = #', this._jsObject, success);
-  }
-
-  /// Data associated with the intent response.
-  // TODO(sashab): What's the best way to serialize/return generic JS objects?
-  Object get data => JS('Object', '#.data', this._jsObject);
-
-  void set data(Object data) {
-    JS('void', '#.data = #', this._jsObject, convertArgument(data));
-  }
-}
-
-/**
- * Events
- */
-
-/// Fired at Chrome startup to apps that were running when Chrome last shut
-/// down.
-class Event_ChromeAppRuntimeOnRestarted extends Event {
-  /*
-   * Override callback type definitions
-   */
-  void addListener(void callback())
-      => super.addListener(callback);
-  void removeListener(void callback())
-      => super.removeListener(callback);
-  bool hasListener(void callback())
-      => super.hasListener(callback);
-
-  /*
-   * Constructor
-   */
-  Event_ChromeAppRuntimeOnRestarted(jsObject) : super._(jsObject, 0);
-}
-
-/// Fired when an app is launched from the launcher or in response to a web
-/// intent.
-class Event_ChromeAppRuntimeOnLaunched extends Event {
-  /*
-   * Override callback type definitions
-   */
-  void addListener(void callback(LaunchData launchData))
-      => super.addListener(callback);
-  void removeListener(void callback(LaunchData launchData))
-      => super.removeListener(callback);
-  bool hasListener(void callback(LaunchData launchData))
-      => super.hasListener(callback);
-
-  /*
-   * Constructor
-   */
-  Event_ChromeAppRuntimeOnLaunched(jsObject) : super._(jsObject, 1);
-}
-
-/**
- * Functions
- */
-class API_ChromeAppRuntime {
-  /*
-   * API connection
-   */
-  Object _jsObject;
-
-  /*
-   * Events
-   */
-  Event_ChromeAppRuntimeOnRestarted onRestarted;
-  Event_ChromeAppRuntimeOnLaunched onLaunched;
-
-  /*
-   * Functions
-   */
-  void postIntentResponse(IntentResponse intentResponse) =>
-    JS('void', '#.postIntentResponse(#)', this._jsObject,
-        convertArgument(intentResponse));
-
-  /*
-   * Constructor
-   */
-  API_ChromeAppRuntime(this._jsObject) {
-    onRestarted = new Event_ChromeAppRuntimeOnRestarted(JS('Object',
-                                                           '#.onRestarted',
-                                                           this._jsObject));
-    onLaunched = new Event_ChromeAppRuntimeOnLaunched(JS('Object',
-                                                         '#.onLaunched',
-                                                         this._jsObject));
-  }
-}
-
-// 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.
-
-// from app.window.idl
-
-/**
- * Types
- */
-class CreateWindowOptions extends ChromeObject {
-  /*
-   * Public constructor
-   */
-  CreateWindowOptions({String id, int defaultWidth, int defaultHeight,
-      int defaultLeft, int defaultTop, int width, int height, int left, int top,
-      int minWidth, int minHeight, int maxWidth, int maxHeight, String type,
-      String frame, Bounds bounds, bool hidden}) {
-    this.id = id;
-    this.defaultWidth = defaultWidth;
-    this.defaultHeight = defaultHeight;
-    this.defaultLeft = defaultLeft;
-    this.defaultTop = defaultTop;
-    this.width = width;
-    this.height = height;
-    this.left = left;
-    this.top = top;
-    this.minWidth = minWidth;
-    this.minHeight = minHeight;
-    this.maxWidth = maxWidth;
-    this.maxHeight = maxHeight;
-    this.type = type;
-    this.frame = frame;
-    this.bounds = bounds;
-    this.hidden = hidden;
-  }
-
-  /*
-   * Private constructor
-   */
-  CreateWindowOptions._proxy(_jsObject) : super._proxy(_jsObject);
-
-  /*
-   * Public accessors
-   */
-  /// Id to identify the window. This will be used to remember the size
-  /// and position of the window and restore that geometry when a window
-  /// with the same id (and no explicit size or position) is later opened.
+  /// Id to identify the window. This will be used to remember the size and
+  /// position of the window and restore that geometry when a window with the
+  /// same id (and no explicit size or position) is later opened.
   String get id => JS('String', '#.id', this._jsObject);
 
   void set id(String id) {
@@ -739,7 +514,7 @@
     JS('void', '#.top = #', this._jsObject, top);
   }
 
-  /// Minimium width of the window.
+  /// Minimum width of the window.
   int get minWidth => JS('int', '#.minWidth', this._jsObject);
 
   void set minWidth(int minWidth) {
@@ -767,7 +542,8 @@
     JS('void', '#.maxHeight = #', this._jsObject, maxHeight);
   }
 
-  /// Window type: 'shell' (the default) is the only currently supported value.
+  /// Window type:  'shell' - the default window type  'panel' - a panel, managed
+  /// by the OS (Currently experimental, Ash only)
   String get type => JS('String', '#.type', this._jsObject);
 
   void set type(String type) {
@@ -781,41 +557,63 @@
     JS('void', '#.frame = #', this._jsObject, frame);
   }
 
-  /// Size of the content in the window (excluding the titlebar). If specified
-  /// in addition to any of the left/top/width/height parameters, this field
-  /// takes precedence. If a frameBounds is specified, the frameBounds take
-  /// precedence.
-  Bounds get bounds =>
-      new Bounds._proxy(JS('Bounds', '#.bounds', this._jsObject));
+  /// Size of the content in the window (excluding the titlebar). If specified in
+  /// addition to any of the left/top/width/height parameters, this field takes
+  /// precedence. If a frameBounds is specified, the frameBounds take precedence.
+  AppWindowBounds get bounds => new AppWindowBounds._proxy(JS('', '#.bounds', this._jsObject));
 
-  void set bounds(Bounds bounds) {
+  void set bounds(AppWindowBounds bounds) {
     JS('void', '#.bounds = #', this._jsObject, convertArgument(bounds));
   }
 
-  /// If true, the window will be created in a hidden state. Call show() on
-  /// the window to show it once it has been created. Defaults to false.
+  /// Enable window background transparency. Only supported in ash. Requires
+  /// experimental API permission.
+  bool get transparentBackground => JS('bool', '#.transparentBackground', this._jsObject);
+
+  void set transparentBackground(bool transparentBackground) {
+    JS('void', '#.transparentBackground = #', this._jsObject, transparentBackground);
+  }
+
+  /// If true, the window will be created in a hidden state. Call show() on the
+  /// window to show it once it has been created. Defaults to false.
   bool get hidden => JS('bool', '#.hidden', this._jsObject);
 
   void set hidden(bool hidden) {
     JS('void', '#.hidden = #', this._jsObject, hidden);
   }
+
+  /// By default if you specify an id for the window, the window will only be
+  /// created if another window with the same id doesn't already exist. If a
+  /// window with the same id already exists that window is activated instead. If
+  /// you do want to create multiple windows with the same id, you can set this
+  /// property to false.
+  bool get singleton => JS('bool', '#.singleton', this._jsObject);
+
+  void set singleton(bool singleton) {
+    JS('void', '#.singleton = #', this._jsObject, singleton);
+  }
+
 }
 
-class Bounds extends ChromeObject {
+class AppWindowBounds extends ChromeObject {
   /*
    * Public constructor
    */
-  Bounds({int left, int top, int width, int height}) {
-    this.left = left;
-    this.top = top;
-    this.width = width;
-    this.height = height;
+  AppWindowBounds({int left, int top, int width, int height}) {
+    if (?left)
+      this.left = left;
+    if (?top)
+      this.top = top;
+    if (?width)
+      this.width = width;
+    if (?height)
+      this.height = height;
   }
 
   /*
    * Private constructor
    */
-  Bounds._proxy(_jsObject) : super._proxy(_jsObject);
+  AppWindowBounds._proxy(_jsObject) : super._proxy(_jsObject);
 
   /*
    * Public accessors
@@ -843,144 +641,484 @@
   void set height(int height) {
     JS('void', '#.height = #', this._jsObject, height);
   }
+
 }
 
-class AppWindow extends ChromeObject {
-  /*
-   * Public constructor
-   * TODO(sashab): Does it make sense to be able to create a new AppWindow this
-   * way?
-   */
-  //AppWindow();
-
+class AppWindowAppWindow extends ChromeObject {
   /*
    * Private constructor
    */
-  AppWindow._proxy(jsObject) : super._proxy(jsObject);
+  AppWindowAppWindow._proxy(_jsObject) : super._proxy(_jsObject);
 
   /*
    * Public accessors
    */
   /// The JavaScript 'window' object for the created child.
+  // 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.
+
   // TODO(sashab, sra): Detect whether this is the current window, or an
   // external one, and return an appropriately-typed object
   WindowBase get contentWindow =>
-      JS("Window", "#.contentWindow", this._jsObject);
+    JS("Window", "#.contentWindow", this._jsObject);
 
   /*
-   * Functions
+   * Methods
    */
-
   /// Focus the window.
-  void focus() => JS("void", "#.focus()", this._jsObject);
+  void focus() => JS('void', '#.focus()', this._jsObject);
 
   /// Minimize the window.
-  void minimize() => JS("void", "#.minimize()", this._jsObject);
+  void minimize() => JS('void', '#.minimize()', this._jsObject);
 
   /// Is the window minimized?
-  bool isMinimized() => JS("bool", "#.isMinimized()", this._jsObject);
+  void isMinimized() => JS('void', '#.isMinimized()', this._jsObject);
 
   /// Maximize the window.
-  void maximize() => JS("void", "#.maximize()", this._jsObject);
+  void maximize() => JS('void', '#.maximize()', this._jsObject);
 
   /// Is the window maximized?
-  bool isMaximized() => JS("bool", "c#.isMaximized()", this._jsObject);
+  void isMaximized() => JS('void', '#.isMaximized()', this._jsObject);
 
   /// Restore the window.
-  void restore() => JS("void", "#.restore()", this._jsObject);
+  void restore() => JS('void', '#.restore()', this._jsObject);
 
   /// Move the window to the position (|left|, |top|).
-  void moveTo(int left, int top) =>
-      JS("void", "#.moveTo(#, #)", this._jsObject, left, top);
+  void moveTo(int left, int top) => JS('void', '#.moveTo(#, #)', this._jsObject, left, top);
 
   /// Resize the window to |width|x|height| pixels in size.
-  void resizeTo(int width, int height) =>
-      JS("void", "#.resizeTo(#, #)", this._jsObject, width, height);
+  void resizeTo(int width, int height) => JS('void', '#.resizeTo(#, #)', this._jsObject, width, height);
 
   /// Draw attention to the window.
-  void drawAttention() => JS("void", "#.drawAttention()", this._jsObject);
+  void drawAttention() => JS('void', '#.drawAttention()', this._jsObject);
 
   /// Clear attention to the window.
-  void clearAttention() => JS("void", "#.clearAttention()", this._jsObject);
+  void clearAttention() => JS('void', '#.clearAttention()', this._jsObject);
 
   /// Close the window.
-  void close() => JS("void", "#.close()", this._jsObject);
+  void close() => JS('void', '#.close()', this._jsObject);
 
   /// Show the window. Does nothing if the window is already visible.
-  void show() => JS("void", "#.show()", this._jsObject);
+  void show() => JS('void', '#.show()', this._jsObject);
 
   /// Hide the window. Does nothing if the window is already hidden.
-  void hide() => JS("void", "#.hide()", this._jsObject);
+  void hide() => JS('void', '#.hide()', this._jsObject);
+
+  /// Get the window's bounds as a $ref:Bounds object.
+  // 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.
+
+  // TODO(sashab, kalman): Fix IDL parser to read function return values
+  // correctly. Currently, it just reads void for all functions.
+  AppWindowBounds getBounds() =>
+      new AppWindowBounds._proxy(JS('void', '#.getBounds()', this._jsObject));
 
   /// Set the window's bounds.
-  void setBounds(Bounds bounds) =>
-      JS("void", "#.setBounds(#)", this._jsObject, convertArgument(bounds));
+  void setBounds(AppWindowBounds bounds) => JS('void', '#.setBounds(#)', this._jsObject, convertArgument(bounds));
 
+  /// Set the app icon for the window (experimental). Currently this is only
+  /// being implemented on Ash. TODO(stevenjb): Investigate implementing this on
+  /// Windows and OSX.
+  void setIcon(String icon_url) => JS('void', '#.setIcon(#)', this._jsObject, icon_url);
+
+}
+
+/**
+ * Events
+ */
+
+/// Fired when the window is resized.
+class Event_app_window_onBoundsChanged extends Event {
+  void addListener(void callback()) => super.addListener(callback);
+
+  void removeListener(void callback()) => super.removeListener(callback);
+
+  bool hasListener(void callback()) => super.hasListener(callback);
+
+  Event_app_window_onBoundsChanged(jsObject) : super._(jsObject, 0);
+}
+
+/// Fired when the window is closed.
+class Event_app_window_onClosed extends Event {
+  void addListener(void callback()) => super.addListener(callback);
+
+  void removeListener(void callback()) => super.removeListener(callback);
+
+  bool hasListener(void callback()) => super.hasListener(callback);
+
+  Event_app_window_onClosed(jsObject) : super._(jsObject, 0);
+}
+
+/// Fired when the window is maximized.
+class Event_app_window_onMaximized extends Event {
+  void addListener(void callback()) => super.addListener(callback);
+
+  void removeListener(void callback()) => super.removeListener(callback);
+
+  bool hasListener(void callback()) => super.hasListener(callback);
+
+  Event_app_window_onMaximized(jsObject) : super._(jsObject, 0);
+}
+
+/// Fired when the window is minimized.
+class Event_app_window_onMinimized extends Event {
+  void addListener(void callback()) => super.addListener(callback);
+
+  void removeListener(void callback()) => super.removeListener(callback);
+
+  bool hasListener(void callback()) => super.hasListener(callback);
+
+  Event_app_window_onMinimized(jsObject) : super._(jsObject, 0);
+}
+
+/// Fired when the window is restored from being minimized or maximized.
+class Event_app_window_onRestored extends Event {
+  void addListener(void callback()) => super.addListener(callback);
+
+  void removeListener(void callback()) => super.removeListener(callback);
+
+  bool hasListener(void callback()) => super.hasListener(callback);
+
+  Event_app_window_onRestored(jsObject) : super._(jsObject, 0);
 }
 
 /**
  * Functions
  */
-class API_ChromeAppWindow {
-  /**
-   * JS object
+
+class API_app_window {
+  /*
+   * API connection
    */
   Object _jsObject;
 
-  /**
-   * Constructor
+  /*
+   * Events
    */
-  API_ChromeAppWindow(this._jsObject);
+  Event_app_window_onBoundsChanged onBoundsChanged;
+  Event_app_window_onClosed onClosed;
+  Event_app_window_onMaximized onMaximized;
+  Event_app_window_onMinimized onMinimized;
+  Event_app_window_onRestored onRestored;
 
-  /**
+  /*
    * Functions
    */
+  /// The size and position of a window can be specified in a number of different
+  /// ways. The most simple option is not specifying anything at all, in which
+  /// case a default size and platform dependent position will be used.<br/><br/>
+  /// Another option is to use the top/left and width/height properties, which
+  /// will always put the window at the specified coordinates with the specified
+  /// size.<br/><br/> Yet another option is to give the window a (unique) id.
+  /// This id is then used to remember the size and position of the window
+  /// whenever it is moved or resized. This size and position is then used
+  /// instead of the specified bounds on subsequent opening of a window with the
+  /// same id. If you need to open a window with an id at a location other than
+  /// the remembered default, you can create it hidden, move it to the desired
+  /// location, then show it.<br/><br/> You can also combine these various
+  /// options, explicitly specifying for example the size while having the
+  /// position be remembered or other combinations like that. Size and position
+  /// are dealt with seperately, but individual coordinates are not. So if you
+  /// specify a top (or left) coordinate, you should also specify a left (or top)
+  /// coordinate, and similar for size.<br/><br/> If you specify both a regular
+  /// and a default value for the same option the regular value is the only one
+  /// that takes effect.
+  // 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.
 
-  /// Returns an <a href="#type-AppWindow">AppWindow</a> object for the
-  /// current script context (ie JavaScript 'window' object). This can also be
-  /// called on a handle to a script context for another page, for example:
-  /// otherWindow.chrome.app.window.current().
-  AppWindow current() =>
-      new AppWindow._proxy(JS("Object", "#.current()", this._jsObject));
-
-  /// The size and position of a window can be specified in a number of
-  /// different ways. The most simple option is not specifying anything at
-  /// all, in which case a default size and platform dependent position will
-  /// be used.
-  ///
-  /// Another option is to use the top/left and width/height properties,
-  /// which will always put the window at the specified coordinates with the
-  /// specified size.
-  ///
-  /// Yet another option is to give the window a (unique) id. This id is then
-  /// used to remember the size and position of the window whenever it is
-  /// moved or resized. This size and position is then used instead of the
-  /// specified bounds on subsequent opening of a window with the same id. If
-  /// you need to open a window with an id at a location other than the
-  /// remembered default, you can create it hidden, move it to the desired
-  /// location, then show it.
-  ///
-  /// You can also combine these various options, explicitly specifying for
-  /// example the size while having the position be remembered or other
-  /// combinations like that. Size and position are dealt with seperately,
-  /// but individual coordinates are not. So if you specify a top (or left)
-  /// coordinate, you should also specify a left (or top) coordinate, and
-  /// similar for size.
-  ///
-  /// If you specify both a regular and a default value for the same option
-  /// the regular value is the only one that takes effect.
-  void create(String url, [CreateWindowOptions options,
-                           void callback(AppWindow created_window)]) {
-    void __proxy_callback(Object created_window) {
+  // TODO(sashab): This override is no longer needed once prefixes are removed.
+  void create(String url,
+              [AppWindowCreateWindowOptions options,
+              void callback(AppWindowAppWindow created_window)]) {
+    void __proxy_callback(created_window) {
       if (?callback)
-        callback(new AppWindow._proxy(created_window));
+        callback(new AppWindowAppWindow._proxy(created_window));
     }
-
-    JS("void", "#.create(#, #, #)",
-       this._jsObject,
-       url,
-       convertArgument(options),
-       convertDartClosureToJS(__proxy_callback, 1)
-    );
+    JS('void', '#.create(#, #, #)', this._jsObject, url, convertArgument(options),
+       convertDartClosureToJS(__proxy_callback, 1));
   }
-}
\ No newline at end of file
+
+  /// Returns an $ref:AppWindow object for the current script context (ie
+  /// JavaScript 'window' object). This can also be called on a handle to a
+  /// script context for another page, for example:
+  /// otherWindow.chrome.app.window.current().
+  // 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.
+
+  // TODO(sashab, kalman): Fix IDL parser to read function return values
+  // correctly. Currently, it just reads void for all functions.
+  AppWindowAppWindow current() =>
+      new AppWindowAppWindow._proxy(JS('void', '#.current()', this._jsObject));
+
+  void initializeAppWindow(Object state) => JS('void', '#.initializeAppWindow(#)', this._jsObject, convertArgument(state));
+
+  API_app_window(this._jsObject) {
+    onBoundsChanged = new Event_app_window_onBoundsChanged(JS('', '#.onBoundsChanged', this._jsObject));
+    onClosed = new Event_app_window_onClosed(JS('', '#.onClosed', this._jsObject));
+    onMaximized = new Event_app_window_onMaximized(JS('', '#.onMaximized', this._jsObject));
+    onMinimized = new Event_app_window_onMinimized(JS('', '#.onMinimized', this._jsObject));
+    onRestored = new Event_app_window_onRestored(JS('', '#.onRestored', this._jsObject));
+  }
+}
+// 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.
+
+// Generated from namespace: app.runtime
+
+
+/**
+ * Types
+ */
+
+class AppRuntimeIntent extends ChromeObject {
+  /*
+   * Private constructor
+   */
+  AppRuntimeIntent._proxy(_jsObject) : super._proxy(_jsObject);
+
+  /*
+   * Public accessors
+   */
+  /// The WebIntent being invoked.
+  String get action => JS('String', '#.action', this._jsObject);
+
+  void set action(String action) {
+    JS('void', '#.action = #', this._jsObject, action);
+  }
+
+  /// The MIME type of the data.
+  String get type => JS('String', '#.type', this._jsObject);
+
+  void set type(String type) {
+    JS('void', '#.type = #', this._jsObject, type);
+  }
+
+  /// Data associated with the intent.
+  Object get data => JS('Object', '#.data', this._jsObject);
+
+  void set data(Object data) {
+    JS('void', '#.data = #', this._jsObject, convertArgument(data));
+  }
+
+
+  /*
+   * Methods
+   */
+  /// Callback to be compatible with WebIntents.
+  void postResult() => JS('void', '#.postResult()', this._jsObject);
+
+  /// Callback to be compatible with WebIntents.
+  void postFailure() => JS('void', '#.postFailure()', this._jsObject);
+
+}
+
+class AppRuntimeLaunchItem extends ChromeObject {
+  /*
+   * Public constructor
+   */
+  AppRuntimeLaunchItem({FileEntry entry, String type}) {
+    if (?entry)
+      this.entry = entry;
+    if (?type)
+      this.type = type;
+  }
+
+  /*
+   * Private constructor
+   */
+  AppRuntimeLaunchItem._proxy(_jsObject) : super._proxy(_jsObject);
+
+  /*
+   * Public accessors
+   */
+  /// FileEntry for the file.
+  FileEntry get entry => JS('FileEntry', '#.entry', this._jsObject);
+
+  void set entry(FileEntry entry) {
+    JS('void', '#.entry = #', this._jsObject, convertArgument(entry));
+  }
+
+  /// The MIME type of the file.
+  String get type => JS('String', '#.type', this._jsObject);
+
+  void set type(String type) {
+    JS('void', '#.type = #', this._jsObject, type);
+  }
+
+}
+
+class AppRuntimeLaunchData extends ChromeObject {
+  /*
+   * Public constructor
+   */
+  AppRuntimeLaunchData({AppRuntimeIntent intent, String id, List<AppRuntimeLaunchItem> items}) {
+    if (?intent)
+      this.intent = intent;
+    if (?id)
+      this.id = id;
+    if (?items)
+      this.items = items;
+  }
+
+  /*
+   * Private constructor
+   */
+  AppRuntimeLaunchData._proxy(_jsObject) : super._proxy(_jsObject);
+
+  /*
+   * Public accessors
+   */
+  AppRuntimeIntent get intent => new AppRuntimeIntent._proxy(JS('', '#.intent', this._jsObject));
+
+  void set intent(AppRuntimeIntent intent) {
+    JS('void', '#.intent = #', this._jsObject, convertArgument(intent));
+  }
+
+  /// The id of the file handler that the app is being invoked with.
+  String get id => JS('String', '#.id', this._jsObject);
+
+  void set id(String id) {
+    JS('void', '#.id = #', this._jsObject, id);
+  }
+
+  List<AppRuntimeLaunchItem> get items {
+    List<AppRuntimeLaunchItem> __proxy_items = new List<AppRuntimeLaunchItem>();
+    for (var o in JS('List', '#.items', this._jsObject)) {
+      __proxy_items.add(new AppRuntimeLaunchItem._proxy(o));
+    }
+    return __proxy_items;
+  }
+
+  void set items(List<AppRuntimeLaunchItem> items) {
+    JS('void', '#.items = #', this._jsObject, convertArgument(items));
+  }
+
+}
+
+class AppRuntimeIntentResponse extends ChromeObject {
+  /*
+   * Public constructor
+   */
+  AppRuntimeIntentResponse({int intentId, bool success, Object data}) {
+    if (?intentId)
+      this.intentId = intentId;
+    if (?success)
+      this.success = success;
+    if (?data)
+      this.data = data;
+  }
+
+  /*
+   * Private constructor
+   */
+  AppRuntimeIntentResponse._proxy(_jsObject) : super._proxy(_jsObject);
+
+  /*
+   * Public accessors
+   */
+  /// Identifies the intent.
+  int get intentId => JS('int', '#.intentId', this._jsObject);
+
+  void set intentId(int intentId) {
+    JS('void', '#.intentId = #', this._jsObject, intentId);
+  }
+
+  /// Was this intent successful? (i.e., postSuccess vs postFailure).
+  bool get success => JS('bool', '#.success', this._jsObject);
+
+  void set success(bool success) {
+    JS('void', '#.success = #', this._jsObject, success);
+  }
+
+  /// Data associated with the intent response.
+  Object get data => JS('Object', '#.data', this._jsObject);
+
+  void set data(Object data) {
+    JS('void', '#.data = #', this._jsObject, convertArgument(data));
+  }
+
+}
+
+/**
+ * Events
+ */
+
+/// Fired when an app is launched from the launcher or in response to a web
+/// intent.
+class Event_app_runtime_onLaunched extends Event {
+  void addListener(void callback(AppRuntimeLaunchData launchData)) {
+    void __proxy_callback(launchData) {
+      if (?callback) {
+        callback(new AppRuntimeLaunchData._proxy(launchData));
+      }
+    }
+    super.addListener(callback);
+  }
+
+  void removeListener(void callback(AppRuntimeLaunchData launchData)) {
+    void __proxy_callback(launchData) {
+      if (?callback) {
+        callback(new AppRuntimeLaunchData._proxy(launchData));
+      }
+    }
+    super.removeListener(callback);
+  }
+
+  bool hasListener(void callback(AppRuntimeLaunchData launchData)) {
+    void __proxy_callback(launchData) {
+      if (?callback) {
+        callback(new AppRuntimeLaunchData._proxy(launchData));
+      }
+    }
+    super.hasListener(callback);
+  }
+
+  Event_app_runtime_onLaunched(jsObject) : super._(jsObject, 1);
+}
+
+/// Fired at Chrome startup to apps that were running when Chrome last shut
+/// down.
+class Event_app_runtime_onRestarted extends Event {
+  void addListener(void callback()) => super.addListener(callback);
+
+  void removeListener(void callback()) => super.removeListener(callback);
+
+  bool hasListener(void callback()) => super.hasListener(callback);
+
+  Event_app_runtime_onRestarted(jsObject) : super._(jsObject, 0);
+}
+
+/**
+ * Functions
+ */
+
+class API_app_runtime {
+  /*
+   * API connection
+   */
+  Object _jsObject;
+
+  /*
+   * Events
+   */
+  Event_app_runtime_onLaunched onLaunched;
+  Event_app_runtime_onRestarted onRestarted;
+
+  /*
+   * Functions
+   */
+  /// postIntentResponse is an internal method to responds to an intent
+  /// previously sent to a packaged app. This is identified by intentId, and
+  /// should only be invoked at most once per intentId.
+  void postIntentResponse(AppRuntimeIntentResponse intentResponse) => JS('void', '#.postIntentResponse(#)', this._jsObject, convertArgument(intentResponse));
+
+  API_app_runtime(this._jsObject) {
+    onLaunched = new Event_app_runtime_onLaunched(JS('', '#.onLaunched', this._jsObject));
+    onRestarted = new Event_app_runtime_onRestarted(JS('', '#.onRestarted', this._jsObject));
+  }
+}
diff --git a/sdk/lib/collection/queue.dart b/sdk/lib/collection/queue.dart
index 87c6fb7..13ec536 100644
--- a/sdk/lib/collection/queue.dart
+++ b/sdk/lib/collection/queue.dart
@@ -9,18 +9,18 @@
  * can iterate over the elements of a queue through [forEach] or with
  * an [Iterator].
  */
-abstract class Queue<E> extends Collection<E> {
+abstract class Queue<E> implements Collection<E> {
 
   /**
    * Creates a queue.
    */
-  factory Queue() => new DoubleLinkedQueue<E>();
+  factory Queue() => new ListQueue<E>();
 
   /**
    * Creates a queue with the elements of [other]. The order in
    * the queue will be the order provided by the iterator of [other].
    */
-  factory Queue.from(Iterable<E> other) => new DoubleLinkedQueue<E>.from(other);
+  factory Queue.from(Iterable<E> other) => new ListQueue<E>.from(other);
 
   /**
    * Removes and returns the first element of this queue. Throws an
@@ -154,11 +154,11 @@
 }
 
 /**
- * Implementation of a double linked list that box list elements into
- * DoubleLinkedQueueEntry objects.
+ * A [Queue] implementation based on a double-linked list.
  *
- * WARNING: This class is temporary located in dart:core. It'll be removed
- * at some point in the near future.
+ * Allows constant time add, remove-at-ends and peek operations.
+ *
+ * Can do [removeAll] and [retainAll] in linear time.
  */
 class DoubleLinkedQueue<E> extends Collection<E> implements Queue<E> {
   _DoubleLinkedQueueEntrySentinel<E> _sentinel;
@@ -317,3 +317,380 @@
 
   E get current => _current;
 }
+
+/**
+ * List based [Queue].
+ *
+ * Keeps a cyclic buffer of elements, and grows to a larger buffer when
+ * it fills up. This guarantees constant time peek and remove operations, and
+ * amortized constant time add operations.
+ *
+ * The structure is efficient for any queue or stack usage.
+ *
+ * Collection operations like [removeAll] and [removeMatching] are very
+ * inefficient. If those are needed, use a [DoubleLinkedQueue] instead.
+ */
+class ListQueue<E> extends Collection<E> implements Queue<E>{
+  static const int _INITIAL_CAPACITY = 8;
+  List<E> _table;
+  int _head;
+  int _tail;
+  int _modificationCount = 0;
+
+  /**
+   * Create an empty queue.
+   *
+   * If [initialCapacity] is given, prepare the queue for at least that many
+   * elements.
+   */
+  ListQueue([int initialCapacity]) : _head = 0, _tail = 0 {
+    if (initialCapacity == null || initialCapacity < _INITIAL_CAPACITY) {
+      initialCapacity = _INITIAL_CAPACITY;
+    } else if (!_isPowerOf2(initialCapacity)) {
+      initialCapacity = _nextPowerOf2(initialCapacity);
+    }
+    assert(_isPowerOf2(initialCapacity));
+    _table = new List<E>.fixedLength(initialCapacity);
+  }
+
+  /**
+   * Create a queue initially containing the elements of [source].
+   */
+  factory ListQueue.from(Iterable<E> source) {
+    if (source is List) {
+      int length = source.length;
+      ListQueue<E> queue = new ListQueue(length + 1);
+      assert(queue._table.length > length);
+      List sourceList = source;
+      queue._table.setRange(0, length, sourceList, 0);
+      queue._tail = length;
+      return queue;
+    } else {
+      return new ListQueue<E>()..addAll(source);
+    }
+  }
+
+  // Iterable interface.
+
+  Iterator<E> get iterator => new _ListQueueIterator(this);
+
+  void forEach(void action (E element)) {
+    int modificationCount = _modificationCount;
+    for (int i = _head; i != _tail; i = (i + 1) & (_table.length - 1)) {
+      action(_table[i]);
+      _checkModification(modificationCount);
+    }
+  }
+
+  bool get isEmpty => _head == _tail;
+
+  int get length => (_tail - _head) & (_table.length - 1);
+
+  E get first {
+    if (_head == _tail) throw new StateError("No elements");
+    return _table[_head];
+  }
+
+  E get last {
+    if (_head == _tail) throw new StateError("No elements");
+    return _table[(_tail - 1) & (_table.length - 1)];
+  }
+
+  E get single {
+    if (_head == _tail) throw new StateError("No elements");
+    if (length > 1) throw new StateError("Too many elements");
+    return _table[_head];
+  }
+
+  E elementAt(int index) {
+    if (index < 0 || index > length) {
+      throw new RangeError.range(index, 0, length);
+    }
+    return _table[(_head + index) & (_table.length - 1)];
+  }
+
+  List<E> toList() {
+    List<E> list = new List<E>(length);
+    _writeToList(list);
+    return list;
+  }
+
+  // Collection interface.
+
+  void add(E element) {
+    _add(element);
+  }
+
+  void addAll(Iterable<E> elements) {
+    if (elements is List) {
+      List list = elements;
+      int addCount = list.length;
+      int length = this.length;
+      if (length + addCount >= _table.length) {
+        _preGrow(length + addCount);
+        // After preGrow, all elements are at the start of the list.
+        _table.setRange(length, addCount, list, 0);
+        _tail += addCount;
+      } else {
+        // Adding addCount elements won't reach _head.
+        int endSpace = _table.length - _tail;
+        if (addCount < endSpace) {
+          _table.setRange(_tail, addCount, list, 0);
+          _tail += addCount;
+        } else {
+          int preSpace = addCount - endSpace;
+          _table.setRange(_tail, endSpace, list, 0);
+          _table.setRange(0, preSpace, list, endSpace);
+          _tail = preSpace;
+        }
+      }
+      _modificationCount++;
+    } else {
+      for (E element in elements) _add(element);
+    }
+  }
+
+  void remove(Object object) {
+    for (int i = _head; i != _tail; i = (i + 1) & (_table.length - 1)) {
+      E element = _table[i];
+      if (element == object) {
+        _remove(i);
+        return;
+      }
+    }
+    _modificationCount++;
+  }
+
+  void removeAll(Iterable objectsToRemove) {
+    IterableMixinWorkaround.removeAllList(this, objectsToRemove);
+  }
+
+  void retainAll(Iterable objectsToRetain) {
+    IterableMixinWorkaround.retainAll(this, objectsToRetain);
+  }
+
+  void _filterMatching(bool test(E element), bool removeMatching) {
+    int index = _head;
+    int modificationCount = _modificationCount;
+    int i = _head;
+    while (i != _tail) {
+      E element = _table[i];
+      bool remove = (test(element) == removeMatching);
+      _checkModification(modificationCount);
+      if (remove) {
+        i = _remove(i);
+        modificationCount = ++_modificationCount;
+      } else {
+        i = (i + 1) & (_table.length - 1);
+      }
+    }
+  }
+
+  /**
+   * Remove all elements matched by [test].
+   *
+   * This method is inefficient since it works by repeatedly removing single
+   * elements, each of which can take linear time.
+   */
+  void removeMatching(bool test(E element)) {
+    _filterMatching(test, true);
+  }
+
+  /**
+   * Remove all elements not matched by [test].
+   *
+   * This method is inefficient since it works by repeatedly removing single
+   * elements, each of which can take linear time.
+   */
+  void retainMatching(bool test(E element)) {
+    _filterMatching(test, false);
+  }
+
+  void clear() {
+    if (_head != _tail) {
+      for (int i = _head; i != _tail; i = (i + 1) & (_table.length - 1)) {
+        _table[i] = null;
+      }
+      _head = _tail = 0;
+      _modificationCount++;
+    }
+  }
+
+  String toString() {
+    return Collections.collectionToString(this);
+  }
+
+  // Queue interface.
+
+  void addLast(E element) { _add(element); }
+
+  void addFirst(E element) {
+    _head = (_head - 1) & (_table.length - 1);
+    _table[_head] = element;
+    if (_head == _tail) _grow();
+    _modificationCount++;
+  }
+
+  E removeFirst() {
+    if (_head == _tail) throw new StateError("No elements");
+    _modificationCount++;
+    E result = _table[_head];
+    _head = (_head + 1) & (_table.length - 1);
+    return result;
+  }
+
+  E removeLast() {
+    if (_head == _tail) throw new StateError("No elements");
+    _modificationCount++;
+    _tail = (_tail - 1) & (_table.length - 1);
+    return _table[_tail];
+  }
+
+  // Internal helper functions.
+
+  /**
+   * Whether [number] is a power of two.
+   *
+   * Only works for positive numbers.
+   */
+  static bool _isPowerOf2(int number) => (number & (number - 1)) == 0;
+
+  /**
+   * Rounds [number] up to the nearest power of 2.
+   *
+   * If [number] is a power of 2 already, it is returned.
+   *
+   * Only works for positive numbers.
+   */
+  static int _nextPowerOf2(int number) {
+    assert(number > 0);
+    number = (number << 2) - 1;
+    for(;;) {
+      int nextNumber = number & (number - 1);
+      if (nextNumber == 0) return number;
+      number = nextNumber;
+    }
+  }
+
+  /** Check if the queue has been modified during iteration. */
+  void _checkModification(int expectedModificationCount) {
+    if (expectedModificationCount != _modificationCount) {
+      throw new ConcurrentModificationError(this);
+    }
+  }
+
+  /** Adds element at end of queue. Used by both [add] and [addAll]. */
+  void _add(E element) {
+    _table[_tail] = element;
+    _tail = (_tail + 1) & (_table.length - 1);
+    if (_head == _tail) _grow();
+    _modificationCount++;
+  }
+
+  /**
+   * Removes the element at [offset] into [_table].
+   *
+   * Removal is performed by linerarly moving elements either before or after
+   * [offset] by one position.
+   *
+   * Returns the new offset of the following element. This may be the same
+   * offset or the following offset depending on how elements are moved
+   * to fill the hole.
+   */
+  int _remove(int offset) {
+    int mask = _table.length - 1;
+    int startDistance = (offset - _head) & mask;
+    int endDistance = (_tail - offset) & mask;
+    if (startDistance < endDistance) {
+      // Closest to start.
+      int i = offset;
+      while (i != _head) {
+        int prevOffset = (i - 1) & mask;
+        _table[i] = _table[prevOffset];
+        i = prevOffset;
+      }
+      _table[_head] = null;
+      _head = (_head + 1) & mask;
+      return (offset + 1) & mask;
+    } else {
+      _tail = (_tail - 1) & mask;
+      int i = offset;
+      while (i != _tail) {
+        int nextOffset = (i + 1) & mask;
+        _table[i] = _table[nextOffset];
+        i = nextOffset;
+      }
+      _table[_tail] = null;
+      return offset;
+    }
+  }
+
+  /**
+   * Grow the table when full.
+   */
+  void _grow() {
+    List<E> newTable = new List<E>.fixedLength(_table.length * 2);
+    int split = _table.length - _head;
+    newTable.setRange(0, split, _table, _head);
+    newTable.setRange(split, _head, _table, 0);
+    _head = 0;
+    _tail = _table.length;
+    _table = newTable;
+  }
+
+  int _writeToList(List<E> target) {
+    assert(target.length >= length);
+    if (_head <= _tail) {
+      int length = _tail - _head;
+      target.setRange(0, length, _table, _head);
+      return length;
+    } else {
+      int firstPartSize = _table.length - _head;
+      target.setRange(0, firstPartSize, _table, _head);
+      target.setRange(firstPartSize, _tail, _table, 0);
+      return _tail + firstPartSize;
+    }
+  }
+
+  /** Grows the table even if it is not full. */
+  void _preGrow(int newElementCount) {
+    assert(newElementCount >= length);
+    int newCapacity = _nextPowerOf2(newElementCount);
+    List<E> newTable = new List<E>.fixedLength(newCapacity);
+    _tail = _writeToList(newTable);
+    _table = newTable;
+    _head = 0;
+  }
+}
+
+/**
+ * Iterator for a [ListQueue].
+ *
+ * Considers any add or remove operation a concurrent modification.
+ */
+class _ListQueueIterator<E> implements Iterator<E> {
+  final ListQueue _queue;
+  final int _end;
+  final int _modificationCount;
+  int _position;
+  E _current;
+
+  _ListQueueIterator(ListQueue queue)
+      : _queue = queue,
+        _end = queue._tail,
+        _modificationCount = queue._modificationCount,
+        _position = queue._head;
+
+  E get current => _current;
+
+  bool moveNext() {
+    _queue._checkModification(_modificationCount);
+    if (_position == _end) {
+      _current = null;
+      return false;
+    }
+    _current = _queue._table[_position];
+    _position = (_position + 1) & (_queue._table.length - 1);
+    return true;
+  }
+}
diff --git a/sdk/lib/core/duration.dart b/sdk/lib/core/duration.dart
index d68c9e7..af79aeb 100644
--- a/sdk/lib/core/duration.dart
+++ b/sdk/lib/core/duration.dart
@@ -50,6 +50,52 @@
                          milliseconds;
 
   /**
+   * Returns the sum of this [Duration] and [other]  as a new [Duration].
+   */
+  Duration operator +(Duration other) {
+    return new Duration(milliseconds: inMilliseconds + other.inMilliseconds);
+  }
+
+  /**
+   * Returns the difference of this [Duration] and [other] as a new
+   * [Duration].
+   */
+  Duration operator -(Duration other) {
+    return new Duration(milliseconds: inMilliseconds - other.inMilliseconds);
+  }
+
+  /**
+   * Multiplies this [Duration] by the given [factor] and returns the result
+   * as a new [Duration].
+   */
+  Duration operator *(int factor) {
+    return new Duration(milliseconds: inMilliseconds * factor);
+  }
+
+  /**
+   * Divides this [Duration] by the given [quotient] and returns the truncated
+   * result as a new [Duration].
+   *
+   * Throws an [IntegerDivisionByZeroException] if [quotient] is `0`.
+   */
+  Duration operator ~/(int quotient) {
+    // By doing the check here instead of relying on "~/" below we get the
+    // exception even with dart2js.
+    if (quotient == 0) throw new IntegerDivisionByZeroException();
+    return new Duration(milliseconds: inMilliseconds ~/ quotient);
+  }
+
+  bool operator <(Duration other) => this.inMilliseconds < other.inMilliseconds;
+
+  bool operator >(Duration other) => this.inMilliseconds > other.inMilliseconds;
+
+  bool operator <=(Duration other) =>
+      this.inMilliseconds <= other.inMilliseconds;
+
+  bool operator >=(Duration other) =>
+      this.inMilliseconds >= other.inMilliseconds;
+
+  /**
    * This [Duration] in days. Incomplete days are discarded
    */
   int get inDays {
diff --git a/sdk/lib/core/iterable.dart b/sdk/lib/core/iterable.dart
index fc1a647..56e96ef 100644
--- a/sdk/lib/core/iterable.dart
+++ b/sdk/lib/core/iterable.dart
@@ -432,3 +432,16 @@
 
   E get current => _current;
 }
+
+/**
+ * An [Iterator] that allows moving backwards as well as forwards.
+ */
+abstract class BiDirectionalIterator<T> extends Iterator<T> {
+  /**
+   * Move back to the previous element.
+   *
+   * Returns true and updates [current] if successful. Returns false
+   * and sets [current] to null if there is no previous element.
+   */
+  bool movePrevious();
+}
diff --git a/sdk/lib/core/string.dart b/sdk/lib/core/string.dart
index 6fb1b90..3584db5 100644
--- a/sdk/lib/core/string.dart
+++ b/sdk/lib/core/string.dart
@@ -280,8 +280,7 @@
    * as one integer by this iterator. Unmatched surrogate halves are treated
    * like valid 16-bit code-units.
    */
-  // TODO(floitsch): make it a Runes class.
-  Iterable<int> get runes;
+  Runes get runes;
 
   /**
    * If this string is not already all lower case, returns a new string
@@ -297,3 +296,199 @@
   // TODO(floitsch): document better. (See EcmaScript for description).
   String toUpperCase();
 }
+
+/**
+ * The runes of a [String].
+ */
+class Runes extends Iterable<int> {
+  final String string;
+  Runes(this.string);
+
+  RuneIterator get iterator => new RuneIterator(string);
+
+  int get last {
+    if (string.length == 0) {
+      throw new StateError("No elements.");
+    }
+    int length = string.length;
+    int code = string.charCodeAt(length - 1);
+    if (_isTrailSurrogate(code) && string.length > 1) {
+      int previousCode = string.charCodeAt(length - 2);
+      if (_isLeadSurrogate(previousCode)) {
+        return _combineSurrogatePair(previousCode, code);
+      }
+    }
+    return code;
+  }
+
+}
+
+// Is then code (a 16-bit unsigned integer) a UTF-16 lead surrogate.
+bool _isLeadSurrogate(int code) => (code & 0xFC00) == 0xD800;
+
+// Is then code (a 16-bit unsigned integer) a UTF-16 trail surrogate.
+bool _isTrailSurrogate(int code) => (code & 0xFC00) == 0xDC00;
+
+// Combine a lead and a trail surrogate value into a single code point.
+int _combineSurrogatePair(int start, int end) {
+  return 0x10000 + ((start & 0x3FF) << 10) + (end & 0x3FF);
+}
+
+/** [Iterator] for reading Unicode code points out of a Dart string. */
+class RuneIterator implements BiDirectionalIterator<int> {
+  /** String being iterated. */
+  final String string;
+  /** Position before the current code point. */
+  int _position;
+  /** Position after the current code point. */
+  int _nextPosition;
+  /**
+   * Current code point.
+   *
+   * If the iterator has hit either end, the [_currentCodePoint] is null
+   * and [: _position == _nextPosition :].
+   */
+  int _currentCodePoint;
+
+  /** Create an iterator positioned at the beginning of the string. */
+  RuneIterator(String string)
+      : this.string = string, _position = 0, _nextPosition = 0;
+
+  /**
+   * Create an iterator positioned before the [index]th code unit of the string.
+   *
+   * When created, there is no [current] value.
+   * A [moveNext] will use the rune starting at [index] the current value,
+   * and a [movePrevious] will use the rune ending just before [index] as the
+   * the current value.
+   *
+   * It is an error if the [index] position is in the middle of a surrogate
+   * pair.
+   */
+  RuneIterator.at(String string, int index)
+      : string = string, _position = index, _nextPosition = index {
+    if (index < 0 || index > string.length) {
+      throw new RangeError.range(index, 0, string.length);
+    }
+    _checkSplitSurrogate(index);
+  }
+
+  /** Throw an error if the index is in the middle of a surrogate pair. */
+  void _checkSplitSurrogate(int index) {
+    if (index > 0 && index < string.length &&
+        _isLeadSurrogate(string.charCodeAt(index - 1)) &&
+        _isTrailSurrogate(string.charCodeAt(index))) {
+      throw new ArgumentError("Index inside surrogate pair: $index");
+    }
+  }
+
+  /**
+   * Returns the starting position of the current rune in the string.
+   *
+   * Returns null if the [current] rune is null.
+   */
+  int get rawIndex => (_position != _nextPosition) ? _position : null;
+
+  /**
+   * Resets the iterator to the rune at the specified index of the string.
+   *
+   * Setting a negative [rawIndex], or one greater than or equal to
+   * [:string.length:],
+   * is an error. So is setting it in the middle of a surrogate pair.
+   *
+   * Setting the position to the end of then string will set [current] to null.
+   */
+  void set rawIndex(int rawIndex) {
+    if (rawIndex >= string.length) {
+      throw new RangeError.range(rawIndex, 0, string.length - 1);
+    }
+    reset(rawIndex);
+    moveNext();
+  }
+
+  /**
+   * Resets the iterator to the given index into the string.
+   *
+   * After this the [current] value is unset.
+   * You must call [moveNext] make the rune at the position current,
+   * or [movePrevious] for the last rune before the position.
+   *
+   * Setting a negative [rawIndex], or one greater than [:string.length:],
+   * is an error. So is setting it in the middle of a surrogate pair.
+   */
+  void reset([int rawIndex = 0]) {
+    if (rawIndex < 0 || rawIndex > string.length) {
+      throw new RangeError.range(rawIndex, 0, string.length);
+    }
+    _checkSplitSurrogate(rawIndex);
+    _position = _nextPosition = rawIndex;
+    _currentCodePoint = null;
+  }
+
+  /** The rune starting at the current position in the string. */
+  int get current => _currentCodePoint;
+
+  /**
+   * The number of code units comprising the current rune.
+   *
+   * Returns zero if there is no current rune ([current] is null).
+   */
+  int get currentSize => _nextPosition - _position;
+
+  /**
+   * A string containing the current rune.
+   *
+   * For runes outside the basic multilingual plane, this will be
+   * a two-character String.
+   *
+   * Returns null if [current] is null.
+   */
+  String get currentAsString {
+    if (_position == _nextPosition) return null;
+    if (_position + 1 == _nextPosition) return string[_position];
+    return string.substring(_position, _nextPosition);
+  }
+
+
+  bool moveNext() {
+    _position = _nextPosition;
+    if (_position == string.length) {
+      _currentCodePoint = null;
+      return false;
+    }
+    int codeUnit = string.charCodeAt(_position);
+    int nextPosition = _position + 1;
+    if (_isLeadSurrogate(codeUnit) && nextPosition < string.length) {
+      int nextCodeUnit = string.charCodeAt(nextPosition);
+      if (_isTrailSurrogate(nextCodeUnit)) {
+        _nextPosition = nextPosition + 1;
+        _currentCodePoint = _combineSurrogatePair(codeUnit, nextCodeUnit);
+        return true;
+      }
+    }
+    _nextPosition = nextPosition;
+    _currentCodePoint = codeUnit;
+    return true;
+  }
+
+  bool movePrevious() {
+    _nextPosition = _position;
+    if (_position == 0) {
+      _currentCodePoint = null;
+      return false;
+    }
+    int position = _position - 1;
+    int codeUnit = string.charCodeAt(position);
+    if (_isTrailSurrogate(codeUnit) && position > 0) {
+      int prevCodeUnit = string.charCodeAt(position - 1);
+      if (_isLeadSurrogate(prevCodeUnit)) {
+        _position = position - 1;
+        _currentCodePoint = _combineSurrogatePair(prevCodeUnit, codeUnit);
+        return true;
+      }
+    }
+    _position = position;
+    _currentCodePoint = codeUnit;
+    return true;
+  }
+}
diff --git a/sdk/lib/html/dart2js/html_dart2js.dart b/sdk/lib/html/dart2js/html_dart2js.dart
index 321b381..2faa94c 100644
--- a/sdk/lib/html/dart2js/html_dart2js.dart
+++ b/sdk/lib/html/dart2js/html_dart2js.dart
@@ -1036,6 +1036,8 @@
   @DocsEditable
   int width;
 
+  @DomName('HTMLCanvasElement.getContext')
+  @DocsEditable
   CanvasRenderingContext getContext(String contextId, [Map attrs]) {
     if (?attrs) {
       var attrs_1 = convertDartToNative_Dictionary(attrs);
@@ -1257,10 +1259,6 @@
   @DocsEditable
   String lineCap;
 
-  @DomName('CanvasRenderingContext2D.lineDashOffset')
-  @DocsEditable
-  num lineDashOffset;
-
   @DomName('CanvasRenderingContext2D.lineJoin')
   @DocsEditable
   String lineJoin;
@@ -1303,21 +1301,29 @@
   @DocsEditable
   String textBaseline;
 
+  @JSName('webkitBackingStorePixelRatio')
   @DomName('CanvasRenderingContext2D.webkitBackingStorePixelRatio')
   @DocsEditable
-  final num webkitBackingStorePixelRatio;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  final num backingStorePixelRatio;
 
+  @JSName('webkitImageSmoothingEnabled')
   @DomName('CanvasRenderingContext2D.webkitImageSmoothingEnabled')
   @DocsEditable
-  bool webkitImageSmoothingEnabled;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  bool imageSmoothingEnabled;
 
+  @JSName('webkitLineDash')
   @DomName('CanvasRenderingContext2D.webkitLineDash')
   @DocsEditable
-  List webkitLineDash;
-
-  @DomName('CanvasRenderingContext2D.webkitLineDashOffset')
-  @DocsEditable
-  num webkitLineDashOffset;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  List lineDash;
 
   @DomName('CanvasRenderingContext2D.arc')
   @DocsEditable
@@ -1347,6 +1353,9 @@
   @DocsEditable
   void closePath() native;
 
+  @DomName('CanvasRenderingContext2D.createImageData')
+  @DocsEditable
+  @Creates('ImageData|=Object')
   ImageData createImageData(imagedata_OR_sw, [num sh]) {
     if ((imagedata_OR_sw is ImageData || imagedata_OR_sw == null) && !?sh) {
       var imagedata_1 = _convertDartToNative_ImageData(imagedata_OR_sw);
@@ -1396,6 +1405,9 @@
   @DocsEditable
   void fillText(String text, num x, num y, [num maxWidth]) native;
 
+  @DomName('CanvasRenderingContext2D.getImageData')
+  @DocsEditable
+  @Creates('ImageData|=Object')
   ImageData getImageData(num sx, num sy, num sw, num sh) {
     return _convertNativeToDart_ImageData(_getImageData_1(sx, sy, sw, sh));
   }
@@ -1429,6 +1441,8 @@
   @DocsEditable
   void moveTo(num x, num y) native;
 
+  @DomName('CanvasRenderingContext2D.putImageData')
+  @DocsEditable
   void putImageData(ImageData imagedata, num dx, num dy, [num dirtyX, num dirtyY, num dirtyWidth, num dirtyHeight]) {
     if (!?dirtyX && !?dirtyY && !?dirtyWidth && !?dirtyHeight) {
       var imagedata_1 = _convertDartToNative_ImageData(imagedata);
@@ -1501,34 +1515,54 @@
   @DocsEditable
   void translate(num tx, num ty) native;
 
-  ImageData webkitGetImageDataHD(num sx, num sy, num sw, num sh) {
-    return _convertNativeToDart_ImageData(_webkitGetImageDataHD_1(sx, sy, sw, sh));
+  @DomName('CanvasRenderingContext2D.webkitGetImageDataHD')
+  @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  @Creates('ImageData|=Object')
+  ImageData getImageDataHD(num sx, num sy, num sw, num sh) {
+    return _convertNativeToDart_ImageData(_getImageDataHD_1(sx, sy, sw, sh));
   }
   @JSName('webkitGetImageDataHD')
   @DomName('CanvasRenderingContext2D.webkitGetImageDataHD')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   @Creates('ImageData|=Object')
-  _webkitGetImageDataHD_1(sx, sy, sw, sh) native;
+  _getImageDataHD_1(sx, sy, sw, sh) native;
 
-  void webkitPutImageDataHD(ImageData imagedata, num dx, num dy, [num dirtyX, num dirtyY, num dirtyWidth, num dirtyHeight]) {
+  @DomName('CanvasRenderingContext2D.webkitPutImageDataHD')
+  @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void putImageDataHD(ImageData imagedata, num dx, num dy, [num dirtyX, num dirtyY, num dirtyWidth, num dirtyHeight]) {
     if (!?dirtyX && !?dirtyY && !?dirtyWidth && !?dirtyHeight) {
       var imagedata_1 = _convertDartToNative_ImageData(imagedata);
-      _webkitPutImageDataHD_1(imagedata_1, dx, dy);
+      _putImageDataHD_1(imagedata_1, dx, dy);
       return;
     }
     var imagedata_2 = _convertDartToNative_ImageData(imagedata);
-    _webkitPutImageDataHD_2(imagedata_2, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight);
+    _putImageDataHD_2(imagedata_2, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight);
     return;
     throw new ArgumentError("Incorrect number or type of arguments");
   }
   @JSName('webkitPutImageDataHD')
   @DomName('CanvasRenderingContext2D.webkitPutImageDataHD')
   @DocsEditable
-  void _webkitPutImageDataHD_1(imagedata, dx, dy) native;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void _putImageDataHD_1(imagedata, dx, dy) native;
   @JSName('webkitPutImageDataHD')
   @DomName('CanvasRenderingContext2D.webkitPutImageDataHD')
   @DocsEditable
-  void _webkitPutImageDataHD_2(imagedata, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight) native;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void _putImageDataHD_2(imagedata, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight) native;
 
 
   /**
@@ -1566,6 +1600,15 @@
   void setStrokeColorHsl(int h, num s, num l, [num a = 1]) {
     this.strokeStyle = 'hsla($h, $s%, $l%, $a)';
   }
+
+  @DomName('CanvasRenderingContext2D.lineDashOffset')
+  num get lineDashOffset => JS('num',
+      '#.lineDashOffset || #.webkitLineDashOffset', this, this);
+
+  @DomName('CanvasRenderingContext2D.lineDashOffset')
+  void set lineDashOffset(num value) => JS('void', 
+      'typeof #.lineDashOffset != "undefined" ? #.lineDashOffset = # : '
+      '#.webkitLineDashOffset = #', this, this, value, this, value);
 }
 // 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
@@ -2386,6 +2429,24 @@
   return _cachedBrowserPrefix;
 }
 
+String _cachedBrowserPropertyPrefix;
+
+/// Prefix as used for JS property names.
+String get _browserPropertyPrefix {
+  if (_cachedBrowserPropertyPrefix == null) {
+    if (_Device.isFirefox) {
+      _cachedBrowserPropertyPrefix = 'moz';
+    } else if (_Device.isIE) {
+      _cachedBrowserPropertyPrefix = 'ms';
+    } else if (_Device.isOpera) {
+      _cachedBrowserPropertyPrefix = 'o';
+    } else {
+      _cachedBrowserPropertyPrefix = 'webkit';
+    }
+  }
+  return _cachedBrowserPropertyPrefix;
+}
+
 @DomName('CSSStyleDeclaration')
 class CssStyleDeclaration native "*CSSStyleDeclaration" {
   factory CssStyleDeclaration() => _CssStyleDeclarationFactoryProvider.createCssStyleDeclaration();
@@ -2449,6 +2510,17 @@
     }
   }
 
+  /**
+   * Checks to see if CSS Transitions are supported.
+   */
+  static bool get supportsTransitions {
+    if (JS('bool', '"transition" in document.body.style')) {
+      return true;
+    }
+    var propertyName = '${_browserPropertyPrefix}Transition';
+    return JS('bool', '# in document.body.style', propertyName);
+  }
+
   // TODO(jacobr): generate this list of properties using the existing script.
   /** Gets the value of "align-content" */
   String get alignContent =>
@@ -5376,10 +5448,18 @@
   }
 
   /** Gets the value of "transition" */
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.FIREFOX)
+  @SupportedBrowser(SupportedBrowser.IE, '10')
+  @SupportedBrowser(SupportedBrowser.SAFARI)
   String get transition =>
     getPropertyValue('${_browserPrefix}transition');
 
   /** Sets the value of "transition" */
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.FIREFOX)
+  @SupportedBrowser(SupportedBrowser.IE, '10')
+  @SupportedBrowser(SupportedBrowser.SAFARI)
   void set transition(String value) {
     setProperty('${_browserPrefix}transition', value, '');
   }
@@ -5884,9 +5964,13 @@
   @DocsEditable
   void getAsString([StringCallback callback]) native;
 
+  @JSName('webkitGetAsEntry')
   @DomName('DataTransferItem.webkitGetAsEntry')
   @DocsEditable
-  Entry webkitGetAsEntry() native;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  Entry getAsEntry() 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
@@ -6091,6 +6175,8 @@
   DedicatedWorkerContextEvents get on =>
     new DedicatedWorkerContextEvents(this);
 
+  @DomName('DedicatedWorkerContext.postMessage')
+  @DocsEditable
   void postMessage(/*any*/ message, [List messagePorts]) {
     if (?messagePorts) {
       var message_1 = convertDartToNative_SerializedScriptValue(message);
@@ -6213,6 +6299,8 @@
   @DocsEditable
   DirectoryReader createReader() native;
 
+  @DomName('DirectoryEntry.getDirectory')
+  @DocsEditable
   void getDirectory(String path, {Map options, EntryCallback successCallback, ErrorCallback errorCallback}) {
     if (?errorCallback) {
       var options_1 = convertDartToNative_Dictionary(options);
@@ -6249,6 +6337,8 @@
   @DocsEditable
   void _getDirectory_4(path) native;
 
+  @DomName('DirectoryEntry.getFile')
+  @DocsEditable
   void getFile(String path, {Map options, EntryCallback successCallback, ErrorCallback errorCallback}) {
     if (?errorCallback) {
       var options_1 = convertDartToNative_Dictionary(options);
@@ -6302,6 +6392,8 @@
   @DocsEditable
   DirectoryReaderSync createReader() native;
 
+  @DomName('DirectoryEntrySync.getDirectory')
+  @DocsEditable
   DirectoryEntrySync getDirectory(String path, Map flags) {
     var flags_1 = convertDartToNative_Dictionary(flags);
     return _getDirectory_1(path, flags_1);
@@ -6311,6 +6403,8 @@
   @DocsEditable
   DirectoryEntrySync _getDirectory_1(path, flags) native;
 
+  @DomName('DirectoryEntrySync.getFile')
+  @DocsEditable
   FileEntrySync getFile(String path, Map flags) {
     var flags_1 = convertDartToNative_Dictionary(flags);
     return _getFile_1(path, flags_1);
@@ -6415,10 +6509,16 @@
 
   @DomName('Document.webkitpointerlockchangeEvent')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   static const EventStreamProvider<Event> pointerLockChangeEvent = const EventStreamProvider<Event>('webkitpointerlockchange');
 
   @DomName('Document.webkitpointerlockerrorEvent')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   static const EventStreamProvider<Event> pointerLockErrorEvent = const EventStreamProvider<Event>('webkitpointerlockerror');
 
   @DocsEditable
@@ -6511,35 +6611,53 @@
   /// Moved to [HtmlDocument].
   @DomName('Document.webkitFullscreenElement')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   final Element $dom_webkitFullscreenElement;
 
   @JSName('webkitFullscreenEnabled')
   /// Moved to [HtmlDocument].
   @DomName('Document.webkitFullscreenEnabled')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   final bool $dom_webkitFullscreenEnabled;
 
   @JSName('webkitHidden')
   /// Moved to [HtmlDocument].
   @DomName('Document.webkitHidden')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   final bool $dom_webkitHidden;
 
   @JSName('webkitIsFullScreen')
   /// Moved to [HtmlDocument].
   @DomName('Document.webkitIsFullScreen')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   final bool $dom_webkitIsFullScreen;
 
   @JSName('webkitPointerLockElement')
   /// Moved to [HtmlDocument].
   @DomName('Document.webkitPointerLockElement')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   final Element $dom_webkitPointerLockElement;
 
   @JSName('webkitVisibilityState')
   @DomName('Document.webkitVisibilityState')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   final String $dom_webkitVisibilityState;
 
   @JSName('caretRangeFromPoint')
@@ -6583,6 +6701,8 @@
   @DocsEditable
   Text $dom_createTextNode(String data) native;
 
+  @DomName('Document.createTouch')
+  @DocsEditable
   Touch $dom_createTouch(Window window, EventTarget target, int identifier, int pageX, int pageY, int screenX, int screenY, int webkitRadiusX, int webkitRadiusY, num webkitRotationAngle, num webkitForce) {
     var target_1 = _convertDartToNative_EventTarget(target);
     return _$dom_createTouch_1(window, target_1, identifier, pageX, pageY, screenX, screenY, webkitRadiusX, webkitRadiusY, webkitRotationAngle, webkitForce);
@@ -6678,18 +6798,27 @@
   /// Moved to [HtmlDocument].
   @DomName('Document.webkitCancelFullScreen')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   void $dom_webkitCancelFullScreen() native;
 
   @JSName('webkitExitFullscreen')
   /// Moved to [HtmlDocument].
   @DomName('Document.webkitExitFullscreen')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   void $dom_webkitExitFullscreen() native;
 
   @JSName('webkitExitPointerLock')
   /// Moved to [HtmlDocument].
   @DomName('Document.webkitExitPointerLock')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   void $dom_webkitExitPointerLock() native;
 
   @DomName('Document.onabort')
@@ -8818,6 +8947,12 @@
     return e is Element && !(e is UnknownElement);
   }
 
+  /**
+   * Called by the DOM when this element has been instantiated.
+   */
+  @Experimental
+  void onCreated() {}
+
   // Hooks to support custom WebComponents.
   /**
    * Experimental support for [web components][wc]. This field stores a
@@ -8832,6 +8967,7 @@
   @Creates('Null')  // Set from Dart code; does not instantiate a native type.
   var xtag;
 
+  @DomName('Element.mouseWheelEvent')
   static const EventStreamProvider<WheelEvent> mouseWheelEvent =
       const _CustomEventStreamProvider<WheelEvent>(
         Element._determineMouseWheelEventType);
@@ -8849,6 +8985,20 @@
     }
   }
 
+  @DomName('Element.webkitTransitionEndEvent')
+  static const EventStreamProvider<TransitionEvent> transitionEndEvent =
+      const _CustomEventStreamProvider<TransitionEvent>(
+        Element._determineTransitionEventType);
+
+  static String _determineTransitionEventType(EventTarget e) {
+    // Unfortunately the normal 'ontransitionend' style checks don't work here.
+    if (_Device.isWebKit) {
+      return 'webkitTransitionEnd';
+    } else if (_Device.isOpera) {
+      return 'oTransitionEnd';
+    }
+    return 'transitionend';
+  }
   /**
    * Creates a text node and inserts it into the DOM at the specified location.
    *
@@ -9139,16 +9289,18 @@
   @DocsEditable
   static const EventStreamProvider<TouchEvent> touchStartEvent = const EventStreamProvider<TouchEvent>('touchstart');
 
-  @DomName('Element.webkitTransitionEndEvent')
-  @DocsEditable
-  static const EventStreamProvider<TransitionEvent> transitionEndEvent = const EventStreamProvider<TransitionEvent>('webkitTransitionEnd');
-
   @DomName('Element.webkitfullscreenchangeEvent')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   static const EventStreamProvider<Event> fullscreenChangeEvent = const EventStreamProvider<Event>('webkitfullscreenchange');
 
   @DomName('Element.webkitfullscreenerrorEvent')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   static const EventStreamProvider<Event> fullscreenErrorEvent = const EventStreamProvider<Event>('webkitfullscreenerror');
 
   @DocsEditable
@@ -9216,9 +9368,13 @@
   @DocsEditable
   bool translate;
 
+  @JSName('webkitdropzone')
   @DomName('Element.webkitdropzone')
   @DocsEditable
-  String webkitdropzone;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  String dropzone;
 
   @DomName('Element.click')
   @DocsEditable
@@ -9318,13 +9474,21 @@
   @DocsEditable
   final String tagName;
 
+  @JSName('webkitPseudo')
   @DomName('Element.webkitPseudo')
   @DocsEditable
-  String webkitPseudo;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  String pseudo;
 
+  @JSName('webkitShadowRoot')
   @DomName('Element.webkitShadowRoot')
   @DocsEditable
-  final ShadowRoot webkitShadowRoot;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  final ShadowRoot shadowRoot;
 
   @DomName('Element.blur')
   @DocsEditable
@@ -9430,17 +9594,29 @@
   @Experimental
   ShadowRoot createShadowRoot() native;
 
+  @JSName('webkitRequestFullScreen')
   @DomName('Element.webkitRequestFullScreen')
   @DocsEditable
-  void webkitRequestFullScreen(int flags) native;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void requestFullScreen(int flags) native;
 
+  @JSName('webkitRequestFullscreen')
   @DomName('Element.webkitRequestFullscreen')
   @DocsEditable
-  void webkitRequestFullscreen() native;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void requestFullscreen() native;
 
+  @JSName('webkitRequestPointerLock')
   @DomName('Element.webkitRequestPointerLock')
   @DocsEditable
-  void webkitRequestPointerLock() native;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void requestPointerLock() native;
 
   @DomName('Element.onabort')
   @DocsEditable
@@ -9624,6 +9800,10 @@
 
   @DomName('Element.onwebkitTransitionEnd')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.FIREFOX)
+  @SupportedBrowser(SupportedBrowser.IE, '10')
+  @SupportedBrowser(SupportedBrowser.SAFARI)
   Stream<TransitionEvent> get onTransitionEnd => transitionEndEvent.forTarget(this);
 
   @DomName('Element.onwebkitfullscreenchange')
@@ -9911,9 +10091,6 @@
   EventListenerList get touchStart => this['touchstart'];
 
   @DocsEditable
-  EventListenerList get transitionEnd => this['webkitTransitionEnd'];
-
-  @DocsEditable
   EventListenerList get fullscreenChange => this['webkitfullscreenchange'];
 
   @DocsEditable
@@ -10622,9 +10799,13 @@
   @DocsEditable
   final String name;
 
+  @JSName('webkitRelativePath')
   @DomName('File.webkitRelativePath')
   @DocsEditable
-  final String webkitRelativePath;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  final String relativePath;
 }
 // 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
@@ -11958,6 +12139,9 @@
 
 
 @DocsEditable
+/**
+ * An `<hr>` tag.
+ */
 @DomName('HTMLHRElement')
 class HRElement extends Element native "*HTMLHRElement" {
 
@@ -12545,6 +12729,17 @@
     document.$dom_body = value;
   }
 
+  /**
+   * Registers a custom Element subclass as an available HTML tag.
+   *
+   * Not yet implemented.
+   */
+  @Experimental
+  void register(String tagName, Type elementClass) {
+    // TODO: tagName validation
+    throw new Exception('Not yet implemented');
+  }
+
   @DomName('Document.caretRangeFromPoint')
   Range caretRangeFromPoint(int x, int y) {
     return document.$dom_caretRangeFromPoint(x, y);
@@ -13515,6 +13710,9 @@
 
   @DomName('HTMLInputElement.webkitSpeechChangeEvent')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   static const EventStreamProvider<Event> speechChangeEvent = const EventStreamProvider<Event>('webkitSpeechChange');
 
   @DocsEditable
@@ -13699,23 +13897,39 @@
   @DocsEditable
   num valueAsNumber;
 
+  @JSName('webkitEntries')
   @DomName('HTMLInputElement.webkitEntries')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   @Returns('_EntryArray')
   @Creates('_EntryArray')
-  final List<Entry> webkitEntries;
+  final List<Entry> entries;
 
+  @JSName('webkitGrammar')
   @DomName('HTMLInputElement.webkitGrammar')
   @DocsEditable
-  bool webkitGrammar;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  bool grammar;
 
+  @JSName('webkitSpeech')
   @DomName('HTMLInputElement.webkitSpeech')
   @DocsEditable
-  bool webkitSpeech;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  bool speech;
 
+  @JSName('webkitdirectory')
   @DomName('HTMLInputElement.webkitdirectory')
   @DocsEditable
-  bool webkitdirectory;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  bool directory;
 
   @DomName('HTMLInputElement.width')
   @DocsEditable
@@ -15336,6 +15550,8 @@
 
 @DocsEditable
 @DomName('LocalMediaStream')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@Experimental
 class LocalMediaStream extends MediaStream implements EventTarget native "*LocalMediaStream" {
 
   @DomName('LocalMediaStream.stop')
@@ -15611,18 +15827,30 @@
 
   @DomName('HTMLMediaElement.webkitkeyaddedEvent')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   static const EventStreamProvider<MediaKeyEvent> keyAddedEvent = const EventStreamProvider<MediaKeyEvent>('webkitkeyadded');
 
   @DomName('HTMLMediaElement.webkitkeyerrorEvent')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   static const EventStreamProvider<MediaKeyEvent> keyErrorEvent = const EventStreamProvider<MediaKeyEvent>('webkitkeyerror');
 
   @DomName('HTMLMediaElement.webkitkeymessageEvent')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   static const EventStreamProvider<MediaKeyEvent> keyMessageEvent = const EventStreamProvider<MediaKeyEvent>('webkitkeymessage');
 
   @DomName('HTMLMediaElement.webkitneedkeyEvent')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   static const EventStreamProvider<MediaKeyEvent> needKeyEvent = const EventStreamProvider<MediaKeyEvent>('webkitneedkey');
 
   @DocsEditable
@@ -15757,25 +15985,45 @@
   @DocsEditable
   num volume;
 
+  @JSName('webkitAudioDecodedByteCount')
   @DomName('HTMLMediaElement.webkitAudioDecodedByteCount')
   @DocsEditable
-  final int webkitAudioDecodedByteCount;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  final int audioDecodedByteCount;
 
+  @JSName('webkitClosedCaptionsVisible')
   @DomName('HTMLMediaElement.webkitClosedCaptionsVisible')
   @DocsEditable
-  bool webkitClosedCaptionsVisible;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  bool closedCaptionsVisible;
 
+  @JSName('webkitHasClosedCaptions')
   @DomName('HTMLMediaElement.webkitHasClosedCaptions')
   @DocsEditable
-  final bool webkitHasClosedCaptions;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  final bool hasClosedCaptions;
 
+  @JSName('webkitPreservesPitch')
   @DomName('HTMLMediaElement.webkitPreservesPitch')
   @DocsEditable
-  bool webkitPreservesPitch;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  bool preservesPitch;
 
+  @JSName('webkitVideoDecodedByteCount')
   @DomName('HTMLMediaElement.webkitVideoDecodedByteCount')
   @DocsEditable
-  final int webkitVideoDecodedByteCount;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  final int videoDecodedByteCount;
 
   @DomName('HTMLMediaElement.addTextTrack')
   @DocsEditable
@@ -15797,17 +16045,29 @@
   @DocsEditable
   void play() native;
 
+  @JSName('webkitAddKey')
   @DomName('HTMLMediaElement.webkitAddKey')
   @DocsEditable
-  void webkitAddKey(String keySystem, Uint8Array key, [Uint8Array initData, String sessionId]) native;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void addKey(String keySystem, Uint8Array key, [Uint8Array initData, String sessionId]) native;
 
+  @JSName('webkitCancelKeyRequest')
   @DomName('HTMLMediaElement.webkitCancelKeyRequest')
   @DocsEditable
-  void webkitCancelKeyRequest(String keySystem, String sessionId) native;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void cancelKeyRequest(String keySystem, String sessionId) native;
 
+  @JSName('webkitGenerateKeyRequest')
   @DomName('HTMLMediaElement.webkitGenerateKeyRequest')
   @DocsEditable
-  void webkitGenerateKeyRequest(String keySystem, [Uint8Array initData]) native;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void generateKeyRequest(String keySystem, [Uint8Array initData]) native;
 
   @DomName('HTMLMediaElement.oncanplay')
   @DocsEditable
@@ -16205,6 +16465,8 @@
 
 
 @DomName('MediaStream')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@Experimental
 class MediaStream extends EventTarget native "*MediaStream" {
 
   @DomName('MediaStream.endedEvent')
@@ -16324,6 +16586,8 @@
 
 @DocsEditable
 @DomName('MediaStreamEvent')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@Experimental
 class MediaStreamEvent extends Event native "*MediaStreamEvent" {
 
   /// Checks if this type is supported on the current platform.
@@ -16340,6 +16604,8 @@
 
 @DocsEditable
 @DomName('MediaStreamTrack')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@Experimental
 class MediaStreamTrack extends EventTarget native "*MediaStreamTrack" {
 
   @DomName('MediaStreamTrack.endedEvent')
@@ -16429,6 +16695,8 @@
 
 @DocsEditable
 @DomName('MediaStreamTrackEvent')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@Experimental
 class MediaStreamTrackEvent extends Event native "*MediaStreamTrackEvent" {
 
   /// Checks if this type is supported on the current platform.
@@ -16595,6 +16863,8 @@
   @DocsEditable
   bool dispatchEvent(Event evt) native;
 
+  @DomName('MessagePort.postMessage')
+  @DocsEditable
   void postMessage(/*any*/ message, [List messagePorts]) {
     if (?messagePorts) {
       var message_1 = convertDartToNative_SerializedScriptValue(message);
@@ -16828,22 +17098,24 @@
   @DocsEditable
   final Node toElement;
 
+  @JSName('webkitMovementX')
   @DomName('MouseEvent.webkitMovementX')
   @DocsEditable
-  final int webkitMovementX;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  final int movementX;
 
+  @JSName('webkitMovementY')
   @DomName('MouseEvent.webkitMovementY')
   @DocsEditable
-  final int webkitMovementY;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  final int movementY;
 
-  @DomName('MouseEvent.x')
+  @DomName('MouseEvent.initMouseEvent')
   @DocsEditable
-  final int x;
-
-  @DomName('MouseEvent.y')
-  @DocsEditable
-  final int y;
-
   void $dom_initMouseEvent(String type, bool canBubble, bool cancelable, Window view, int detail, int screenX, int screenY, int clientX, int clientY, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, int button, EventTarget relatedTarget) {
     var relatedTarget_1 = _convertDartToNative_EventTarget(relatedTarget);
     _$dom_initMouseEvent_1(type, canBubble, cancelable, view, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget_1);
@@ -16975,6 +17247,8 @@
   @DocsEditable
   void disconnect() native;
 
+  @DomName('MutationObserver.observe')
+  @DocsEditable
   void _observe(Node target, Map options) {
     var options_1 = convertDartToNative_Dictionary(options);
     __observe_1(target, options_1);
@@ -17124,230 +17398,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 
-@DocsEditable
-@DomName('NamedNodeMap')
-class NamedNodeMap implements JavaScriptIndexingBehavior, List<Node> native "*NamedNodeMap" {
-
-  @DomName('NamedNodeMap.length')
-  @DocsEditable
-  int get length => JS("int", "#.length", this);
-
-  Node operator[](int index) => JS("Node", "#[#]", this, index);
-
-  void operator[]=(int index, Node value) {
-    throw new UnsupportedError("Cannot assign element of immutable List.");
-  }
-  // -- 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);
-  }
-
-  dynamic reduce(dynamic initialValue, dynamic combine(dynamic, Node)) {
-    return IterableMixinWorkaround.reduce(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);
-
-  List mappedBy(f(Node element)) =>
-      IterableMixinWorkaround.mappedByList(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() => new List<Node>.from(this);
-  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 firstMatching(bool test(Node value), { Node orElse() }) {
-    return IterableMixinWorkaround.firstMatching(this, test, orElse);
-  }
-
-  Node lastMatching(bool test(Node value), {Node orElse()}) {
-    return IterableMixinWorkaround.lastMatchingInList(this, test, orElse);
-  }
-
-  Node singleMatching(bool test(Node value)) {
-    return IterableMixinWorkaround.singleMatching(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 addLast(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.");
-  }
-
-  List<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");
-  }
-
-  Node min([int compare(Node a, Node b)]) =>
-      IterableMixinWorkaround.min(this, compare);
-
-  Node max([int compare(Node a, Node b)]) =>
-      IterableMixinWorkaround.max(this, compare);
-
-  Node removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  Node removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeAll(Iterable elements) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainAll(Iterable elements) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeMatching(bool test(Node element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainMatching(bool test(Node element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int rangeLength, List<Node> from, [int startFrom]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int rangeLength) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void insertRange(int start, int rangeLength, [Node initialValue]) {
-    throw new UnsupportedError("Cannot insertRange on immutable List.");
-  }
-
-  List<Node> getRange(int start, int rangeLength) =>
-      Lists.getRange(this, start, rangeLength, <Node>[]);
-
-  // -- end List<Node> mixins.
-
-  @DomName('NamedNodeMap.getNamedItem')
-  @DocsEditable
-  Node getNamedItem(String name) native;
-
-  @DomName('NamedNodeMap.getNamedItemNS')
-  @DocsEditable
-  Node getNamedItemNS(String namespaceURI, String localName) native;
-
-  @DomName('NamedNodeMap.item')
-  @DocsEditable
-  Node item(int index) native;
-
-  @DomName('NamedNodeMap.removeNamedItem')
-  @DocsEditable
-  Node removeNamedItem(String name) native;
-
-  @DomName('NamedNodeMap.removeNamedItemNS')
-  @DocsEditable
-  Node removeNamedItemNS(String namespaceURI, String localName) native;
-
-  @DomName('NamedNodeMap.setNamedItem')
-  @DocsEditable
-  Node setNamedItem(Node node) native;
-
-  @DomName('NamedNodeMap.setNamedItemNS')
-  @DocsEditable
-  Node setNamedItemNS(Node node) 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
-// BSD-style license that can be found in the LICENSE file.
-
-
 @DomName('Navigator')
 class Navigator native "*Navigator" {
 
@@ -17463,9 +17513,13 @@
   @DocsEditable
   final String vendorSub;
 
+  @JSName('webkitBattery')
   @DomName('Navigator.webkitBattery')
   @DocsEditable
-  final BatteryManager webkitBattery;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  final BatteryManager battery;
 
   @DomName('Navigator.getStorageUpdates')
   @DocsEditable
@@ -17475,11 +17529,15 @@
   @DocsEditable
   bool javaEnabled() native;
 
+  @JSName('webkitGetGamepads')
   @DomName('Navigator.webkitGetGamepads')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   @Returns('_GamepadList')
   @Creates('_GamepadList')
-  List<Gamepad> webkitGetGamepads() native;
+  List<Gamepad> getGamepads() native;
 
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
@@ -17588,7 +17646,7 @@
   void remove(Object object) {
     if (object is! Node) return;
     Node node = object;
-    if (!identical(this, node.parentNode)) return;
+    if (!identical(_this, node.parentNode)) return;
     _this.$dom_removeChild(node);
   }
 
@@ -17785,7 +17843,7 @@
   @JSName('attributes')
   @DomName('Node.attributes')
   @DocsEditable
-  final NamedNodeMap $dom_attributes;
+  final _NamedNodeMap $dom_attributes;
 
   @JSName('childNodes')
   @DomName('Node.childNodes')
@@ -18206,14 +18264,21 @@
   @DocsEditable
   final String systemId;
 }
-// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
+// 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.
 
 
-@DocsEditable
 @DomName('Notification')
 class Notification extends EventTarget native "*Notification" {
+  factory Notification(String title, [Map options]) {
+    if (?options) {
+      return JS('Notification', 'new Notification(#,#)', title,
+          convertDartToNative_SerializedScriptValue(options));
+    } else {
+      return JS('Notification', 'new Notification(#)', title);
+    }
+  }
 
   @DomName('Notification.clickEvent')
   @DocsEditable
@@ -18235,17 +18300,6 @@
   @DocsEditable
   static const EventStreamProvider<Event> showEvent = const EventStreamProvider<Event>('show');
 
-  @DomName('Notification.Notification')
-  @DocsEditable
-  factory Notification(String title, [Map options]) {
-    if (?options) {
-      return Notification._create_1(title, options);
-    }
-    return Notification._create_2(title);
-  }
-  static Notification _create_1(title, options) => JS('Notification', 'new Notification(#,#)', title, options);
-  static Notification _create_2(title) => JS('Notification', 'new Notification(#)', title);
-
   @DocsEditable
   @DomName('EventTarget.addEventListener, EventTarget.removeEventListener, EventTarget.dispatchEvent')
   @deprecated
@@ -18317,6 +18371,7 @@
   @DomName('Notification.onshow')
   @DocsEditable
   Stream<Event> get onShow => showEvent.forTarget(this);
+
 }
 
 @DocsEditable
@@ -19500,21 +19555,19 @@
   @DocsEditable
   final RtcDataChannel channel;
 }
-// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
+// 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.
 
 
-@DocsEditable
 @DomName('RTCIceCandidate')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@Experimental
 class RtcIceCandidate native "*RTCIceCandidate" {
-
-  @DomName('RTCIceCandidate.RTCIceCandidate')
-  @DocsEditable
   factory RtcIceCandidate(Map dictionary) {
-    return RtcIceCandidate._create_1(dictionary);
+    return JS('RtcIceCandidate', 'new RTCIceCandidate(#)',
+        convertDartToNative_SerializedScriptValue(dictionary));
   }
-  static RtcIceCandidate _create_1(dictionary) => JS('RtcIceCandidate', 'new RTCIceCandidate(#)', dictionary);
 
   @DomName('RTCIceCandidate.candidate')
   @DocsEditable
@@ -19527,6 +19580,7 @@
   @DomName('RTCIceCandidate.sdpMid')
   @DocsEditable
   final String sdpMid;
+
 }
 // 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
@@ -19541,14 +19595,43 @@
   @DocsEditable
   final RtcIceCandidate candidate;
 }
-// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
+// 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.
 
 
-@DocsEditable
 @DomName('RTCPeerConnection')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@Experimental
 class RtcPeerConnection extends EventTarget native "*RTCPeerConnection" {
+  factory RtcPeerConnection(Map rtcIceServers, [Map mediaConstraints]) {
+    var constructorName = JS('RtcPeerConnection', 'window[#]',
+        '${_browserPropertyPrefix}RTCPeerConnection');
+    if (?mediaConstraints) {
+      return JS('RtcPeerConnection', 'new #(#,#)', constructorName,
+          convertDartToNative_SerializedScriptValue(rtcIceServers),
+          convertDartToNative_SerializedScriptValue(mediaConstraints));
+    } else {
+      return JS('RtcPeerConnection', 'new #(#)', constructorName,
+          convertDartToNative_SerializedScriptValue(rtcIceServers));
+    }
+  }
+
+  /**
+   * Checks if Real Time Communication (RTC) APIs are supported and enabled on 
+   * the current platform.
+   */
+  static bool get supported {
+    // Currently in Firefox some of the RTC elements are defined but throw an
+    // error unless the user has specifically enabled them in their 
+    // about:config. So we have to construct an element to actually test if RTC
+    // is supported at at the given time.
+    try {
+      var c = new RtcPeerConnection({"iceServers": [ {"url":"stun:foo.com"}]});
+      return c is RtcPeerConnection; 
+    } catch (_) {}
+    return false;
+  }
 
   @DomName('RTCPeerConnection.addstreamEvent')
   @DocsEditable
@@ -19578,17 +19661,6 @@
   @DocsEditable
   static const EventStreamProvider<Event> stateChangeEvent = const EventStreamProvider<Event>('statechange');
 
-  @DomName('RTCPeerConnection.RTCPeerConnection')
-  @DocsEditable
-  factory RtcPeerConnection(Map rtcIceServers, [Map mediaConstraints]) {
-    if (?mediaConstraints) {
-      return RtcPeerConnection._create_1(rtcIceServers, mediaConstraints);
-    }
-    return RtcPeerConnection._create_2(rtcIceServers);
-  }
-  static RtcPeerConnection _create_1(rtcIceServers, mediaConstraints) => JS('RtcPeerConnection', 'new RTCPeerConnection(#,#)', rtcIceServers, mediaConstraints);
-  static RtcPeerConnection _create_2(rtcIceServers) => JS('RtcPeerConnection', 'new RTCPeerConnection(#)', rtcIceServers);
-
   @DocsEditable
   @DomName('EventTarget.addEventListener, EventTarget.removeEventListener, EventTarget.dispatchEvent')
   @deprecated
@@ -19640,6 +19712,8 @@
   @DocsEditable
   void addIceCandidate(RtcIceCandidate candidate) native;
 
+  @DomName('RTCPeerConnection.addStream')
+  @DocsEditable
   void addStream(MediaStream stream, [Map mediaConstraints]) {
     if (?mediaConstraints) {
       var mediaConstraints_1 = convertDartToNative_Dictionary(mediaConstraints);
@@ -19662,6 +19736,8 @@
   @DocsEditable
   void close() native;
 
+  @DomName('RTCPeerConnection.createAnswer')
+  @DocsEditable
   void createAnswer(RtcSessionDescriptionCallback successCallback, [RtcErrorCallback failureCallback, Map mediaConstraints]) {
     if (?mediaConstraints) {
       var mediaConstraints_1 = convertDartToNative_Dictionary(mediaConstraints);
@@ -19680,6 +19756,8 @@
   @DocsEditable
   void _createAnswer_2(RtcSessionDescriptionCallback successCallback, RtcErrorCallback failureCallback) native;
 
+  @DomName('RTCPeerConnection.createDataChannel')
+  @DocsEditable
   RtcDataChannel createDataChannel(String label, [Map options]) {
     if (?options) {
       var options_1 = convertDartToNative_Dictionary(options);
@@ -19696,6 +19774,8 @@
   @DocsEditable
   RtcDataChannel _createDataChannel_2(label) native;
 
+  @DomName('RTCPeerConnection.createOffer')
+  @DocsEditable
   void createOffer(RtcSessionDescriptionCallback successCallback, [RtcErrorCallback failureCallback, Map mediaConstraints]) {
     if (?mediaConstraints) {
       var mediaConstraints_1 = convertDartToNative_Dictionary(mediaConstraints);
@@ -19739,6 +19819,8 @@
   @DocsEditable
   void setRemoteDescription(RtcSessionDescription description, [VoidCallback successCallback, RtcErrorCallback failureCallback]) native;
 
+  @DomName('RTCPeerConnection.updateIce')
+  @DocsEditable
   void updateIce([Map configuration, Map mediaConstraints]) {
     if (?mediaConstraints) {
       var configuration_1 = convertDartToNative_Dictionary(configuration);
@@ -19794,8 +19876,11 @@
   @DomName('RTCPeerConnection.onstatechange')
   @DocsEditable
   Stream<Event> get onStateChange => stateChangeEvent.forTarget(this);
+
 }
 
+
+
 @DocsEditable
 @deprecated
 class RtcPeerConnectionEvents extends Events {
@@ -19820,21 +19905,19 @@
   @DocsEditable
   EventListenerList get stateChange => this['statechange'];
 }
-// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
+// 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.
 
 
-@DocsEditable
 @DomName('RTCSessionDescription')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@Experimental
 class RtcSessionDescription native "*RTCSessionDescription" {
-
-  @DomName('RTCSessionDescription.RTCSessionDescription')
-  @DocsEditable
   factory RtcSessionDescription(Map dictionary) {
-    return RtcSessionDescription._create_1(dictionary);
+    return JS('RtcSessionDescription', 'new RTCSessionDescription(#)',
+        convertDartToNative_SerializedScriptValue(dictionary));
   }
-  static RtcSessionDescription _create_1(dictionary) => JS('RtcSessionDescription', 'new RTCSessionDescription(#)', dictionary);
 
   @DomName('RTCSessionDescription.sdp')
   @DocsEditable
@@ -19843,6 +19926,7 @@
   @DomName('RTCSessionDescription.type')
   @DocsEditable
   String type;
+
 }
 // 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
@@ -21470,6 +21554,9 @@
 
   // -- end List<Map> mixins.
 
+  @DomName('SQLResultSetRowList.item')
+  @DocsEditable
+  @Creates('=Object')
   Map item(int index) {
     return convertNativeToDart_Dictionary(_item_1(index));
   }
@@ -22921,21 +23008,37 @@
   @Returns('Element|Document')
   final dynamic _target;
 
+  @JSName('webkitForce')
   @DomName('Touch.webkitForce')
   @DocsEditable
-  final num webkitForce;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  final num force;
 
+  @JSName('webkitRadiusX')
   @DomName('Touch.webkitRadiusX')
   @DocsEditable
-  final int webkitRadiusX;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  final int radiusX;
 
+  @JSName('webkitRadiusY')
   @DomName('Touch.webkitRadiusY')
   @DocsEditable
-  final int webkitRadiusY;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  final int radiusY;
 
+  @JSName('webkitRotationAngle')
   @DomName('Touch.webkitRotationAngle')
   @DocsEditable
-  final num webkitRotationAngle;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  final num rotationAngle;
 }
 // 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
@@ -24428,41 +24531,73 @@
   @DocsEditable
   final int videoWidth;
 
+  @JSName('webkitDecodedFrameCount')
   @DomName('HTMLVideoElement.webkitDecodedFrameCount')
   @DocsEditable
-  final int webkitDecodedFrameCount;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  final int decodedFrameCount;
 
+  @JSName('webkitDisplayingFullscreen')
   @DomName('HTMLVideoElement.webkitDisplayingFullscreen')
   @DocsEditable
-  final bool webkitDisplayingFullscreen;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  final bool displayingFullscreen;
 
+  @JSName('webkitDroppedFrameCount')
   @DomName('HTMLVideoElement.webkitDroppedFrameCount')
   @DocsEditable
-  final int webkitDroppedFrameCount;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  final int droppedFrameCount;
 
+  @JSName('webkitSupportsFullscreen')
   @DomName('HTMLVideoElement.webkitSupportsFullscreen')
   @DocsEditable
-  final bool webkitSupportsFullscreen;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  final bool supportsFullscreen;
 
   @DomName('HTMLVideoElement.width')
   @DocsEditable
   int width;
 
+  @JSName('webkitEnterFullScreen')
   @DomName('HTMLVideoElement.webkitEnterFullScreen')
   @DocsEditable
-  void webkitEnterFullScreen() native;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void enterFullScreen() native;
 
+  @JSName('webkitEnterFullscreen')
   @DomName('HTMLVideoElement.webkitEnterFullscreen')
   @DocsEditable
-  void webkitEnterFullscreen() native;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void enterFullscreen() native;
 
+  @JSName('webkitExitFullScreen')
   @DomName('HTMLVideoElement.webkitExitFullScreen')
   @DocsEditable
-  void webkitExitFullScreen() native;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void exitFullScreen() native;
 
+  @JSName('webkitExitFullscreen')
   @DomName('HTMLVideoElement.webkitExitFullscreen')
   @DocsEditable
-  void webkitExitFullscreen() native;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void exitFullscreen() 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
@@ -25667,6 +25802,8 @@
   @DocsEditable
   void stencilOpSeparate(int face, int fail, int zfail, int zpass) native;
 
+  @DomName('WebGLRenderingContext.texImage2D')
+  @DocsEditable
   void texImage2D(int target, int level, int internalformat, int format_OR_width, int height_OR_type, border_OR_canvas_OR_image_OR_pixels_OR_video, [int format, int type, ArrayBufferView pixels]) {
     if ((border_OR_canvas_OR_image_OR_pixels_OR_video is int || border_OR_canvas_OR_image_OR_pixels_OR_video == null)) {
       _texImage2D_1(target, level, internalformat, format_OR_width, height_OR_type, border_OR_canvas_OR_image_OR_pixels_OR_video, format, type, pixels);
@@ -25720,6 +25857,8 @@
   @DocsEditable
   void texParameteri(int target, int pname, int param) native;
 
+  @DomName('WebGLRenderingContext.texSubImage2D')
+  @DocsEditable
   void texSubImage2D(int target, int level, int xoffset, int yoffset, int format_OR_width, int height_OR_type, canvas_OR_format_OR_image_OR_pixels_OR_video, [int type, ArrayBufferView pixels]) {
     if ((canvas_OR_format_OR_image_OR_pixels_OR_video is int || canvas_OR_format_OR_image_OR_pixels_OR_video == null)) {
       _texSubImage2D_1(target, level, xoffset, yoffset, format_OR_width, height_OR_type, canvas_OR_format_OR_image_OR_pixels_OR_video, type, pixels);
@@ -26064,27 +26203,6 @@
 
 
 @DocsEditable
-@DomName('WebKitTransitionEvent')
-class WebKitTransitionEvent extends Event native "*WebKitTransitionEvent" {
-
-  @DomName('WebKitTransitionEvent.elapsedTime')
-  @DocsEditable
-  final num elapsedTime;
-
-  @DomName('WebKitTransitionEvent.propertyName')
-  @DocsEditable
-  final String propertyName;
-
-  @DomName('WebKitTransitionEvent.pseudoElement')
-  @DocsEditable
-  final String pseudoElement;
-}
-// 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.
-
-
-@DocsEditable
 /**
  * Use the WebSocket interface to connect to a WebSocket,
  * and to send and receive data on that WebSocket.
@@ -26335,9 +26453,13 @@
   }
 
 
+  @JSName('webkitDirectionInvertedFromDevice')
   @DomName('WheelEvent.webkitDirectionInvertedFromDevice')
   @DocsEditable
-  final bool webkitDirectionInvertedFromDevice;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  final bool directionInvertedFromDevice;
 
   @JSName('initWebKitWheelEvent')
   @DomName('WheelEvent.initWebKitWheelEvent')
@@ -26692,14 +26814,23 @@
 
   @DomName('DOMWindow.webkitAnimationEndEvent')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   static const EventStreamProvider<AnimationEvent> animationEndEvent = const EventStreamProvider<AnimationEvent>('webkitAnimationEnd');
 
   @DomName('DOMWindow.webkitAnimationIterationEvent')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   static const EventStreamProvider<AnimationEvent> animationIterationEvent = const EventStreamProvider<AnimationEvent>('webkitAnimationIteration');
 
   @DomName('DOMWindow.webkitAnimationStartEvent')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   static const EventStreamProvider<AnimationEvent> animationStartEvent = const EventStreamProvider<AnimationEvent>('webkitAnimationStart');
 
   @DocsEditable
@@ -26899,9 +27030,13 @@
   @Experimental
   final NotificationCenter notifications;
 
+  @JSName('webkitStorageInfo')
   @DomName('DOMWindow.webkitStorageInfo')
   @DocsEditable
-  final StorageInfo webkitStorageInfo;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  final StorageInfo storageInfo;
 
   WindowBase get window => _convertNativeToDart_Window(this._window);
   @JSName('window')
@@ -26993,6 +27128,8 @@
   @Creates('DatabaseSync')
   Database openDatabase(String name, String version, String displayName, int estimatedSize, [DatabaseCallback creationCallback]) native;
 
+  @DomName('DOMWindow.postMessage')
+  @DocsEditable
   void postMessage(/*SerializedScriptValue*/ message, String targetOrigin, [List messagePorts]) {
     if (?message && !?messagePorts) {
       var message_1 = convertDartToNative_SerializedScriptValue(message);
@@ -27560,6 +27697,8 @@
   WorkerEvents get on =>
     new WorkerEvents(this);
 
+  @DomName('Worker.postMessage')
+  @DocsEditable
   void postMessage(/*SerializedScriptValue*/ message, [List messagePorts]) {
     if (?messagePorts) {
       var message_1 = convertDartToNative_SerializedScriptValue(message);
@@ -27631,9 +27770,13 @@
   @DocsEditable
   final WorkerContext self;
 
+  @JSName('webkitNotifications')
   @DomName('WorkerContext.webkitNotifications')
   @DocsEditable
-  final NotificationCenter webkitNotifications;
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  final NotificationCenter notifications;
 
   @JSName('addEventListener')
   @DomName('WorkerContext.addEventListener')
@@ -29563,6 +29706,230 @@
 
 
 @DocsEditable
+@DomName('NamedNodeMap')
+class _NamedNodeMap implements JavaScriptIndexingBehavior, List<Node> native "*NamedNodeMap" {
+
+  @DomName('NamedNodeMap.length')
+  @DocsEditable
+  int get length => JS("int", "#.length", this);
+
+  Node operator[](int index) => JS("Node", "#[#]", this, index);
+
+  void operator[]=(int index, Node value) {
+    throw new UnsupportedError("Cannot assign element of immutable List.");
+  }
+  // -- 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);
+  }
+
+  dynamic reduce(dynamic initialValue, dynamic combine(dynamic, Node)) {
+    return IterableMixinWorkaround.reduce(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);
+
+  List mappedBy(f(Node element)) =>
+      IterableMixinWorkaround.mappedByList(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() => new List<Node>.from(this);
+  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 firstMatching(bool test(Node value), { Node orElse() }) {
+    return IterableMixinWorkaround.firstMatching(this, test, orElse);
+  }
+
+  Node lastMatching(bool test(Node value), {Node orElse()}) {
+    return IterableMixinWorkaround.lastMatchingInList(this, test, orElse);
+  }
+
+  Node singleMatching(bool test(Node value)) {
+    return IterableMixinWorkaround.singleMatching(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 addLast(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.");
+  }
+
+  List<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");
+  }
+
+  Node min([int compare(Node a, Node b)]) =>
+      IterableMixinWorkaround.min(this, compare);
+
+  Node max([int compare(Node a, Node b)]) =>
+      IterableMixinWorkaround.max(this, compare);
+
+  Node removeAt(int pos) {
+    throw new UnsupportedError("Cannot remove from immutable List.");
+  }
+
+  Node removeLast() {
+    throw new UnsupportedError("Cannot remove from immutable List.");
+  }
+
+  void remove(Object object) {
+    throw new UnsupportedError("Cannot remove from immutable List.");
+  }
+
+  void removeAll(Iterable elements) {
+    throw new UnsupportedError("Cannot remove from immutable List.");
+  }
+
+  void retainAll(Iterable elements) {
+    throw new UnsupportedError("Cannot remove from immutable List.");
+  }
+
+  void removeMatching(bool test(Node element)) {
+    throw new UnsupportedError("Cannot remove from immutable List.");
+  }
+
+  void retainMatching(bool test(Node element)) {
+    throw new UnsupportedError("Cannot remove from immutable List.");
+  }
+
+  void setRange(int start, int rangeLength, List<Node> from, [int startFrom]) {
+    throw new UnsupportedError("Cannot setRange on immutable List.");
+  }
+
+  void removeRange(int start, int rangeLength) {
+    throw new UnsupportedError("Cannot removeRange on immutable List.");
+  }
+
+  void insertRange(int start, int rangeLength, [Node initialValue]) {
+    throw new UnsupportedError("Cannot insertRange on immutable List.");
+  }
+
+  List<Node> getRange(int start, int rangeLength) =>
+      Lists.getRange(this, start, rangeLength, <Node>[]);
+
+  // -- end List<Node> mixins.
+
+  @DomName('NamedNodeMap.getNamedItem')
+  @DocsEditable
+  Node getNamedItem(String name) native;
+
+  @DomName('NamedNodeMap.getNamedItemNS')
+  @DocsEditable
+  Node getNamedItemNS(String namespaceURI, String localName) native;
+
+  @DomName('NamedNodeMap.item')
+  @DocsEditable
+  Node item(int index) native;
+
+  @DomName('NamedNodeMap.removeNamedItem')
+  @DocsEditable
+  Node removeNamedItem(String name) native;
+
+  @DomName('NamedNodeMap.removeNamedItemNS')
+  @DocsEditable
+  Node removeNamedItemNS(String namespaceURI, String localName) native;
+
+  @DomName('NamedNodeMap.setNamedItem')
+  @DocsEditable
+  Node setNamedItem(Node node) native;
+
+  @DomName('NamedNodeMap.setNamedItemNS')
+  @DocsEditable
+  Node setNamedItemNS(Node node) 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
+// BSD-style license that can be found in the LICENSE file.
+
+
+@DocsEditable
 @DomName('SpeechInputResultList')
 class _SpeechInputResultList implements JavaScriptIndexingBehavior, List<SpeechInputResult> native "*SpeechInputResultList" {
 
@@ -30162,6 +30529,28 @@
 // BSD-style license that can be found in the LICENSE file.
 
 
+// This class maps WebKitTransitionEvent to TransitionEvent for older Chrome
+// browser versions.
+@DomName('WebKitTransitionEvent')
+class _WebKitTransitionEvent extends Event implements TransitionEvent  native "*WebKitTransitionEvent" {
+
+  @DomName('WebKitTransitionEvent.elapsedTime')
+  @DocsEditable
+  final num elapsedTime;
+
+  @DomName('WebKitTransitionEvent.propertyName')
+  @DocsEditable
+  final String propertyName;
+
+  @DomName('WebKitTransitionEvent.pseudoElement')
+  @DocsEditable
+  final String pseudoElement;
+}
+// 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.
+
+
 abstract class _AttributeMap implements Map<String, String> {
   final Element _element;
 
@@ -30855,6 +31244,17 @@
   Stream<T> forTarget(EventTarget e, {bool useCapture: false}) {
     return new _EventStream(e, _eventType, useCapture);
   }
+
+  /**
+   * Gets the type of the event which this would listen for on the specified
+   * event target.
+   *
+   * The target is necessary because some browsers may use different event names
+   * for the same purpose and the target allows differentiating browser support.
+   */
+  String getEventType(EventTarget target) {
+    return _eventType;
+  }
 }
 
 /**
@@ -30870,6 +31270,10 @@
   Stream<T> forTarget(EventTarget e, {bool useCapture: false}) {
     return new _EventStream(e, _eventTypeGetter(e), useCapture);
   }
+
+  String getEventType(EventTarget target) {
+    return _eventTypeGetter(target);
+  }
 }
 // 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/html/dartium/html_dartium.dart b/sdk/lib/html/dartium/html_dartium.dart
index 50b3922a..c5cd920 100644
--- a/sdk/lib/html/dartium/html_dartium.dart
+++ b/sdk/lib/html/dartium/html_dartium.dart
@@ -1568,31 +1568,38 @@
 
   @DomName('CanvasRenderingContext2D.webkitBackingStorePixelRatio')
   @DocsEditable
-  num get webkitBackingStorePixelRatio native "CanvasRenderingContext2D_webkitBackingStorePixelRatio_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  num get backingStorePixelRatio native "CanvasRenderingContext2D_webkitBackingStorePixelRatio_Getter";
 
   @DomName('CanvasRenderingContext2D.webkitImageSmoothingEnabled')
   @DocsEditable
-  bool get webkitImageSmoothingEnabled native "CanvasRenderingContext2D_webkitImageSmoothingEnabled_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  bool get imageSmoothingEnabled native "CanvasRenderingContext2D_webkitImageSmoothingEnabled_Getter";
 
   @DomName('CanvasRenderingContext2D.webkitImageSmoothingEnabled')
   @DocsEditable
-  void set webkitImageSmoothingEnabled(bool value) native "CanvasRenderingContext2D_webkitImageSmoothingEnabled_Setter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void set imageSmoothingEnabled(bool value) native "CanvasRenderingContext2D_webkitImageSmoothingEnabled_Setter";
 
   @DomName('CanvasRenderingContext2D.webkitLineDash')
   @DocsEditable
-  List get webkitLineDash native "CanvasRenderingContext2D_webkitLineDash_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  List get lineDash native "CanvasRenderingContext2D_webkitLineDash_Getter";
 
   @DomName('CanvasRenderingContext2D.webkitLineDash')
   @DocsEditable
-  void set webkitLineDash(List value) native "CanvasRenderingContext2D_webkitLineDash_Setter";
-
-  @DomName('CanvasRenderingContext2D.webkitLineDashOffset')
-  @DocsEditable
-  num get webkitLineDashOffset native "CanvasRenderingContext2D_webkitLineDashOffset_Getter";
-
-  @DomName('CanvasRenderingContext2D.webkitLineDashOffset')
-  @DocsEditable
-  void set webkitLineDashOffset(num value) native "CanvasRenderingContext2D_webkitLineDashOffset_Setter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void set lineDash(List value) native "CanvasRenderingContext2D_webkitLineDash_Setter";
 
   @DomName('CanvasRenderingContext2D.arc')
   @DocsEditable
@@ -1932,9 +1939,12 @@
 
   @DomName('CanvasRenderingContext2D.webkitGetImageDataHD')
   @DocsEditable
-  ImageData webkitGetImageDataHD(num sx, num sy, num sw, num sh) native "CanvasRenderingContext2D_webkitGetImageDataHD_Callback";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  ImageData getImageDataHD(num sx, num sy, num sw, num sh) native "CanvasRenderingContext2D_webkitGetImageDataHD_Callback";
 
-  void webkitPutImageDataHD(ImageData imagedata, num dx, num dy, [num dirtyX, num dirtyY, num dirtyWidth, num dirtyHeight]) {
+  void putImageDataHD(ImageData imagedata, num dx, num dy, [num dirtyX, num dirtyY, num dirtyWidth, num dirtyHeight]) {
     if ((imagedata is ImageData || imagedata == null) && (dx is num || dx == null) && (dy is num || dy == null) && !?dirtyX && !?dirtyY && !?dirtyWidth && !?dirtyHeight) {
       _webkitPutImageDataHD_1(imagedata, dx, dy);
       return;
@@ -1990,6 +2000,7 @@
   void setStrokeColorHsl(int h, num s, num l, [num a = 1]) {
     this.strokeStyle = 'hsla($h, $s%, $l%, $a)';
   }
+
 }
 // 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
@@ -2995,6 +3006,24 @@
   return _cachedBrowserPrefix;
 }
 
+String _cachedBrowserPropertyPrefix;
+
+/// Prefix as used for JS property names.
+String get _browserPropertyPrefix {
+  if (_cachedBrowserPropertyPrefix == null) {
+    if (_Device.isFirefox) {
+      _cachedBrowserPropertyPrefix = 'moz';
+    } else if (_Device.isIE) {
+      _cachedBrowserPropertyPrefix = 'ms';
+    } else if (_Device.isOpera) {
+      _cachedBrowserPropertyPrefix = 'o';
+    } else {
+      _cachedBrowserPropertyPrefix = 'webkit';
+    }
+  }
+  return _cachedBrowserPropertyPrefix;
+}
+
 @DomName('CSSStyleDeclaration')
 class CssStyleDeclaration extends NativeFieldWrapperClass1 {
   factory CssStyleDeclaration() => _CssStyleDeclarationFactoryProvider.createCssStyleDeclaration();
@@ -3057,6 +3086,10 @@
     return propValue != null ? propValue : '';
   }
 
+  /**
+   * Checks to see if CSS Transitions are supported.
+   */
+  static bool get supportsTransitions => true;
 
   // TODO(jacobr): generate this list of properties using the existing script.
   /** Gets the value of "align-content" */
@@ -5985,10 +6018,18 @@
   }
 
   /** Gets the value of "transition" */
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.FIREFOX)
+  @SupportedBrowser(SupportedBrowser.IE, '10')
+  @SupportedBrowser(SupportedBrowser.SAFARI)
   String get transition =>
     getPropertyValue('${_browserPrefix}transition');
 
   /** Sets the value of "transition" */
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.FIREFOX)
+  @SupportedBrowser(SupportedBrowser.IE, '10')
+  @SupportedBrowser(SupportedBrowser.SAFARI)
   void set transition(String value) {
     setProperty('${_browserPrefix}transition', value, '');
   }
@@ -6551,7 +6592,10 @@
 
   @DomName('DataTransferItem.webkitGetAsEntry')
   @DocsEditable
-  Entry webkitGetAsEntry() native "DataTransferItem_webkitGetAsEntry_Callback";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  Entry getAsEntry() native "DataTransferItem_webkitGetAsEntry_Callback";
 
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
@@ -7192,10 +7236,16 @@
 
   @DomName('Document.webkitpointerlockchangeEvent')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   static const EventStreamProvider<Event> pointerLockChangeEvent = const EventStreamProvider<Event>('webkitpointerlockchange');
 
   @DomName('Document.webkitpointerlockerrorEvent')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   static const EventStreamProvider<Event> pointerLockErrorEvent = const EventStreamProvider<Event>('webkitpointerlockerror');
 
   @DocsEditable
@@ -7295,30 +7345,48 @@
   /// Moved to [HtmlDocument].
   @DomName('Document.webkitFullscreenElement')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   Element get $dom_webkitFullscreenElement native "Document_webkitFullscreenElement_Getter";
 
   /// Moved to [HtmlDocument].
   @DomName('Document.webkitFullscreenEnabled')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   bool get $dom_webkitFullscreenEnabled native "Document_webkitFullscreenEnabled_Getter";
 
   /// Moved to [HtmlDocument].
   @DomName('Document.webkitHidden')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   bool get $dom_webkitHidden native "Document_webkitHidden_Getter";
 
   /// Moved to [HtmlDocument].
   @DomName('Document.webkitIsFullScreen')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   bool get $dom_webkitIsFullScreen native "Document_webkitIsFullScreen_Getter";
 
   /// Moved to [HtmlDocument].
   @DomName('Document.webkitPointerLockElement')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   Element get $dom_webkitPointerLockElement native "Document_webkitPointerLockElement_Getter";
 
   @DomName('Document.webkitVisibilityState')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   String get $dom_webkitVisibilityState native "Document_webkitVisibilityState_Getter";
 
   /// Use the [Range] constructor instead.
@@ -7427,16 +7495,25 @@
   /// Moved to [HtmlDocument].
   @DomName('Document.webkitCancelFullScreen')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   void $dom_webkitCancelFullScreen() native "Document_webkitCancelFullScreen_Callback";
 
   /// Moved to [HtmlDocument].
   @DomName('Document.webkitExitFullscreen')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   void $dom_webkitExitFullscreen() native "Document_webkitExitFullscreen_Callback";
 
   /// Moved to [HtmlDocument].
   @DomName('Document.webkitExitPointerLock')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   void $dom_webkitExitPointerLock() native "Document_webkitExitPointerLock_Callback";
 
   @DomName('Document.onabort')
@@ -9631,6 +9708,12 @@
     return e is Element && !(e is UnknownElement);
   }
 
+  /**
+   * Called by the DOM when this element has been instantiated.
+   */
+  @Experimental
+  void onCreated() {}
+
   // Hooks to support custom WebComponents.
   /**
    * Experimental support for [web components][wc]. This field stores a
@@ -9829,14 +9912,23 @@
 
   @DomName('Element.webkitTransitionEndEvent')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   static const EventStreamProvider<TransitionEvent> transitionEndEvent = const EventStreamProvider<TransitionEvent>('webkitTransitionEnd');
 
   @DomName('Element.webkitfullscreenchangeEvent')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   static const EventStreamProvider<Event> fullscreenChangeEvent = const EventStreamProvider<Event>('webkitfullscreenchange');
 
   @DomName('Element.webkitfullscreenerrorEvent')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   static const EventStreamProvider<Event> fullscreenErrorEvent = const EventStreamProvider<Event>('webkitfullscreenerror');
 
   @DocsEditable
@@ -9873,7 +9965,7 @@
 
   bool translate;
 
-  String webkitdropzone;
+  String dropzone;
 
   void click();
 
@@ -9987,15 +10079,24 @@
 
   @DomName('Element.webkitPseudo')
   @DocsEditable
-  String get webkitPseudo native "Element_webkitPseudo_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  String get pseudo native "Element_webkitPseudo_Getter";
 
   @DomName('Element.webkitPseudo')
   @DocsEditable
-  void set webkitPseudo(String value) native "Element_webkitPseudo_Setter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void set pseudo(String value) native "Element_webkitPseudo_Setter";
 
   @DomName('Element.webkitShadowRoot')
   @DocsEditable
-  ShadowRoot get webkitShadowRoot native "Element_webkitShadowRoot_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  ShadowRoot get shadowRoot native "Element_webkitShadowRoot_Getter";
 
   @DomName('Element.blur')
   @DocsEditable
@@ -10103,15 +10204,24 @@
 
   @DomName('Element.webkitRequestFullScreen')
   @DocsEditable
-  void webkitRequestFullScreen(int flags) native "Element_webkitRequestFullScreen_Callback";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void requestFullScreen(int flags) native "Element_webkitRequestFullScreen_Callback";
 
   @DomName('Element.webkitRequestFullscreen')
   @DocsEditable
-  void webkitRequestFullscreen() native "Element_webkitRequestFullscreen_Callback";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void requestFullscreen() native "Element_webkitRequestFullscreen_Callback";
 
   @DomName('Element.webkitRequestPointerLock')
   @DocsEditable
-  void webkitRequestPointerLock() native "Element_webkitRequestPointerLock_Callback";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void requestPointerLock() native "Element_webkitRequestPointerLock_Callback";
 
   @DomName('Element.onabort')
   @DocsEditable
@@ -10295,6 +10405,10 @@
 
   @DomName('Element.onwebkitTransitionEnd')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.FIREFOX)
+  @SupportedBrowser(SupportedBrowser.IE, '10')
+  @SupportedBrowser(SupportedBrowser.SAFARI)
   Stream<TransitionEvent> get onTransitionEnd => transitionEndEvent.forTarget(this);
 
   @DomName('Element.onwebkitfullscreenchange')
@@ -11385,7 +11499,10 @@
 
   @DomName('File.webkitRelativePath')
   @DocsEditable
-  String get webkitRelativePath native "File_webkitRelativePath_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  String get relativePath native "File_webkitRelativePath_Getter";
 
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
@@ -12874,6 +12991,9 @@
 
 
 @DocsEditable
+/**
+ * An `<hr>` tag.
+ */
 @DomName('HTMLHRElement')
 class HRElement extends _Element_Merged {
   HRElement.internal() : super.internal();
@@ -13473,6 +13593,17 @@
     document.$dom_body = value;
   }
 
+  /**
+   * Registers a custom Element subclass as an available HTML tag.
+   *
+   * Not yet implemented.
+   */
+  @Experimental
+  void register(String tagName, Type elementClass) {
+    // TODO: tagName validation
+    throw new Exception('Not yet implemented');
+  }
+
   @DomName('Document.caretRangeFromPoint')
   Range caretRangeFromPoint(int x, int y) {
     return document.$dom_caretRangeFromPoint(x, y);
@@ -14565,6 +14696,9 @@
 
   @DomName('HTMLInputElement.webkitSpeechChangeEvent')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   static const EventStreamProvider<Event> speechChangeEvent = const EventStreamProvider<Event>('webkitSpeechChange');
 
   @DocsEditable
@@ -14899,31 +15033,52 @@
 
   @DomName('HTMLInputElement.webkitEntries')
   @DocsEditable
-  List<Entry> get webkitEntries native "HTMLInputElement_webkitEntries_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  List<Entry> get entries native "HTMLInputElement_webkitEntries_Getter";
 
   @DomName('HTMLInputElement.webkitGrammar')
   @DocsEditable
-  bool get webkitGrammar native "HTMLInputElement_webkitGrammar_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  bool get grammar native "HTMLInputElement_webkitGrammar_Getter";
 
   @DomName('HTMLInputElement.webkitGrammar')
   @DocsEditable
-  void set webkitGrammar(bool value) native "HTMLInputElement_webkitGrammar_Setter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void set grammar(bool value) native "HTMLInputElement_webkitGrammar_Setter";
 
   @DomName('HTMLInputElement.webkitSpeech')
   @DocsEditable
-  bool get webkitSpeech native "HTMLInputElement_webkitSpeech_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  bool get speech native "HTMLInputElement_webkitSpeech_Getter";
 
   @DomName('HTMLInputElement.webkitSpeech')
   @DocsEditable
-  void set webkitSpeech(bool value) native "HTMLInputElement_webkitSpeech_Setter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void set speech(bool value) native "HTMLInputElement_webkitSpeech_Setter";
 
   @DomName('HTMLInputElement.webkitdirectory')
   @DocsEditable
-  bool get webkitdirectory native "HTMLInputElement_webkitdirectory_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  bool get directory native "HTMLInputElement_webkitdirectory_Getter";
 
   @DomName('HTMLInputElement.webkitdirectory')
   @DocsEditable
-  void set webkitdirectory(bool value) native "HTMLInputElement_webkitdirectory_Setter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void set directory(bool value) native "HTMLInputElement_webkitdirectory_Setter";
 
   @DomName('HTMLInputElement.width')
   @DocsEditable
@@ -16715,6 +16870,8 @@
 
 @DocsEditable
 @DomName('LocalMediaStream')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@Experimental
 class LocalMediaStream extends MediaStream implements EventTarget {
   LocalMediaStream.internal() : super.internal();
 
@@ -17061,18 +17218,30 @@
 
   @DomName('HTMLMediaElement.webkitkeyaddedEvent')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   static const EventStreamProvider<MediaKeyEvent> keyAddedEvent = const EventStreamProvider<MediaKeyEvent>('webkitkeyadded');
 
   @DomName('HTMLMediaElement.webkitkeyerrorEvent')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   static const EventStreamProvider<MediaKeyEvent> keyErrorEvent = const EventStreamProvider<MediaKeyEvent>('webkitkeyerror');
 
   @DomName('HTMLMediaElement.webkitkeymessageEvent')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   static const EventStreamProvider<MediaKeyEvent> keyMessageEvent = const EventStreamProvider<MediaKeyEvent>('webkitkeymessage');
 
   @DomName('HTMLMediaElement.webkitneedkeyEvent')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   static const EventStreamProvider<MediaKeyEvent> needKeyEvent = const EventStreamProvider<MediaKeyEvent>('webkitneedkey');
 
   @DocsEditable
@@ -17261,31 +17430,52 @@
 
   @DomName('HTMLMediaElement.webkitAudioDecodedByteCount')
   @DocsEditable
-  int get webkitAudioDecodedByteCount native "HTMLMediaElement_webkitAudioDecodedByteCount_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  int get audioDecodedByteCount native "HTMLMediaElement_webkitAudioDecodedByteCount_Getter";
 
   @DomName('HTMLMediaElement.webkitClosedCaptionsVisible')
   @DocsEditable
-  bool get webkitClosedCaptionsVisible native "HTMLMediaElement_webkitClosedCaptionsVisible_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  bool get closedCaptionsVisible native "HTMLMediaElement_webkitClosedCaptionsVisible_Getter";
 
   @DomName('HTMLMediaElement.webkitClosedCaptionsVisible')
   @DocsEditable
-  void set webkitClosedCaptionsVisible(bool value) native "HTMLMediaElement_webkitClosedCaptionsVisible_Setter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void set closedCaptionsVisible(bool value) native "HTMLMediaElement_webkitClosedCaptionsVisible_Setter";
 
   @DomName('HTMLMediaElement.webkitHasClosedCaptions')
   @DocsEditable
-  bool get webkitHasClosedCaptions native "HTMLMediaElement_webkitHasClosedCaptions_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  bool get hasClosedCaptions native "HTMLMediaElement_webkitHasClosedCaptions_Getter";
 
   @DomName('HTMLMediaElement.webkitPreservesPitch')
   @DocsEditable
-  bool get webkitPreservesPitch native "HTMLMediaElement_webkitPreservesPitch_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  bool get preservesPitch native "HTMLMediaElement_webkitPreservesPitch_Getter";
 
   @DomName('HTMLMediaElement.webkitPreservesPitch')
   @DocsEditable
-  void set webkitPreservesPitch(bool value) native "HTMLMediaElement_webkitPreservesPitch_Setter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void set preservesPitch(bool value) native "HTMLMediaElement_webkitPreservesPitch_Setter";
 
   @DomName('HTMLMediaElement.webkitVideoDecodedByteCount')
   @DocsEditable
-  int get webkitVideoDecodedByteCount native "HTMLMediaElement_webkitVideoDecodedByteCount_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  int get videoDecodedByteCount native "HTMLMediaElement_webkitVideoDecodedByteCount_Getter";
 
   TextTrack addTextTrack(String kind, [String label, String language]) {
     if (?language) {
@@ -17325,7 +17515,7 @@
   @DocsEditable
   void play() native "HTMLMediaElement_play_Callback";
 
-  void webkitAddKey(String keySystem, Uint8Array key, [Uint8Array initData, String sessionId]) {
+  void addKey(String keySystem, Uint8Array key, [Uint8Array initData, String sessionId]) {
     if (?initData) {
       _webkitAddKey_1(keySystem, key, initData, sessionId);
       return;
@@ -17344,9 +17534,12 @@
 
   @DomName('HTMLMediaElement.webkitCancelKeyRequest')
   @DocsEditable
-  void webkitCancelKeyRequest(String keySystem, String sessionId) native "HTMLMediaElement_webkitCancelKeyRequest_Callback";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void cancelKeyRequest(String keySystem, String sessionId) native "HTMLMediaElement_webkitCancelKeyRequest_Callback";
 
-  void webkitGenerateKeyRequest(String keySystem, [Uint8Array initData]) {
+  void generateKeyRequest(String keySystem, [Uint8Array initData]) {
     if (?initData) {
       _webkitGenerateKeyRequest_1(keySystem, initData);
       return;
@@ -17798,6 +17991,8 @@
 
 
 @DomName('MediaStream')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@Experimental
 class MediaStream extends EventTarget {
   MediaStream.internal() : super.internal();
 
@@ -17918,6 +18113,8 @@
 
 @DocsEditable
 @DomName('MediaStreamEvent')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@Experimental
 class MediaStreamEvent extends Event {
   MediaStreamEvent.internal() : super.internal();
 
@@ -17938,6 +18135,8 @@
 
 @DocsEditable
 @DomName('MediaStreamTrack')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@Experimental
 class MediaStreamTrack extends EventTarget {
   MediaStreamTrack.internal() : super.internal();
 
@@ -18033,6 +18232,8 @@
 
 @DocsEditable
 @DomName('MediaStreamTrackEvent')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@Experimental
 class MediaStreamTrackEvent extends Event {
   MediaStreamTrackEvent.internal() : super.internal();
 
@@ -18493,19 +18694,17 @@
 
   @DomName('MouseEvent.webkitMovementX')
   @DocsEditable
-  int get webkitMovementX native "MouseEvent_webkitMovementX_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  int get movementX native "MouseEvent_webkitMovementX_Getter";
 
   @DomName('MouseEvent.webkitMovementY')
   @DocsEditable
-  int get webkitMovementY native "MouseEvent_webkitMovementY_Getter";
-
-  @DomName('MouseEvent.x')
-  @DocsEditable
-  int get x native "MouseEvent_x_Getter";
-
-  @DomName('MouseEvent.y')
-  @DocsEditable
-  int get y native "MouseEvent_y_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  int get movementY native "MouseEvent_webkitMovementY_Getter";
 
   @DomName('MouseEvent.initMouseEvent')
   @DocsEditable
@@ -18724,234 +18923,6 @@
 // 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
-@DomName('NamedNodeMap')
-class NamedNodeMap extends NativeFieldWrapperClass1 implements List<Node> {
-  NamedNodeMap.internal();
-
-  @DomName('NamedNodeMap.length')
-  @DocsEditable
-  int get length native "NamedNodeMap_length_Getter";
-
-  Node operator[](int index) native "NamedNodeMap_item_Callback";
-
-  void operator[]=(int index, Node value) {
-    throw new UnsupportedError("Cannot assign element of immutable List.");
-  }
-  // -- 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);
-  }
-
-  dynamic reduce(dynamic initialValue, dynamic combine(dynamic, Node)) {
-    return IterableMixinWorkaround.reduce(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);
-
-  List mappedBy(f(Node element)) =>
-      IterableMixinWorkaround.mappedByList(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() => new List<Node>.from(this);
-  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 firstMatching(bool test(Node value), { Node orElse() }) {
-    return IterableMixinWorkaround.firstMatching(this, test, orElse);
-  }
-
-  Node lastMatching(bool test(Node value), {Node orElse()}) {
-    return IterableMixinWorkaround.lastMatchingInList(this, test, orElse);
-  }
-
-  Node singleMatching(bool test(Node value)) {
-    return IterableMixinWorkaround.singleMatching(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 addLast(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.");
-  }
-
-  List<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");
-  }
-
-  Node min([int compare(Node a, Node b)]) =>
-      IterableMixinWorkaround.min(this, compare);
-
-  Node max([int compare(Node a, Node b)]) =>
-      IterableMixinWorkaround.max(this, compare);
-
-  Node removeAt(int pos) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  Node removeLast() {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void remove(Object object) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeAll(Iterable elements) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainAll(Iterable elements) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void removeMatching(bool test(Node element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void retainMatching(bool test(Node element)) {
-    throw new UnsupportedError("Cannot remove from immutable List.");
-  }
-
-  void setRange(int start, int rangeLength, List<Node> from, [int startFrom]) {
-    throw new UnsupportedError("Cannot setRange on immutable List.");
-  }
-
-  void removeRange(int start, int rangeLength) {
-    throw new UnsupportedError("Cannot removeRange on immutable List.");
-  }
-
-  void insertRange(int start, int rangeLength, [Node initialValue]) {
-    throw new UnsupportedError("Cannot insertRange on immutable List.");
-  }
-
-  List<Node> getRange(int start, int rangeLength) =>
-      Lists.getRange(this, start, rangeLength, <Node>[]);
-
-  // -- end List<Node> mixins.
-
-  @DomName('NamedNodeMap.getNamedItem')
-  @DocsEditable
-  Node getNamedItem(String name) native "NamedNodeMap_getNamedItem_Callback";
-
-  @DomName('NamedNodeMap.getNamedItemNS')
-  @DocsEditable
-  Node getNamedItemNS(String namespaceURI, String localName) native "NamedNodeMap_getNamedItemNS_Callback";
-
-  @DomName('NamedNodeMap.item')
-  @DocsEditable
-  Node item(int index) native "NamedNodeMap_item_Callback";
-
-  @DomName('NamedNodeMap.removeNamedItem')
-  @DocsEditable
-  Node removeNamedItem(String name) native "NamedNodeMap_removeNamedItem_Callback";
-
-  @DomName('NamedNodeMap.removeNamedItemNS')
-  @DocsEditable
-  Node removeNamedItemNS(String namespaceURI, String localName) native "NamedNodeMap_removeNamedItemNS_Callback";
-
-  @DomName('NamedNodeMap.setNamedItem')
-  @DocsEditable
-  Node setNamedItem(Node node) native "NamedNodeMap_setNamedItem_Callback";
-
-  @DomName('NamedNodeMap.setNamedItemNS')
-  @DocsEditable
-  Node setNamedItemNS(Node node) native "NamedNodeMap_setNamedItemNS_Callback";
-
-}
-// 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.
-
 
 @DomName('Navigator')
 class Navigator extends NativeFieldWrapperClass1 {
@@ -19060,7 +19031,10 @@
 
   @DomName('Navigator.webkitBattery')
   @DocsEditable
-  BatteryManager get webkitBattery native "Navigator_webkitBattery_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  BatteryManager get battery native "Navigator_webkitBattery_Getter";
 
   @DomName('Navigator.getStorageUpdates')
   @DocsEditable
@@ -19072,7 +19046,10 @@
 
   @DomName('Navigator.webkitGetGamepads')
   @DocsEditable
-  List<Gamepad> webkitGetGamepads() native "Navigator_webkitGetGamepads_Callback";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  List<Gamepad> getGamepads() native "Navigator_webkitGetGamepads_Callback";
 
   @DomName('Navigator.webkitGetUserMedia')
   @DocsEditable
@@ -19189,7 +19166,7 @@
   void remove(Object object) {
     if (object is! Node) return;
     Node node = object;
-    if (!identical(this, node.parentNode)) return;
+    if (!identical(_this, node.parentNode)) return;
     _this.$dom_removeChild(node);
   }
 
@@ -19386,7 +19363,7 @@
 
   @DomName('Node.attributes')
   @DocsEditable
-  NamedNodeMap get $dom_attributes native "Node_attributes_Getter";
+  _NamedNodeMap get $dom_attributes native "Node_attributes_Getter";
 
   @DomName('Node.childNodes')
   @DocsEditable
@@ -21365,6 +21342,8 @@
 
 @DocsEditable
 @DomName('RTCIceCandidate')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@Experimental
 class RtcIceCandidate extends NativeFieldWrapperClass1 {
   RtcIceCandidate.internal();
 
@@ -21407,16 +21386,21 @@
   RtcIceCandidate get candidate native "RTCIceCandidateEvent_candidate_Getter";
 
 }
-// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
+// 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: Do not edit - generated code.
 
-
-@DocsEditable
 @DomName('RTCPeerConnection')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@Experimental
 class RtcPeerConnection extends EventTarget {
+
+  /**
+   * Checks if Real Time Communication (RTC) APIs are supported and enabled on 
+   * the current platform.
+   */
+  static bool get supported => true;
   RtcPeerConnection.internal() : super.internal();
 
   @DomName('RTCPeerConnection.addstreamEvent')
@@ -21580,6 +21564,8 @@
 
 }
 
+
+
 @DocsEditable
 @deprecated
 class RtcPeerConnectionEvents extends Events {
@@ -21613,6 +21599,8 @@
 
 @DocsEditable
 @DomName('RTCSessionDescription')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@Experimental
 class RtcSessionDescription extends NativeFieldWrapperClass1 {
   RtcSessionDescription.internal();
 
@@ -25217,19 +25205,31 @@
 
   @DomName('Touch.webkitForce')
   @DocsEditable
-  num get webkitForce native "Touch_webkitForce_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  num get force native "Touch_webkitForce_Getter";
 
   @DomName('Touch.webkitRadiusX')
   @DocsEditable
-  int get webkitRadiusX native "Touch_webkitRadiusX_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  int get radiusX native "Touch_webkitRadiusX_Getter";
 
   @DomName('Touch.webkitRadiusY')
   @DocsEditable
-  int get webkitRadiusY native "Touch_webkitRadiusY_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  int get radiusY native "Touch_webkitRadiusY_Getter";
 
   @DomName('Touch.webkitRotationAngle')
   @DocsEditable
-  num get webkitRotationAngle native "Touch_webkitRotationAngle_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  num get rotationAngle native "Touch_webkitRotationAngle_Getter";
 
 }
 // Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
@@ -26883,19 +26883,31 @@
 
   @DomName('HTMLVideoElement.webkitDecodedFrameCount')
   @DocsEditable
-  int get webkitDecodedFrameCount native "HTMLVideoElement_webkitDecodedFrameCount_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  int get decodedFrameCount native "HTMLVideoElement_webkitDecodedFrameCount_Getter";
 
   @DomName('HTMLVideoElement.webkitDisplayingFullscreen')
   @DocsEditable
-  bool get webkitDisplayingFullscreen native "HTMLVideoElement_webkitDisplayingFullscreen_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  bool get displayingFullscreen native "HTMLVideoElement_webkitDisplayingFullscreen_Getter";
 
   @DomName('HTMLVideoElement.webkitDroppedFrameCount')
   @DocsEditable
-  int get webkitDroppedFrameCount native "HTMLVideoElement_webkitDroppedFrameCount_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  int get droppedFrameCount native "HTMLVideoElement_webkitDroppedFrameCount_Getter";
 
   @DomName('HTMLVideoElement.webkitSupportsFullscreen')
   @DocsEditable
-  bool get webkitSupportsFullscreen native "HTMLVideoElement_webkitSupportsFullscreen_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  bool get supportsFullscreen native "HTMLVideoElement_webkitSupportsFullscreen_Getter";
 
   @DomName('HTMLVideoElement.width')
   @DocsEditable
@@ -26907,19 +26919,31 @@
 
   @DomName('HTMLVideoElement.webkitEnterFullScreen')
   @DocsEditable
-  void webkitEnterFullScreen() native "HTMLVideoElement_webkitEnterFullScreen_Callback";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void enterFullScreen() native "HTMLVideoElement_webkitEnterFullScreen_Callback";
 
   @DomName('HTMLVideoElement.webkitEnterFullscreen')
   @DocsEditable
-  void webkitEnterFullscreen() native "HTMLVideoElement_webkitEnterFullscreen_Callback";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void enterFullscreen() native "HTMLVideoElement_webkitEnterFullscreen_Callback";
 
   @DomName('HTMLVideoElement.webkitExitFullScreen')
   @DocsEditable
-  void webkitExitFullScreen() native "HTMLVideoElement_webkitExitFullScreen_Callback";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void exitFullScreen() native "HTMLVideoElement_webkitExitFullScreen_Callback";
 
   @DomName('HTMLVideoElement.webkitExitFullscreen')
   @DocsEditable
-  void webkitExitFullscreen() native "HTMLVideoElement_webkitExitFullscreen_Callback";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void exitFullscreen() native "HTMLVideoElement_webkitExitFullscreen_Callback";
 
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
@@ -28666,31 +28690,6 @@
 
 
 @DocsEditable
-@DomName('WebKitTransitionEvent')
-class WebKitTransitionEvent extends Event {
-  WebKitTransitionEvent.internal() : super.internal();
-
-  @DomName('WebKitTransitionEvent.elapsedTime')
-  @DocsEditable
-  num get elapsedTime native "WebKitTransitionEvent_elapsedTime_Getter";
-
-  @DomName('WebKitTransitionEvent.propertyName')
-  @DocsEditable
-  String get propertyName native "WebKitTransitionEvent_propertyName_Getter";
-
-  @DomName('WebKitTransitionEvent.pseudoElement')
-  @DocsEditable
-  String get pseudoElement native "WebKitTransitionEvent_pseudoElement_Getter";
-
-}
-// 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.
-
-
-@DocsEditable
 /**
  * Use the WebSocket interface to connect to a WebSocket,
  * and to send and receive data on that WebSocket.
@@ -28938,7 +28937,10 @@
 
   @DomName('WheelEvent.webkitDirectionInvertedFromDevice')
   @DocsEditable
-  bool get webkitDirectionInvertedFromDevice native "WheelEvent_webkitDirectionInvertedFromDevice_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  bool get directionInvertedFromDevice native "WheelEvent_webkitDirectionInvertedFromDevice_Getter";
 
   @DomName('WheelEvent.wheelDeltaX')
   @DocsEditable
@@ -29066,14 +29068,23 @@
 
   @DomName('DOMWindow.webkitAnimationEndEvent')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   static const EventStreamProvider<AnimationEvent> animationEndEvent = const EventStreamProvider<AnimationEvent>('webkitAnimationEnd');
 
   @DomName('DOMWindow.webkitAnimationIterationEvent')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   static const EventStreamProvider<AnimationEvent> animationIterationEvent = const EventStreamProvider<AnimationEvent>('webkitAnimationIteration');
 
   @DomName('DOMWindow.webkitAnimationStartEvent')
   @DocsEditable
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
   static const EventStreamProvider<AnimationEvent> animationStartEvent = const EventStreamProvider<AnimationEvent>('webkitAnimationStart');
 
   @DocsEditable
@@ -29298,7 +29309,10 @@
 
   @DomName('DOMWindow.webkitStorageInfo')
   @DocsEditable
-  StorageInfo get webkitStorageInfo native "DOMWindow_webkitStorageInfo_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  StorageInfo get storageInfo native "DOMWindow_webkitStorageInfo_Getter";
 
   @DomName('DOMWindow.window')
   @DocsEditable
@@ -29443,10 +29457,6 @@
   @DocsEditable
   void stop() native "DOMWindow_stop_Callback";
 
-  @DomName('DOMWindow.webkitCancelAnimationFrame')
-  @DocsEditable
-  void webkitCancelAnimationFrame(int id) native "DOMWindow_webkitCancelAnimationFrame_Callback";
-
   @DomName('DOMWindow.webkitConvertPointFromNodeToPage')
   @DocsEditable
   DomPoint convertPointFromNodeToPage(Node node, DomPoint p) native "DOMWindow_webkitConvertPointFromNodeToPage_Callback";
@@ -29455,10 +29465,6 @@
   @DocsEditable
   DomPoint convertPointFromPageToNode(Node node, DomPoint p) native "DOMWindow_webkitConvertPointFromPageToNode_Callback";
 
-  @DomName('DOMWindow.webkitRequestAnimationFrame')
-  @DocsEditable
-  int webkitRequestAnimationFrame(RequestAnimationFrameCallback callback) native "DOMWindow_webkitRequestAnimationFrame_Callback";
-
   @DomName('DOMWindow.webkitRequestFileSystem')
   @DocsEditable
   @SupportedBrowser(SupportedBrowser.CHROME)
@@ -30019,7 +30025,10 @@
 
   @DomName('WorkerContext.webkitNotifications')
   @DocsEditable
-  NotificationCenter get webkitNotifications native "WorkerContext_webkitNotifications_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  NotificationCenter get notifications native "WorkerContext_webkitNotifications_Getter";
 
   @DomName('WorkerContext.addEventListener')
   @DocsEditable
@@ -31174,11 +31183,17 @@
 
   @DomName('HTMLElement.webkitdropzone')
   @DocsEditable
-  String get webkitdropzone native "HTMLElement_webkitdropzone_Getter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  String get dropzone native "HTMLElement_webkitdropzone_Getter";
 
   @DomName('HTMLElement.webkitdropzone')
   @DocsEditable
-  void set webkitdropzone(String value) native "HTMLElement_webkitdropzone_Setter";
+  @SupportedBrowser(SupportedBrowser.CHROME)
+  @SupportedBrowser(SupportedBrowser.SAFARI)
+  @Experimental
+  void set dropzone(String value) native "HTMLElement_webkitdropzone_Setter";
 
   @DomName('HTMLElement.click')
   @DocsEditable
@@ -32164,6 +32179,234 @@
 
 
 @DocsEditable
+@DomName('NamedNodeMap')
+class _NamedNodeMap extends NativeFieldWrapperClass1 implements List<Node> {
+  _NamedNodeMap.internal();
+
+  @DomName('NamedNodeMap.length')
+  @DocsEditable
+  int get length native "NamedNodeMap_length_Getter";
+
+  Node operator[](int index) native "NamedNodeMap_item_Callback";
+
+  void operator[]=(int index, Node value) {
+    throw new UnsupportedError("Cannot assign element of immutable List.");
+  }
+  // -- 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);
+  }
+
+  dynamic reduce(dynamic initialValue, dynamic combine(dynamic, Node)) {
+    return IterableMixinWorkaround.reduce(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);
+
+  List mappedBy(f(Node element)) =>
+      IterableMixinWorkaround.mappedByList(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() => new List<Node>.from(this);
+  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 firstMatching(bool test(Node value), { Node orElse() }) {
+    return IterableMixinWorkaround.firstMatching(this, test, orElse);
+  }
+
+  Node lastMatching(bool test(Node value), {Node orElse()}) {
+    return IterableMixinWorkaround.lastMatchingInList(this, test, orElse);
+  }
+
+  Node singleMatching(bool test(Node value)) {
+    return IterableMixinWorkaround.singleMatching(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 addLast(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.");
+  }
+
+  List<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.le