+ Stop running `pub get` at gclient sync time. This was slow and unnecessary.
+ Stop symlinking non-dart_pkg packages into gen/dart-pkg/packages. This suffered from three problems:
   1) These symlinks were not being specified as build output by any rule. They were a magic side effect.
   2) There was a race where many symlinks to the same package could be created, the last one winning.
   3) Related to 2, if two packages relied on two different versions of the same package, we could have hard to reproduce bugs that depend upon the winner of the race.

We now have a checked in copy of all third-party Dart packages needed by the Mojo Dart SDK. These live under //mojo/public/dart/third_party. There is a script 'update_packages.py' which uses `pub get` to handle all version constraints and updates the source tree after pub is run. It's now trivial to ensure we have a compatible set of packages checked into the tree and to update the package set.

Fixes #395
Fixes #401
Fixes #403
Fixes #429

R=jamesr@chromium.org, tonyg@chromium.org, zra@google.com

Review URL: https://codereview.chromium.org/1346773002 .
diff --git a/mojo/public/dart/third_party/stack_trace/.gitignore b/mojo/public/dart/third_party/stack_trace/.gitignore
new file mode 100644
index 0000000..7dbf035
--- /dev/null
+++ b/mojo/public/dart/third_party/stack_trace/.gitignore
@@ -0,0 +1,15 @@
+# Don’t commit the following directories created by pub.
+.buildlog
+.pub/
+build/
+packages
+.packages
+
+# Or the files created by dart2js.
+*.dart.js
+*.js_
+*.js.deps
+*.js.map
+
+# Include when developing application packages.
+pubspec.lock
\ No newline at end of file
diff --git a/mojo/public/dart/third_party/stack_trace/.test_config b/mojo/public/dart/third_party/stack_trace/.test_config
new file mode 100644
index 0000000..412fc5c
--- /dev/null
+++ b/mojo/public/dart/third_party/stack_trace/.test_config
@@ -0,0 +1,3 @@
+{
+  "test_package": true
+}
\ No newline at end of file
diff --git a/mojo/public/dart/third_party/stack_trace/CHANGELOG.md b/mojo/public/dart/third_party/stack_trace/CHANGELOG.md
new file mode 100644
index 0000000..dae0db6
--- /dev/null
+++ b/mojo/public/dart/third_party/stack_trace/CHANGELOG.md
@@ -0,0 +1,145 @@
+## 1.4.2
+
+* Improve the display of `data:` URIs in stack traces.
+
+## 1.4.1
+
+* Fix a crashing bug in `UnparsedFrame.toString()`.
+
+## 1.4.0
+
+* `new Trace.parse()` and related constructors will no longer throw an exception
+  if they encounter an unparseable stack frame. Instead, they will generate an
+  `UnparsedFrame`, which exposes no metadata but preserves the frame's original
+  text.
+
+* Properly parse native-code V8 frames.
+
+## 1.3.5
+
+* Properly shorten library names for pathnames of folded frames on Windows.
+
+## 1.3.4
+
+* No longer say that stack chains aren't supported on dart2js now that
+  [sdk#15171][] is fixed. Note that this fix only applies to Dart 1.12.
+
+[sdk#15171]: https://github.com/dart-lang/sdk/issues/15171
+
+## 1.3.3
+
+* When a `null` stack trace is passed to a completer or stream controller in
+  nested `Chain.capture()` blocks, substitute the inner block's chain rather
+  than the outer block's.
+
+* Add support for empty chains and chains of empty traces to `Chain.parse()`.
+
+* Don't crash when parsing stack traces from Dart VM stack overflows.
+
+## 1.3.2
+
+* Don't crash when running `Trace.terse` on empty stack traces.
+
+## 1.3.1
+
+* Support more types of JavaScriptCore stack frames.
+
+## 1.3.0
+
+* Support stack traces generated by JavaScriptCore. They can be explicitly
+  parsed via `new Trace.parseJSCore` and `new Frame.parseJSCore`.
+
+## 1.2.4
+
+* Fix a type annotation in `LazyTrace`.
+
+## 1.2.3
+
+* Fix a crash in `Chain.parse`.
+
+## 1.2.2
+
+* Don't print the first folded frame of terse stack traces. This frame
+  is always just an internal isolate message handler anyway. This
+  improves the readability of stack traces, especially in stack chains.
+
+* Remove the line numbers and specific files in all terse folded frames, not
+  just those from core libraries.
+
+* Make padding consistent across all stack traces for `Chain.toString()`.
+
+## 1.2.1
+
+* Add `terse` to `LazyTrace.foldFrames()`.
+
+* Further improve stack chains when using the VM's async/await implementation.
+
+## 1.2.0
+
+* Add a `terse` argument to `Trace.foldFrames()` and `Chain.foldFrames()`. This
+  allows them to inherit the behavior of `Trace.terse` and `Chain.terse` without
+  having to duplicate the logic.
+
+## 1.1.3
+
+* Produce nicer-looking stack chains when using the VM's async/await
+  implementation.
+
+## 1.1.2
+
+* Support VM frames without line *or* column numbers, which async/await programs
+  occasionally generate.
+
+* Replace `<<anonymous closure>_async_body>` in VM frames' members with the
+  terser `<async>`.
+
+## 1.1.1
+
+* Widen the SDK constraint to include 1.7.0-dev.4.0.
+
+## 1.1.0
+
+* Unify the parsing of Safari and Firefox stack traces. This fixes an error in
+  Firefox trace parsing.
+
+* Deprecate `Trace.parseSafari6_0`, `Trace.parseSafari6_1`,
+  `Frame.parseSafari6_0`, and `Frame.parseSafari6_1`.
+
+* Add `Frame.parseSafari`.
+
+## 1.0.3
+
+* Use `Zone.errorCallback` to attach stack chains to all errors without the need
+  for `Chain.track`, which is now deprecated.
+
+## 1.0.2
+
+* Remove a workaround for [issue 17083][].
+
+[issue 17083]: http://code.google.com/p/dart/issues/detail?id=17083
+
+## 1.0.1
+
+* Synchronous errors in the [Chain.capture] callback are now handled correctly.
+
+## 1.0.0
+
+* No API changes, just declared stable.
+
+## 0.9.3+2
+
+* Update the dependency on path.
+
+* Improve the formatting of library URIs in stack traces.
+
+## 0.9.3+1
+
+* If an error is thrown in `Chain.capture`'s `onError` handler, that error is
+  handled by the parent zone. This matches the behavior of `runZoned` in
+  `dart:async`.
+
+## 0.9.3
+
+* Add a `Chain.foldFrames` method that parallels `Trace.foldFrames`.
+
+* Record anonymous method frames in IE10 as "<fn>".
diff --git a/mojo/public/dart/third_party/stack_trace/LICENSE b/mojo/public/dart/third_party/stack_trace/LICENSE
new file mode 100644
index 0000000..5c60afe
--- /dev/null
+++ b/mojo/public/dart/third_party/stack_trace/LICENSE
@@ -0,0 +1,26 @@
+Copyright 2014, the Dart project authors. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+    * Neither the name of Google Inc. nor the names of its
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/mojo/public/dart/third_party/stack_trace/README.md b/mojo/public/dart/third_party/stack_trace/README.md
new file mode 100644
index 0000000..fe85adb
--- /dev/null
+++ b/mojo/public/dart/third_party/stack_trace/README.md
@@ -0,0 +1,205 @@
+This library provides the ability to parse, inspect, and manipulate stack traces
+produced by the underlying Dart implementation. It also provides functions to
+produce string representations of stack traces in a more readable format than
+the native [StackTrace] implementation.
+
+`Trace`s can be parsed from native [StackTrace]s using `Trace.from`, or captured
+using `Trace.current`. Native [StackTrace]s can also be directly converted to
+human-readable strings using `Trace.format`.
+
+[StackTrace]: http://api.dartlang.org/docs/releases/latest/dart_core/StackTrace.html
+
+Here's an example native stack trace from debugging this library:
+
+    #0      Object.noSuchMethod (dart:core-patch:1884:25)
+    #1      Trace.terse.<anonymous closure> (file:///usr/local/google-old/home/goog/dart/dart/pkg/stack_trace/lib/src/trace.dart:47:21)
+    #2      IterableMixinWorkaround.reduce (dart:collection:29:29)
+    #3      List.reduce (dart:core-patch:1247:42)
+    #4      Trace.terse (file:///usr/local/google-old/home/goog/dart/dart/pkg/stack_trace/lib/src/trace.dart:40:35)
+    #5      format (file:///usr/local/google-old/home/goog/dart/dart/pkg/stack_trace/lib/stack_trace.dart:24:28)
+    #6      main.<anonymous closure> (file:///usr/local/google-old/home/goog/dart/dart/test.dart:21:29)
+    #7      _CatchErrorFuture._sendError (dart:async:525:24)
+    #8      _FutureImpl._setErrorWithoutAsyncTrace (dart:async:393:26)
+    #9      _FutureImpl._setError (dart:async:378:31)
+    #10     _ThenFuture._sendValue (dart:async:490:16)
+    #11     _FutureImpl._handleValue.<anonymous closure> (dart:async:349:28)
+    #12     Timer.run.<anonymous closure> (dart:async:2402:21)
+    #13     Timer.Timer.<anonymous closure> (dart:async-patch:15:15)
+
+and its human-readable representation:
+
+    dart:core-patch                             Object.noSuchMethod
+    pkg/stack_trace/lib/src/trace.dart 47:21    Trace.terse.<fn>
+    dart:collection                             IterableMixinWorkaround.reduce
+    dart:core-patch                             List.reduce
+    pkg/stack_trace/lib/src/trace.dart 40:35    Trace.terse
+    pkg/stack_trace/lib/stack_trace.dart 24:28  format
+    test.dart 21:29                             main.<fn>
+    dart:async                                  _CatchErrorFuture._sendError
+    dart:async                                  _FutureImpl._setErrorWithoutAsyncTrace
+    dart:async                                  _FutureImpl._setError
+    dart:async                                  _ThenFuture._sendValue
+    dart:async                                  _FutureImpl._handleValue.<fn>
+    dart:async                                  Timer.run.<fn>
+    dart:async-patch                            Timer.Timer.<fn>
+
+You can further clean up the stack trace using `Trace.terse`. This folds
+together multiple stack frames from the Dart core libraries, so that only the
+core library method that was directly called from user code is visible. For
+example:
+
+    dart:core                                   Object.noSuchMethod
+    pkg/stack_trace/lib/src/trace.dart 47:21    Trace.terse.<fn>
+    dart:core                                   List.reduce
+    pkg/stack_trace/lib/src/trace.dart 40:35    Trace.terse
+    pkg/stack_trace/lib/stack_trace.dart 24:28  format
+    test.dart 21:29                             main.<fn>
+
+## Stack Chains
+
+This library also provides the ability to capture "stack chains" with the
+`Chain` class. When writing asynchronous code, a single stack trace isn't very
+useful, since the call stack is unwound every time something async happens. A
+stack chain tracks stack traces through asynchronous calls, so that you can see
+the full path from `main` down to the error.
+
+To use stack chains, just wrap the code that you want to track in
+`Chain.capture`. This will create a new [Zone][] in which stack traces are
+recorded and woven into chains every time an asynchronous call occurs. Zones are
+sticky, too, so any asynchronous operations started in the `Chain.capture`
+callback will have their chains tracked, as will asynchronous operations they
+start and so on.
+
+Here's an example of some code that doesn't capture its stack chains:
+
+```dart
+import 'dart:async';
+
+void main() {
+  scheduleAsync();
+}
+
+void scheduleAsync() {
+  return new Future.delayed(new Duration(seconds: 1))
+      .then((_) => runAsync());
+}
+
+void runAsync() {
+  throw 'oh no!';
+}
+```
+
+If we run this, it prints the following:
+
+    Uncaught Error: oh no!
+    Stack Trace: 
+    #0      runAsync (file:///usr/local/google-old/home/goog/dart/dart/test.dart:13:3)
+    #1      scheduleAsync.<anonymous closure> (file:///usr/local/google-old/home/goog/dart/dart/test.dart:9:28)
+    #2      _rootRunUnary (dart:async/zone.dart:717)
+    #3      _RootZone.runUnary (dart:async/zone.dart:854)
+    #4      _Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:488)
+    #5      _Future._propagateToListeners (dart:async/future_impl.dart:571)
+    #6      _Future._complete (dart:async/future_impl.dart:317)
+    #7      _SyncCompleter.complete (dart:async/future_impl.dart:44)
+    #8      Future.Future.delayed.<anonymous closure> (dart:async/future.dart:219)
+    #9      _createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:11)
+    #10     _handleTimeout (dart:io/timer_impl.dart:292)
+    #11     _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:115)
+
+Notice how there's no mention of `main` in that stack trace. All we know is that
+the error was in `runAsync`; we don't know why `runAsync` was called.
+
+Now let's look at the same code with stack chains captured:
+
+```dart
+import 'dart:async';
+
+import 'package:stack_trace/stack_trace.dart';
+
+void main() {
+  Chain.capture(() {
+    scheduleAsync();
+  });
+}
+
+void scheduleAsync() {
+  new Future.delayed(new Duration(seconds: 1))
+      .then((_) => runAsync());
+}
+
+void runAsync() {
+  throw 'oh no!';
+}
+```
+
+Now if we run it, it prints this:
+
+    Uncaught Error: oh no!
+    Stack Trace: 
+    test.dart 17:3                                                runAsync
+    test.dart 13:28                                               scheduleAsync.<fn>
+    package:stack_trace/src/stack_zone_specification.dart 129:26  registerUnaryCallback.<fn>.<fn>
+    package:stack_trace/src/stack_zone_specification.dart 174:15  StackZoneSpecification._run
+    package:stack_trace/src/stack_zone_specification.dart 177:7   StackZoneSpecification._run
+    package:stack_trace/src/stack_zone_specification.dart 175:7   StackZoneSpecification._run
+    package:stack_trace/src/stack_zone_specification.dart 129:18  registerUnaryCallback.<fn>
+    dart:async/zone.dart 717                                      _rootRunUnary
+    dart:async/zone.dart 449                                      _ZoneDelegate.runUnary
+    dart:async/zone.dart 654                                      _CustomizedZone.runUnary
+    dart:async/future_impl.dart 488                               _Future._propagateToListeners.handleValueCallback
+    dart:async/future_impl.dart 571                               _Future._propagateToListeners
+    dart:async/future_impl.dart 317                               _Future._complete
+    dart:async/future_impl.dart 44                                _SyncCompleter.complete
+    dart:async/future.dart 219                                    Future.Future.delayed.<fn>
+    package:stack_trace/src/stack_zone_specification.dart 174:15  StackZoneSpecification._run
+    package:stack_trace/src/stack_zone_specification.dart 119:52  registerCallback.<fn>
+    dart:async/zone.dart 706                                      _rootRun
+    dart:async/zone.dart 440                                      _ZoneDelegate.run
+    dart:async/zone.dart 650                                      _CustomizedZone.run
+    dart:async/zone.dart 561                                      _BaseZone.runGuarded
+    dart:async/zone.dart 586                                      _BaseZone.bindCallback.<fn>
+    package:stack_trace/src/stack_zone_specification.dart 174:15  StackZoneSpecification._run
+    package:stack_trace/src/stack_zone_specification.dart 119:52  registerCallback.<fn>
+    dart:async/zone.dart 710                                      _rootRun
+    dart:async/zone.dart 440                                      _ZoneDelegate.run
+    dart:async/zone.dart 650                                      _CustomizedZone.run
+    dart:async/zone.dart 561                                      _BaseZone.runGuarded
+    dart:async/zone.dart 586                                      _BaseZone.bindCallback.<fn>
+    dart:async-patch/timer_patch.dart 11                          _createTimer.<fn>
+    dart:io/timer_impl.dart 292                                   _handleTimeout
+    dart:isolate-patch/isolate_patch.dart 115                     _RawReceivePortImpl._handleMessage
+    ===== asynchronous gap ===========================
+    dart:async/zone.dart 476                                      _ZoneDelegate.registerUnaryCallback
+    dart:async/zone.dart 666                                      _CustomizedZone.registerUnaryCallback
+    dart:async/future_impl.dart 164                               _Future._Future._then
+    dart:async/future_impl.dart 187                               _Future.then
+    test.dart 13:12                                               scheduleAsync
+    test.dart 7:18                                                main.<fn>
+    dart:async/zone.dart 710                                      _rootRun
+    dart:async/zone.dart 440                                      _ZoneDelegate.run
+    dart:async/zone.dart 650                                      _CustomizedZone.run
+    dart:async/zone.dart 944                                      runZoned
+    package:stack_trace/src/chain.dart 93:20                      Chain.capture
+    test.dart 6:16                                                main
+    dart:isolate-patch/isolate_patch.dart 216                     _startIsolate.isolateStartHandler
+    dart:isolate-patch/isolate_patch.dart 115                     _RawReceivePortImpl._handleMessage
+
+That's a lot of text! If you look closely, though, you can see that `main` is
+listed in the first trace in the chain.
+
+Thankfully, you can call `Chain.terse` just like `Trace.terse` to get rid of all
+the frames you don't care about. The terse version of the stack chain above is
+this:
+
+    test.dart 17:3       runAsync
+    test.dart 13:28      scheduleAsync.<fn>
+    ===== asynchronous gap ===========================
+    dart:async           _Future.then
+    test.dart 13:12      scheduleAsync
+    test.dart 7:18       main.<fn>
+    package:stack_trace  Chain.capture
+    test.dart 6:16       main
+
+That's a lot easier to understand!
+
+[Zone]: https://api.dartlang.org/apidocs/channels/stable/#dart-async.Zone
diff --git a/mojo/public/dart/third_party/stack_trace/codereview.settings b/mojo/public/dart/third_party/stack_trace/codereview.settings
new file mode 100644
index 0000000..aff5c37
--- /dev/null
+++ b/mojo/public/dart/third_party/stack_trace/codereview.settings
@@ -0,0 +1,3 @@
+CODE_REVIEW_SERVER: http://codereview.chromium.org/
+VIEW_VC: https://github.com/dart-lang/stack_trace/commit/
+CC_LIST: reviews@dartlang.org
\ No newline at end of file
diff --git a/mojo/public/dart/third_party/stack_trace/lib/src/chain.dart b/mojo/public/dart/third_party/stack_trace/lib/src/chain.dart
new file mode 100644
index 0000000..acd17e9
--- /dev/null
+++ b/mojo/public/dart/third_party/stack_trace/lib/src/chain.dart
@@ -0,0 +1,196 @@
+// 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 stack_trace.chain;
+
+import 'dart:async';
+import 'dart:collection';
+import 'dart:math' as math;
+
+import 'frame.dart';
+import 'stack_zone_specification.dart';
+import 'trace.dart';
+import 'utils.dart';
+
+/// A function that handles errors in the zone wrapped by [Chain.capture].
+typedef void ChainHandler(error, Chain chain);
+
+/// The line used in the string representation of stack chains to represent
+/// the gap between traces.
+const _gap = '===== asynchronous gap ===========================\n';
+
+/// A chain of stack traces.
+///
+/// A stack chain is a collection of one or more stack traces that collectively
+/// represent the path from [main] through nested function calls to a particular
+/// code location, usually where an error was thrown. Multiple stack traces are
+/// necessary when using asynchronous functions, since the program's stack is
+/// reset before each asynchronous callback is run.
+///
+/// Stack chains can be automatically tracked using [Chain.capture]. This sets
+/// up a new [Zone] in which the current stack chain is tracked and can be
+/// accessed using [new Chain.current]. Any errors that would be top-leveled in
+/// the zone can be handled, along with their associated chains, with the
+/// `onError` callback. For example:
+///
+///     Chain.capture(() {
+///       // ...
+///     }, onError: (error, stackChain) {
+///       print("Caught error $error\n"
+///             "$stackChain");
+///     });
+class Chain implements StackTrace {
+
+  /// The stack traces that make up this chain.
+  ///
+  /// Like the frames in a stack trace, the traces are ordered from most local
+  /// to least local. The first one is the trace where the actual exception was
+  /// raised, the second one is where that callback was scheduled, and so on.
+  final List<Trace> traces;
+
+  /// The [StackZoneSpecification] for the current zone.
+  static StackZoneSpecification get _currentSpec =>
+    Zone.current[#stack_trace.stack_zone.spec];
+
+  /// Runs [callback] in a [Zone] in which the current stack chain is tracked
+  /// and automatically associated with (most) errors.
+  ///
+  /// If [onError] is passed, any error in the zone that would otherwise go
+  /// unhandled is passed to it, along with the [Chain] associated with that
+  /// error. Note that if [callback] produces multiple unhandled errors,
+  /// [onError] may be called more than once. If [onError] isn't passed, the
+  /// parent Zone's `unhandledErrorHandler` will be called with the error and
+  /// its chain.
+  ///
+  /// Note that even if [onError] isn't passed, this zone will still be an error
+  /// zone. This means that any errors that would cross the zone boundary are
+  /// considered unhandled.
+  ///
+  /// If [callback] returns a value, it will be returned by [capture] as well.
+  static capture(callback(), {ChainHandler onError}) {
+    var spec = new StackZoneSpecification(onError);
+    return runZoned(() {
+      try {
+        return callback();
+      } catch (error, stackTrace) {
+        // TODO(nweiz): Don't special-case this when issue 19566 is fixed.
+        return Zone.current.handleUncaughtError(error, stackTrace);
+      }
+    }, zoneSpecification: spec.toSpec(), zoneValues: {
+      #stack_trace.stack_zone.spec: spec
+    });
+  }
+
+  /// Returns [futureOrStream] unmodified.
+  ///
+  /// Prior to Dart 1.7, this was necessary to ensure that stack traces for
+  /// exceptions reported with [Completer.completeError] and
+  /// [StreamController.addError] were tracked correctly.
+  @Deprecated("Chain.track is not necessary in Dart 1.7+.")
+  static track(futureOrStream) => futureOrStream;
+
+  /// Returns the current stack chain.
+  ///
+  /// By default, the first frame of the first trace will be the line where
+  /// [Chain.current] is called. If [level] is passed, the first trace will
+  /// start that many frames up instead.
+  ///
+  /// If this is called outside of a [capture] zone, it just returns a
+  /// single-trace chain.
+  factory Chain.current([int level=0]) {
+    if (_currentSpec != null) return _currentSpec.currentChain(level + 1);
+    return new Chain([new Trace.current(level + 1)]);
+  }
+
+  /// Returns the stack chain associated with [trace].
+  ///
+  /// The first stack trace in the returned chain will always be [trace]
+  /// (converted to a [Trace] if necessary). If there is no chain associated
+  /// with [trace] or if this is called outside of a [capture] zone, this just
+  /// returns a single-trace chain containing [trace].
+  ///
+  /// If [trace] is already a [Chain], it will be returned as-is.
+  factory Chain.forTrace(StackTrace trace) {
+    if (trace is Chain) return trace;
+    if (_currentSpec == null) return new Chain([new Trace.from(trace)]);
+    return _currentSpec.chainFor(trace);
+  }
+
+  /// Parses a string representation of a stack chain.
+  ///
+  /// Specifically, this parses the output of [Chain.toString].
+  factory Chain.parse(String chain) {
+    if (chain.isEmpty) return new Chain([]);
+    return new Chain(
+        chain.split(_gap).map((trace) => new Trace.parseFriendly(trace)));
+  }
+
+  /// Returns a new [Chain] comprised of [traces].
+  Chain(Iterable<Trace> traces)
+      : traces = new UnmodifiableListView<Trace>(traces.toList());
+
+  /// Returns a terser version of [this].
+  ///
+  /// This calls [Trace.terse] on every trace in [traces], and discards any
+  /// trace that contain only internal frames.
+  Chain get terse => foldFrames((_) => false, terse: true);
+
+  /// Returns a new [Chain] based on [this] where multiple stack frames matching
+  /// [predicate] are folded together.
+  ///
+  /// This means that whenever there are multiple frames in a row that match
+  /// [predicate], only the last one is kept. In addition, traces that are
+  /// composed entirely of frames matching [predicate] are omitted.
+  ///
+  /// This is useful for limiting the amount of library code that appears in a
+  /// stack trace by only showing user code and code that's called by user code.
+  ///
+  /// If [terse] is true, this will also fold together frames from the core
+  /// library or from this package, and simplify core library frames as in
+  /// [Trace.terse].
+  Chain foldFrames(bool predicate(Frame frame), {bool terse: false}) {
+    var foldedTraces = traces.map(
+        (trace) => trace.foldFrames(predicate, terse: terse));
+    var nonEmptyTraces = foldedTraces.where((trace) {
+      // Ignore traces that contain only folded frames.
+      if (trace.frames.length > 1) return true;
+
+      // In terse mode, the trace may have removed an outer folded frame,
+      // leaving a single non-folded frame. We can detect a folded frame because
+      // it has no line information.
+      if (!terse) return false;
+      return trace.frames.single.line != null;
+    });
+
+    // If all the traces contain only internal processing, preserve the last
+    // (top-most) one so that the chain isn't empty.
+    if (nonEmptyTraces.isEmpty && foldedTraces.isNotEmpty) {
+      return new Chain([foldedTraces.last]);
+    }
+
+    return new Chain(nonEmptyTraces);
+  }
+
+  /// Converts [this] to a [Trace].
+  ///
+  /// The trace version of a chain is just the concatenation of all the traces
+  /// in the chain.
+  Trace toTrace() => new Trace(flatten(traces.map((trace) => trace.frames)));
+
+  String toString() {
+    // Figure out the longest path so we know how much to pad.
+    var longest = traces.map((trace) {
+      return trace.frames.map((frame) => frame.location.length)
+          .fold(0, math.max);
+    }).fold(0, math.max);
+
+    // Don't call out to [Trace.toString] here because that doesn't ensure that
+    // padding is consistent across all traces.
+    return traces.map((trace) {
+      return trace.frames.map((frame) {
+        return '${padRight(frame.location, longest)}  ${frame.member}\n';
+      }).join();
+    }).join(_gap);
+  }
+}
diff --git a/mojo/public/dart/third_party/stack_trace/lib/src/frame.dart b/mojo/public/dart/third_party/stack_trace/lib/src/frame.dart
new file mode 100644
index 0000000..4077c7c
--- /dev/null
+++ b/mojo/public/dart/third_party/stack_trace/lib/src/frame.dart
@@ -0,0 +1,306 @@
+// 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 frame;
+
+import 'package:path/path.dart' as path;
+
+import 'trace.dart';
+import 'unparsed_frame.dart';
+
+// #1      Foo._bar (file:///home/nweiz/code/stuff.dart:42:21)
+// #1      Foo._bar (file:///home/nweiz/code/stuff.dart:42)
+// #1      Foo._bar (file:///home/nweiz/code/stuff.dart)
+final _vmFrame = new RegExp(r'^#\d+\s+(\S.*) \((.+?)((?::\d+){0,2})\)$');
+
+//     at Object.stringify (native)
+//     at VW.call$0 (http://pub.dartlang.org/stuff.dart.js:560:28)
+//     at VW.call$0 (eval as fn
+//         (http://pub.dartlang.org/stuff.dart.js:560:28), efn:3:28)
+//     at http://pub.dartlang.org/stuff.dart.js:560:28
+final _v8Frame = new RegExp(
+    r'^\s*at (?:(\S.*?)(?: \[as [^\]]+\])? \((.*)\)|(.*))$');
+
+// http://pub.dartlang.org/stuff.dart.js:560:28
+final _v8UrlLocation = new RegExp(r'^(.*):(\d+):(\d+)|native$');
+
+// eval as function (http://pub.dartlang.org/stuff.dart.js:560:28), efn:3:28
+// eval as function (http://pub.dartlang.org/stuff.dart.js:560:28)
+// eval as function (eval as otherFunction
+//     (http://pub.dartlang.org/stuff.dart.js:560:28))
+final _v8EvalLocation = new RegExp(
+    r'^eval at (?:\S.*?) \((.*)\)(?:, .*?:\d+:\d+)?$');
+
+// .VW.call$0@http://pub.dartlang.org/stuff.dart.js:560
+// .VW.call$0("arg")@http://pub.dartlang.org/stuff.dart.js:560
+// .VW.call$0/name<@http://pub.dartlang.org/stuff.dart.js:560
+// .VW.call$0@http://pub.dartlang.org/stuff.dart.js:560:36
+// http://pub.dartlang.org/stuff.dart.js:560
+final _firefoxSafariFrame = new RegExp(
+    r'^'
+    r'(?:' // Member description. Not present in some Safari frames.
+      r'([^@(/]*)' // The actual name of the member.
+      r'(?:\(.*\))?' // Arguments to the member, sometimes captured by Firefox.
+      r'((?:/[^/]*)*)' // Extra characters indicating a nested closure.
+      r'(?:\(.*\))?' // Arguments to the closure.
+      r'@'
+    r')?'
+    r'(.*?)' // The frame's URL.
+    r':'
+    r'(\d*)' // The line number. Empty in Safari if it's unknown.
+    r'(?::(\d*))?' // The column number. Not present in older browsers and
+                   // empty in Safari if it's unknown.
+    r'$');
+
+// foo/bar.dart 10:11 in Foo._bar
+// http://dartlang.org/foo/bar.dart in Foo._bar
+final _friendlyFrame = new RegExp(
+    r'^(\S+)(?: (\d+)(?::(\d+))?)?\s+([^\d]\S*)$');
+
+/// A regular expression that matches asynchronous member names generated by the
+/// VM.
+final _asyncBody = new RegExp(r'<(<anonymous closure>|[^>]+)_async_body>');
+
+final _initialDot = new RegExp(r"^\.");
+
+/// A single stack frame. Each frame points to a precise location in Dart code.
+class Frame {
+  /// The URI of the file in which the code is located.
+  ///
+  /// This URI will usually have the scheme `dart`, `file`, `http`, or `https`.
+  final Uri uri;
+
+  /// The line number on which the code location is located.
+  ///
+  /// This can be null, indicating that the line number is unknown or
+  /// unimportant.
+  final int line;
+
+  /// The column number of the code location.
+  ///
+  /// This can be null, indicating that the column number is unknown or
+  /// unimportant.
+  final int column;
+
+  /// The name of the member in which the code location occurs.
+  ///
+  /// Anonymous closures are represented as `<fn>` in this member string.
+  final String member;
+
+  /// Whether this stack frame comes from the Dart core libraries.
+  bool get isCore => uri.scheme == 'dart';
+
+  /// Returns a human-friendly description of the library that this stack frame
+  /// comes from.
+  ///
+  /// This will usually be the string form of [uri], but a relative URI will be
+  /// used if possible. Data URIs will be truncated.
+  String get library {
+    if (uri.scheme == 'data') return "data:...";
+    return path.prettyUri(uri);
+  }
+
+  /// Returns the name of the package this stack frame comes from, or `null` if
+  /// this stack frame doesn't come from a `package:` URL.
+  String get package {
+    if (uri.scheme != 'package') return null;
+    return uri.path.split('/').first;
+  }
+
+  /// A human-friendly description of the code location.
+  String get location {
+    if (line == null) return library;
+    if (column == null) return '$library $line';
+    return '$library $line:$column';
+  }
+
+  /// Returns a single frame of the current stack.
+  ///
+  /// By default, this will return the frame above the current method. If
+  /// [level] is `0`, it will return the current method's frame; if [level] is
+  /// higher than `1`, it will return higher frames.
+  factory Frame.caller([int level=1]) {
+    if (level < 0) {
+      throw new ArgumentError("Argument [level] must be greater than or equal "
+          "to 0.");
+    }
+
+    return new Trace.current(level + 1).frames.first;
+  }
+
+  /// Parses a string representation of a Dart VM stack frame.
+  factory Frame.parseVM(String frame) => _catchFormatException(frame, () {
+    // The VM sometimes folds multiple stack frames together and replaces them
+    // with "...".
+    if (frame == '...') {
+      return new Frame(new Uri(), null, null, '...');
+    }
+
+    var match = _vmFrame.firstMatch(frame);
+    if (match == null) return new UnparsedFrame(frame);
+
+    // Get the pieces out of the regexp match. Function, URI and line should
+    // always be found. The column is optional.
+    var member = match[1]
+        .replaceAll(_asyncBody, "<async>")
+        .replaceAll("<anonymous closure>", "<fn>");
+    var uri = Uri.parse(match[2]);
+
+    var lineAndColumn = match[3].split(':');
+    var line = lineAndColumn.length > 1 ? int.parse(lineAndColumn[1]) : null;
+    var column = lineAndColumn.length > 2 ? int.parse(lineAndColumn[2]) : null;
+    return new Frame(uri, line, column, member);
+  });
+
+  /// Parses a string representation of a Chrome/V8 stack frame.
+  factory Frame.parseV8(String frame) => _catchFormatException(frame, () {
+    var match = _v8Frame.firstMatch(frame);
+    if (match == null) return new UnparsedFrame(frame);
+
+    // v8 location strings can be arbitrarily-nested, since it adds a layer of
+    // nesting for each eval performed on that line.
+    parseLocation(location, member) {
+      var evalMatch = _v8EvalLocation.firstMatch(location);
+      while (evalMatch != null) {
+        location = evalMatch[1];
+        evalMatch = _v8EvalLocation.firstMatch(location);
+      }
+
+      if (location == 'native') {
+        return new Frame(Uri.parse('native'), null, null, member);
+      }
+
+      var urlMatch = _v8UrlLocation.firstMatch(location);
+      if (urlMatch == null) return new UnparsedFrame(frame);
+
+      return new Frame(
+          _uriOrPathToUri(urlMatch[1]),
+          int.parse(urlMatch[2]),
+          int.parse(urlMatch[3]),
+          member);
+    }
+
+    // V8 stack frames can be in two forms.
+    if (match[2] != null) {
+      // The first form looks like " at FUNCTION (LOCATION)". V8 proper lists
+      // anonymous functions within eval as "<anonymous>", while IE10 lists them
+      // as "Anonymous function".
+      return parseLocation(match[2],
+          match[1].replaceAll("<anonymous>", "<fn>")
+                  .replaceAll("Anonymous function", "<fn>"));
+    } else {
+      // The second form looks like " at LOCATION", and is used for anonymous
+      // functions.
+      return parseLocation(match[3], "<fn>");
+    }
+  });
+
+  /// Parses a string representation of a JavaScriptCore stack trace.
+  factory Frame.parseJSCore(String frame) => new Frame.parseV8(frame);
+
+  /// Parses a string representation of an IE stack frame.
+  ///
+  /// IE10+ frames look just like V8 frames. Prior to IE10, stack traces can't
+  /// be retrieved.
+  factory Frame.parseIE(String frame) => new Frame.parseV8(frame);
+
+  /// Parses a string representation of a Firefox stack frame.
+  factory Frame.parseFirefox(String frame) => _catchFormatException(frame, () {
+    var match = _firefoxSafariFrame.firstMatch(frame);
+    if (match == null) return new UnparsedFrame(frame);
+
+    // Normally this is a URI, but in a jsshell trace it can be a path.
+    var uri = _uriOrPathToUri(match[3]);
+
+    var member;
+    if (match[1] != null) {
+      member = match[1];
+      member +=
+          new List.filled('/'.allMatches(match[2]).length, ".<fn>").join();
+      if (member == '') member = '<fn>';
+
+      // Some Firefox members have initial dots. We remove them for consistency
+      // with other platforms.
+      member = member.replaceFirst(_initialDot, '');
+    } else {
+      member = '<fn>';
+    }
+
+    var line = match[4] == '' ? null : int.parse(match[4]);
+    var column = match[5] == null || match[5] == '' ?
+        null : int.parse(match[5]);
+    return new Frame(uri, line, column, member);
+  });
+
+  /// Parses a string representation of a Safari 6.0 stack frame.
+  @Deprecated("Use Frame.parseSafari instead.")
+  factory Frame.parseSafari6_0(String frame) => new Frame.parseFirefox(frame);
+
+  /// Parses a string representation of a Safari 6.1+ stack frame.
+  @Deprecated("Use Frame.parseSafari instead.")
+  factory Frame.parseSafari6_1(String frame) => new Frame.parseFirefox(frame);
+
+  /// Parses a string representation of a Safari stack frame.
+  factory Frame.parseSafari(String frame) => new Frame.parseFirefox(frame);
+
+  /// Parses this package's string representation of a stack frame.
+  factory Frame.parseFriendly(String frame) => _catchFormatException(frame, () {
+    var match = _friendlyFrame.firstMatch(frame);
+    if (match == null) {
+      throw new FormatException(
+          "Couldn't parse package:stack_trace stack trace line '$frame'.");
+    }
+
+    var uri = Uri.parse(match[1]);
+    // If there's no scheme, this is a relative URI. We should interpret it as
+    // relative to the current working directory.
+    if (uri.scheme == '') {
+      uri = path.toUri(path.absolute(path.fromUri(uri)));
+    }
+
+    var line = match[2] == null ? null : int.parse(match[2]);
+    var column = match[3] == null ? null : int.parse(match[3]);
+    return new Frame(uri, line, column, match[4]);
+  });
+
+  /// A regular expression matching an absolute URI.
+  static final _uriRegExp = new RegExp(r'^[a-zA-Z][-+.a-zA-Z\d]*://');
+
+  /// A regular expression matching a Windows path.
+  static final _windowsRegExp = new RegExp(r'^([a-zA-Z]:[\\/]|\\\\)');
+
+  /// Converts [uriOrPath], which can be a URI, a Windows path, or a Posix path,
+  /// to a URI (absolute if possible).
+  static Uri _uriOrPathToUri(String uriOrPath) {
+    if (uriOrPath.contains(_uriRegExp)) {
+      return Uri.parse(uriOrPath);
+    } else if (uriOrPath.contains(_windowsRegExp)) {
+      return new Uri.file(uriOrPath, windows: true);
+    } else if (uriOrPath.startsWith('/')) {
+      return new Uri.file(uriOrPath, windows: false);
+    }
+
+    // As far as I've seen, Firefox and V8 both always report absolute paths in
+    // their stack frames. However, if we do get a relative path, we should
+    // handle it gracefully.
+    if (uriOrPath.contains('\\')) return path.windows.toUri(uriOrPath);
+    return Uri.parse(uriOrPath);
+  }
+
+  /// Runs [body] and returns its result.
+  ///
+  /// If [body] throws a [FormatException], returns an [UnparsedFrame] with
+  /// [text] instead.
+  static Frame _catchFormatException(String text, Frame body()) {
+    try {
+      return body();
+    } on FormatException catch (_) {
+      return new UnparsedFrame(text);
+    }
+  }
+
+  Frame(this.uri, this.line, this.column, this.member);
+
+  String toString() => '$location in $member';
+}
diff --git a/mojo/public/dart/third_party/stack_trace/lib/src/lazy_trace.dart b/mojo/public/dart/third_party/stack_trace/lib/src/lazy_trace.dart
new file mode 100644
index 0000000..6bfe51c
--- /dev/null
+++ b/mojo/public/dart/third_party/stack_trace/lib/src/lazy_trace.dart
@@ -0,0 +1,36 @@
+// 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 lazy_trace;
+
+import 'frame.dart';
+import 'trace.dart';
+
+/// A thunk for lazily constructing a [Trace].
+typedef Trace TraceThunk();
+
+/// A wrapper around a [TraceThunk]. This works around issue 9579 by avoiding
+/// the conversion of native [StackTrace]s to strings until it's absolutely
+/// necessary.
+class LazyTrace implements Trace {
+  final TraceThunk _thunk;
+  Trace _inner;
+
+  LazyTrace(this._thunk);
+
+  Trace get _trace {
+    if (_inner == null) _inner = _thunk();
+    return _inner;
+  }
+
+  List<Frame> get frames => _trace.frames;
+  StackTrace get vmTrace => _trace.vmTrace;
+  Trace get terse => new LazyTrace(() => _trace.terse);
+  Trace foldFrames(bool predicate(Frame frame), {bool terse: false}) =>
+    new LazyTrace(() => _trace.foldFrames(predicate, terse: terse));
+  String toString() => _trace.toString();
+
+  // Work around issue 14075.
+  set frames(_) => throw new UnimplementedError();
+}
diff --git a/mojo/public/dart/third_party/stack_trace/lib/src/stack_zone_specification.dart b/mojo/public/dart/third_party/stack_trace/lib/src/stack_zone_specification.dart
new file mode 100644
index 0000000..25d9642
--- /dev/null
+++ b/mojo/public/dart/third_party/stack_trace/lib/src/stack_zone_specification.dart
@@ -0,0 +1,236 @@
+// 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 stack_trace.stack_zone_specification;
+
+import 'dart:async';
+
+import 'trace.dart';
+import 'chain.dart';
+
+/// A class encapsulating the zone specification for a [Chain.capture] zone.
+///
+/// Until they're materialized and exposed to the user, stack chains are tracked
+/// as linked lists of [Trace]s using the [_Node] class. These nodes are stored
+/// in three distinct ways:
+///
+/// * When a callback is registered, a node is created and stored as a captured
+///   local variable until the callback is run.
+///
+/// * When a callback is run, its captured node is set as the [_currentNode] so
+///   it can be available to [Chain.current] and to be linked into additional
+///   chains when more callbacks are scheduled.
+///
+/// * When a callback throws an error or a Future or Stream emits an error, the
+///   current node is associated with that error's stack trace using the
+///   [_chains] expando.
+///
+/// Since [ZoneSpecification] can't be extended or even implemented, in order to
+/// get a real [ZoneSpecification] instance it's necessary to call [toSpec].
+class StackZoneSpecification {
+  /// The expando that associates stack chains with [StackTrace]s.
+  ///
+  /// The chains are associated with stack traces rather than errors themselves
+  /// because it's a common practice to throw strings as errors, which can't be
+  /// used with expandos.
+  ///
+  /// The chain associated with a given stack trace doesn't contain a node for
+  /// that stack trace.
+  final _chains = new Expando<_Node>("stack chains");
+
+  /// The error handler for the zone.
+  ///
+  /// If this is null, that indicates that any unhandled errors should be passed
+  /// to the parent zone.
+  final ChainHandler _onError;
+
+  /// The most recent node of the current stack chain.
+  _Node _currentNode;
+
+  StackZoneSpecification([this._onError]);
+
+  /// Converts [this] to a real [ZoneSpecification].
+  ZoneSpecification toSpec() {
+    return new ZoneSpecification(
+        handleUncaughtError: handleUncaughtError,
+        registerCallback: registerCallback,
+        registerUnaryCallback: registerUnaryCallback,
+        registerBinaryCallback: registerBinaryCallback,
+        errorCallback: errorCallback);
+  }
+
+  /// Returns the current stack chain.
+  ///
+  /// By default, the first frame of the first trace will be the line where
+  /// [currentChain] is called. If [level] is passed, the first trace will start
+  /// that many frames up instead.
+  Chain currentChain([int level=0]) => _createNode(level + 1).toChain();
+
+  /// Returns the stack chain associated with [trace], if one exists.
+  ///
+  /// The first stack trace in the returned chain will always be [trace]
+  /// (converted to a [Trace] if necessary). If there is no chain associated
+  /// with [trace], this just returns a single-trace chain containing [trace].
+  Chain chainFor(StackTrace trace) {
+    if (trace is Chain) return trace;
+    var previous = trace == null ? null : _chains[trace];
+    return new _Node(trace, previous).toChain();
+  }
+
+  /// Ensures that an error emitted by [future] has the correct stack
+  /// information associated with it.
+  ///
+  /// By default, the first frame of the first trace will be the line where
+  /// [trackFuture] is called. If [level] is passed, the first trace will start
+  /// that many frames up instead.
+  Future trackFuture(Future future, [int level=0]) {
+    var completer = new Completer.sync();
+    var node = _createNode(level + 1);
+    future.then(completer.complete).catchError((e, stackTrace) {
+      if (stackTrace == null) stackTrace = new Trace.current();
+      if (stackTrace is! Chain && _chains[stackTrace] == null) {
+        _chains[stackTrace] = node;
+      }
+      completer.completeError(e, stackTrace);
+    });
+    return completer.future;
+  }
+
+  /// Ensures that any errors emitted by [stream] have the correct stack
+  /// information associated with them.
+  ///
+  /// By default, the first frame of the first trace will be the line where
+  /// [trackStream] is called. If [level] is passed, the first trace will start
+  /// that many frames up instead.
+  Stream trackStream(Stream stream, [int level=0]) {
+    var node = _createNode(level + 1);
+    return stream.transform(new StreamTransformer.fromHandlers(
+        handleError: (error, stackTrace, sink) {
+      if (stackTrace == null) stackTrace = new Trace.current();
+      if (stackTrace is! Chain && _chains[stackTrace] == null) {
+        _chains[stackTrace] = node;
+      }
+      sink.addError(error, stackTrace);
+    }));
+  }
+
+  /// Tracks the current stack chain so it can be set to [_currentChain] when
+  /// [f] is run.
+  ZoneCallback registerCallback(Zone self, ZoneDelegate parent, Zone zone,
+      Function f) {
+    if (f == null) return parent.registerCallback(zone, null);
+    var node = _createNode(1);
+    return parent.registerCallback(zone, () => _run(f, node));
+  }
+
+  /// Tracks the current stack chain so it can be set to [_currentChain] when
+  /// [f] is run.
+  ZoneUnaryCallback registerUnaryCallback(Zone self, ZoneDelegate parent,
+      Zone zone, Function f) {
+    if (f == null) return parent.registerUnaryCallback(zone, null);
+    var node = _createNode(1);
+    return parent.registerUnaryCallback(zone, (arg) {
+      return _run(() => f(arg), node);
+    });
+  }
+
+  /// Tracks the current stack chain so it can be set to [_currentChain] when
+  /// [f] is run.
+  ZoneBinaryCallback registerBinaryCallback(Zone self, ZoneDelegate parent,
+      Zone zone, Function f) {
+    if (f == null) return parent.registerBinaryCallback(zone, null);
+    var node = _createNode(1);
+    return parent.registerBinaryCallback(zone, (arg1, arg2) {
+      return _run(() => f(arg1, arg2), node);
+    });
+  }
+
+  /// Looks up the chain associated with [stackTrace] and passes it either to
+  /// [_onError] or [parent]'s error handler.
+  handleUncaughtError(Zone self, ZoneDelegate parent, Zone zone, error,
+      StackTrace stackTrace) {
+    var stackChain = chainFor(stackTrace);
+    if (_onError == null) {
+      return parent.handleUncaughtError(zone, error, stackChain);
+    }
+
+    // TODO(nweiz): Currently this copies a lot of logic from [runZoned]. Just
+    // allow [runBinary] to throw instead once issue 18134 is fixed.
+    try {
+      return parent.runBinary(zone, _onError, error, stackChain);
+    } catch (newError, newStackTrace) {
+      if (identical(newError, error)) {
+        return parent.handleUncaughtError(zone, error, stackChain);
+      } else {
+        return parent.handleUncaughtError(zone, newError, newStackTrace);
+      }
+    }
+  }
+
+  /// Attaches the current stack chain to [stackTrace], replacing it if
+  /// necessary.
+  AsyncError errorCallback(Zone self, ZoneDelegate parent, Zone zone,
+      Object error, StackTrace stackTrace) {
+    // Go up two levels to get through [_CustomZone.errorCallback].
+    if (stackTrace == null) {
+      stackTrace = _createNode(2).toChain();
+    } else {
+      if (_chains[stackTrace] == null) _chains[stackTrace] = _createNode(2);
+    }
+
+    var asyncError = parent.errorCallback(zone, error, stackTrace);
+    return asyncError == null ? new AsyncError(error, stackTrace) : asyncError;
+  }
+
+  /// Creates a [_Node] with the current stack trace and linked to
+  /// [_currentNode].
+  ///
+  /// By default, the first frame of the first trace will be the line where
+  /// [_createNode] is called. If [level] is passed, the first trace will start
+  /// that many frames up instead.
+  _Node _createNode([int level=0]) =>
+    new _Node(new Trace.current(level + 1), _currentNode);
+
+  // TODO(nweiz): use a more robust way of detecting and tracking errors when
+  // issue 15105 is fixed.
+  /// Runs [f] with [_currentNode] set to [node].
+  ///
+  /// If [f] throws an error, this associates [node] with that error's stack
+  /// trace.
+  _run(Function f, _Node node) {
+    var previousNode = _currentNode;
+    _currentNode = node;
+    try {
+      return f();
+    } catch (e, stackTrace) {
+      _chains[stackTrace] = node;
+      rethrow;
+    } finally {
+      _currentNode = previousNode;
+    }
+  }
+}
+
+/// A linked list node representing a single entry in a stack chain.
+class _Node {
+  /// The stack trace for this link of the chain.
+  final Trace trace;
+
+  /// The previous node in the chain.
+  final _Node previous;
+
+  _Node(StackTrace trace, [this.previous])
+      : trace = trace == null ? new Trace.current() : new Trace.from(trace);
+
+  /// Converts this to a [Chain].
+  Chain toChain() {
+    var nodes = <Trace>[];
+    var node = this;
+    while (node != null) {
+      nodes.add(node.trace);
+      node = node.previous;
+    }
+    return new Chain(nodes);
+  }
+}
diff --git a/mojo/public/dart/third_party/stack_trace/lib/src/trace.dart b/mojo/public/dart/third_party/stack_trace/lib/src/trace.dart
new file mode 100644
index 0000000..f615cd2
--- /dev/null
+++ b/mojo/public/dart/third_party/stack_trace/lib/src/trace.dart
@@ -0,0 +1,298 @@
+// 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 trace;
+
+import 'dart:collection';
+import 'dart:math' as math;
+
+import 'chain.dart';
+import 'frame.dart';
+import 'lazy_trace.dart';
+import 'unparsed_frame.dart';
+import 'utils.dart';
+import 'vm_trace.dart';
+
+final _terseRegExp = new RegExp(r"(-patch)?([/\\].*)?$");
+
+/// A RegExp to match V8's stack traces.
+///
+/// V8's traces start with a line that's either just "Error" or else is a
+/// description of the exception that occurred. That description can be multiple
+/// lines, so we just look for any line other than the first that begins with
+/// three or four spaces and "at".
+final _v8Trace = new RegExp(r"\n    ?at ");
+
+/// A RegExp to match indidual lines of V8's stack traces.
+///
+/// This is intended to filter out the leading exception details of the trace
+/// though it is possible for the message to match this as well.
+final _v8TraceLine = new RegExp(r"    ?at ");
+
+/// A RegExp to match Firefox and Safari's stack traces.
+///
+/// Firefox and Safari have very similar stack trace formats, so we use the same
+/// logic for parsing them.
+///
+/// Firefox's trace frames start with the name of the function in which the
+/// error occurred, possibly including its parameters inside `()`. For example,
+/// `.VW.call$0("arg")@http://pub.dartlang.org/stuff.dart.js:560`.
+///
+/// Safari traces occasionally don't include the initial method name followed by
+/// "@", and they always have both the line and column number (or just a
+/// trailing colon if no column number is available). They can also contain
+/// empty lines or lines consisting only of `[native code]`.
+final _firefoxSafariTrace = new RegExp(
+    r"^"
+    r"(" // Member description. Not present in some Safari frames.
+      r"([.0-9A-Za-z_$/<]|\(.*\))*" // Member name and arguments.
+      r"@"
+    r")?"
+    r"[^\s]*" // Frame URL.
+    r":\d*" // Line or column number. Some older frames only have a line number.
+    r"$", multiLine: true);
+
+/// A RegExp to match this package's stack traces.
+final _friendlyTrace = new RegExp(r"^[^\s]+( \d+(:\d+)?)?[ \t]+[^\s]+$",
+    multiLine: true);
+
+/// A stack trace, comprised of a list of stack frames.
+class Trace implements StackTrace {
+  /// The stack frames that comprise this stack trace.
+  final List<Frame> frames;
+
+  /// Returns a human-readable representation of [stackTrace]. If [terse] is
+  /// set, this folds together multiple stack frames from the Dart core
+  /// libraries, so that only the core library method directly called from user
+  /// code is visible (see [Trace.terse]).
+  static String format(StackTrace stackTrace, {bool terse: true}) {
+    var trace = new Trace.from(stackTrace);
+    if (terse) trace = trace.terse;
+    return trace.toString();
+  }
+
+  /// Returns the current stack trace.
+  ///
+  /// By default, the first frame of this trace will be the line where
+  /// [Trace.current] is called. If [level] is passed, the trace will start that
+  /// many frames up instead.
+  factory Trace.current([int level=0]) {
+    if (level < 0) {
+      throw new ArgumentError("Argument [level] must be greater than or equal "
+          "to 0.");
+    }
+
+    try {
+      throw '';
+    } catch (_, nativeTrace) {
+      var trace = new Trace.from(nativeTrace);
+      return new LazyTrace(() => new Trace(trace.frames.skip(level + 1)));
+    }
+  }
+
+  /// Returns a new stack trace containing the same data as [trace].
+  ///
+  /// If [trace] is a native [StackTrace], its data will be parsed out; if it's
+  /// a [Trace], it will be returned as-is.
+  factory Trace.from(StackTrace trace) {
+    // Normally explicitly validating null arguments is bad Dart style, but here
+    // the natural failure will only occur when the LazyTrace is materialized,
+    // and we want to provide an error that's more local to the actual problem.
+    if (trace == null) {
+      throw new ArgumentError("Cannot create a Trace from null.");
+    }
+
+    if (trace is Trace) return trace;
+    if (trace is Chain) return trace.toTrace();
+    return new LazyTrace(() => new Trace.parse(trace.toString()));
+  }
+
+  /// Parses a string representation of a stack trace.
+  ///
+  /// [trace] should be formatted in the same way as a Dart VM or browser stack
+  /// trace.
+  factory Trace.parse(String trace) {
+    try {
+      if (trace.isEmpty) return new Trace(<Frame>[]);
+      if (trace.contains(_v8Trace)) return new Trace.parseV8(trace);
+      if (trace.contains("\tat ")) return new Trace.parseJSCore(trace);
+      if (trace.contains(_firefoxSafariTrace)) {
+        return new Trace.parseFirefox(trace);
+      }
+      if (trace.contains(_friendlyTrace)) {
+        return new Trace.parseFriendly(trace);
+      }
+
+      // Default to parsing the stack trace as a VM trace. This is also hit on
+      // IE and Safari, where the stack trace is just an empty string (issue
+      // 11257).
+      return new Trace.parseVM(trace);
+    } on FormatException catch (error) {
+      throw new FormatException('${error.message}\nStack trace:\n$trace');
+    }
+  }
+
+  /// Parses a string representation of a Dart VM stack trace.
+  Trace.parseVM(String trace)
+      : this(_parseVM(trace));
+
+  static List<Frame> _parseVM(String trace) {
+    var lines = trace.trim().split("\n");
+    var frames = lines.take(lines.length - 1)
+        .map((line) => new Frame.parseVM(line))
+        .toList();
+
+    // TODO(nweiz): Remove this when issue 23614 is fixed.
+    if (!lines.last.endsWith(".da")) {
+      frames.add(new Frame.parseVM(lines.last));
+    }
+
+    return frames;
+  }
+
+  /// Parses a string representation of a Chrome/V8 stack trace.
+  Trace.parseV8(String trace)
+      : this(trace.split("\n").skip(1)
+          // It's possible that an Exception's description contains a line that
+          // looks like a V8 trace line, which will screw this up.
+          // Unfortunately, that's impossible to detect.
+          .skipWhile((line) => !line.startsWith(_v8TraceLine))
+          .map((line) => new Frame.parseV8(line)));
+
+  /// Parses a string representation of a JavaScriptCore stack trace.
+  Trace.parseJSCore(String trace)
+      : this(trace.split("\n")
+            .where((line) => line != "\tat ")
+            .map((line) => new Frame.parseV8(line)));
+
+  /// Parses a string representation of an Internet Explorer stack trace.
+  ///
+  /// IE10+ traces look just like V8 traces. Prior to IE10, stack traces can't
+  /// be retrieved.
+  Trace.parseIE(String trace)
+      : this.parseV8(trace);
+
+  /// Parses a string representation of a Firefox stack trace.
+  Trace.parseFirefox(String trace)
+      : this(trace.trim().split("\n")
+          .where((line) => line.isNotEmpty && line != '[native code]')
+          .map((line) => new Frame.parseFirefox(line)));
+
+  /// Parses a string representation of a Safari stack trace.
+  Trace.parseSafari(String trace)
+      : this.parseFirefox(trace);
+
+  /// Parses a string representation of a Safari 6.1+ stack trace.
+  @Deprecated("Use Trace.parseSafari instead.")
+  Trace.parseSafari6_1(String trace)
+      : this.parseSafari(trace);
+
+  /// Parses a string representation of a Safari 6.0 stack trace.
+  @Deprecated("Use Trace.parseSafari instead.")
+  Trace.parseSafari6_0(String trace)
+      : this(trace.trim().split("\n")
+          .where((line) => line != '[native code]')
+          .map((line) => new Frame.parseFirefox(line)));
+
+  /// Parses this package's string representation of a stack trace.
+  ///
+  /// This also parses string representations of [Chain]s. They parse to the
+  /// same trace that [Chain.toTrace] would return.
+  Trace.parseFriendly(String trace)
+      : this(trace.isEmpty
+            ? []
+            : trace.trim().split("\n")
+                // Filter out asynchronous gaps from [Chain]s.
+                .where((line) => !line.startsWith('====='))
+                .map((line) => new Frame.parseFriendly(line)));
+
+  /// Returns a new [Trace] comprised of [frames].
+  Trace(Iterable<Frame> frames)
+      : frames = new UnmodifiableListView<Frame>(frames.toList());
+
+  /// Returns a VM-style [StackTrace] object.
+  ///
+  /// The return value's [toString] method will always return a string
+  /// representation in the Dart VM's stack trace format, regardless of what
+  /// platform is being used.
+  StackTrace get vmTrace => new VMTrace(frames);
+
+  /// Returns a terser version of [this].
+  ///
+  /// This is accomplished by folding together multiple stack frames from the
+  /// core library or from this package, as in [foldFrames]. Remaining core
+  /// library frames have their libraries, "-patch" suffixes, and line numbers
+  /// removed. If the outermost frame of the stack trace is a core library
+  /// frame, it's removed entirely.
+  ///
+  /// For custom folding, see [foldFrames].
+  Trace get terse => foldFrames((_) => false, terse: true);
+
+  /// Returns a new [Trace] based on [this] where multiple stack frames matching
+  /// [predicate] are folded together.
+  ///
+  /// This means that whenever there are multiple frames in a row that match
+  /// [predicate], only the last one is kept. This is useful for limiting the
+  /// amount of library code that appears in a stack trace by only showing user
+  /// code and code that's called by user code.
+  ///
+  /// If [terse] is true, this will also fold together frames from the core
+  /// library or from this package, simplify core library frames, and
+  /// potentially remove the outermost frame as in [Trace.terse].
+  Trace foldFrames(bool predicate(Frame frame), {bool terse: false}) {
+    if (terse) {
+      var oldPredicate = predicate;
+      predicate = (frame) {
+        if (oldPredicate(frame)) return true;
+
+        if (frame.isCore) return true;
+        if (frame.package == 'stack_trace') return true;
+
+        // Ignore async stack frames without any line or column information.
+        // These come from the VM's async/await implementation and represent
+        // internal frames. They only ever show up in stack chains and are
+        // always surrounded by other traces that are actually useful, so we can
+        // just get rid of them.
+        // TODO(nweiz): Get rid of this logic some time after issue 22009 is
+        // fixed.
+        if (!frame.member.contains('<async>')) return false;
+        return frame.line == null;
+      };
+    }
+
+    var newFrames = [];
+    for (var frame in frames.reversed) {
+      if (frame is UnparsedFrame || !predicate(frame)) {
+        newFrames.add(frame);
+      } else if (newFrames.isEmpty || !predicate(newFrames.last)) {
+        newFrames.add(new Frame(
+            frame.uri, frame.line, frame.column, frame.member));
+      }
+    }
+
+    if (terse) {
+      newFrames = newFrames.map((frame) {
+        if (frame is UnparsedFrame || !predicate(frame)) return frame;
+        var library = frame.library.replaceAll(_terseRegExp, '');
+        return new Frame(Uri.parse(library), null, null, frame.member);
+      }).toList();
+      if (newFrames.length > 1 && newFrames.first.isCore) newFrames.removeAt(0);
+    }
+
+    return new Trace(newFrames.reversed);
+  }
+
+  /// Returns a human-readable string representation of [this].
+  String toString() {
+    // Figure out the longest path so we know how much to pad.
+    var longest = frames.map((frame) => frame.location.length)
+        .fold(0, math.max);
+
+    // Print out the stack trace nicely formatted.
+    return frames.map((frame) {
+      if (frame is UnparsedFrame) return "$frame\n";
+      return '${padRight(frame.location, longest)}  ${frame.member}\n';
+    }).join();
+  }
+}
diff --git a/mojo/public/dart/third_party/stack_trace/lib/src/unparsed_frame.dart b/mojo/public/dart/third_party/stack_trace/lib/src/unparsed_frame.dart
new file mode 100644
index 0000000..f037c8a
--- /dev/null
+++ b/mojo/public/dart/third_party/stack_trace/lib/src/unparsed_frame.dart
@@ -0,0 +1,26 @@
+// Copyright (c) 2015, 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 stack_trace.unparsed_frame;
+
+import 'frame.dart';
+
+/// A frame that failed to parse.
+///
+/// The [member] property contains the original frame's contents.
+class UnparsedFrame implements Frame {
+  final Uri uri = new Uri(path: "unparsed");
+  final int line = null;
+  final int column = null;
+  final bool isCore = false;
+  final String library = "unparsed";
+  final String package = null;
+  final String location = "unparsed";
+
+  final String member;
+
+  UnparsedFrame(this.member);
+
+  String toString() => member;
+}
\ No newline at end of file
diff --git a/mojo/public/dart/third_party/stack_trace/lib/src/utils.dart b/mojo/public/dart/third_party/stack_trace/lib/src/utils.dart
new file mode 100644
index 0000000..62a2820
--- /dev/null
+++ b/mojo/public/dart/third_party/stack_trace/lib/src/utils.dart
@@ -0,0 +1,36 @@
+// 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 stack_trace.src.utils;
+
+/// Returns [string] with enough spaces added to the end to make it [length]
+/// characters long.
+String padRight(String string, int length) {
+  if (string.length >= length) return string;
+
+  var result = new StringBuffer();
+  result.write(string);
+  for (var i = 0; i < length - string.length; i++) {
+    result.write(' ');
+  }
+
+  return result.toString();
+}
+
+/// Flattens nested lists inside an iterable into a single list containing only
+/// non-list elements.
+List flatten(Iterable nested) {
+  var result = [];
+  helper(list) {
+    for (var element in list) {
+      if (element is List) {
+        helper(element);
+      } else {
+        result.add(element);
+      }
+    }
+  }
+  helper(nested);
+  return result;
+}
diff --git a/mojo/public/dart/third_party/stack_trace/lib/src/vm_trace.dart b/mojo/public/dart/third_party/stack_trace/lib/src/vm_trace.dart
new file mode 100644
index 0000000..7991160
--- /dev/null
+++ b/mojo/public/dart/third_party/stack_trace/lib/src/vm_trace.dart
@@ -0,0 +1,34 @@
+// 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 vm_trace;
+
+import 'frame.dart';
+import 'utils.dart';
+
+/// An implementation of [StackTrace] that emulates the behavior of the VM's
+/// implementation.
+///
+/// In particular, when [toString] is called, this returns a string in the VM's
+/// stack trace format.
+class VMTrace implements StackTrace {
+  /// The stack frames that comprise this stack trace.
+  final List<Frame> frames;
+
+  VMTrace(this.frames);
+
+  String toString() {
+    var i = 1;
+    return frames.map((frame) {
+      var number = padRight("#${i++}", 8);
+      var member = frame.member
+          .replaceAllMapped(new RegExp(r"[^.]+\.<async>"),
+              (match) => "${match[1]}.<${match[1]}_async_body>")
+          .replaceAll("<fn>", "<anonymous closure>");
+      var line = frame.line == null ? 0 : frame.line;
+      var column = frame.column == null ? 0 : frame.column;
+      return "$number$member (${frame.uri}:$line:$column)\n";
+    }).join();
+  }
+}
diff --git a/mojo/public/dart/third_party/stack_trace/lib/stack_trace.dart b/mojo/public/dart/third_party/stack_trace/lib/stack_trace.dart
new file mode 100644
index 0000000..6bba635
--- /dev/null
+++ b/mojo/public/dart/third_party/stack_trace/lib/stack_trace.dart
@@ -0,0 +1,29 @@
+// 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.
+
+/**
+ * Stack trace generation and parsing.
+ *
+ * ## Installing ##
+ *
+ * Use [pub][] to install this package. Add the following to your `pubspec.yaml`
+ * file.
+ *
+ *     dependencies:
+ *       stack_trace: any
+ *
+ * Then run `pub install`.
+ *
+ * For more information, see the
+ * [stack_trace package on pub.dartlang.org][pkg].
+ *
+ * [pub]: http://pub.dartlang.org
+ * [pkg]: http://pub.dartlang.org/packages/stack_trace
+ */
+library stack_trace;
+
+export 'src/chain.dart';
+export 'src/frame.dart';
+export 'src/trace.dart';
+export 'src/unparsed_frame.dart';
diff --git a/mojo/public/dart/third_party/stack_trace/pubspec.yaml b/mojo/public/dart/third_party/stack_trace/pubspec.yaml
new file mode 100644
index 0000000..5c8b720
--- /dev/null
+++ b/mojo/public/dart/third_party/stack_trace/pubspec.yaml
@@ -0,0 +1,22 @@
+name: stack_trace
+
+# Note! This version number is referenced directly in the pub source code in
+# lib/src/barback.dart. Pub implicitly places a version constraint on
+# stack_trace to ensure users only select a version of stack_trace that works
+# with their current version of pub.
+#
+# When the major version is upgraded, you *must* update that version constraint
+# in pub to stay in sync with this.
+version: 1.4.2
+author: "Dart Team <misc@dartlang.org>"
+homepage: http://github.com/dart-lang/stack_trace
+description: >
+  A package for manipulating stack traces and printing them readably.
+
+dependencies:
+  path: "^1.2.0"
+
+dev_dependencies:
+  test: "^0.12.0"
+environment:
+  sdk: ">=1.8.0 <2.0.0"
diff --git a/mojo/public/dart/third_party/stack_trace/test/chain/chain_test.dart b/mojo/public/dart/third_party/stack_trace/test/chain/chain_test.dart
new file mode 100644
index 0000000..0eb3f00
--- /dev/null
+++ b/mojo/public/dart/third_party/stack_trace/test/chain/chain_test.dart
@@ -0,0 +1,282 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:path/path.dart' as p;
+import 'package:stack_trace/stack_trace.dart';
+import 'package:test/test.dart';
+
+import 'utils.dart';
+
+void main() {
+  group('Chain.parse()', () {
+    test('parses a real Chain', () {
+      return captureFuture(() => inMicrotask(() => throw 'error'))
+          .then((chain) {
+        expect(new Chain.parse(chain.toString()).toString(),
+            equals(chain.toString()));
+      });
+    });
+
+    test('parses an empty string', () {
+      var chain = new Chain.parse('');
+      expect(chain.traces, isEmpty);
+    });
+
+    test('parses a chain containing empty traces', () {
+      var chain = new Chain.parse(
+          '===== asynchronous gap ===========================\n'
+          '===== asynchronous gap ===========================\n');
+      expect(chain.traces, hasLength(3));
+      expect(chain.traces[0].frames, isEmpty);
+      expect(chain.traces[1].frames, isEmpty);
+      expect(chain.traces[2].frames, isEmpty);
+    });
+  });
+
+  test("toString() ensures that all traces are aligned", () {
+    var chain = new Chain([
+      new Trace.parse('short 10:11  Foo.bar\n'),
+      new Trace.parse('loooooooooooong 10:11  Zop.zoop')
+    ]);
+
+    expect(chain.toString(), equals(
+        'short 10:11            Foo.bar\n'
+        '===== asynchronous gap ===========================\n'
+        'loooooooooooong 10:11  Zop.zoop\n'));
+  });
+
+  var userSlashCode = p.join('user', 'code.dart');
+  group('Chain.terse', () {
+    test('makes each trace terse', () {
+      var chain = new Chain([
+        new Trace.parse(
+            'dart:core 10:11       Foo.bar\n'
+            'dart:core 10:11       Bar.baz\n'
+            'user/code.dart 10:11  Bang.qux\n'
+            'dart:core 10:11       Zip.zap\n'
+            'dart:core 10:11       Zop.zoop'),
+        new Trace.parse(
+            'user/code.dart 10:11                        Bang.qux\n'
+            'dart:core 10:11                             Foo.bar\n'
+            'package:stack_trace/stack_trace.dart 10:11  Bar.baz\n'
+            'dart:core 10:11                             Zip.zap\n'
+            'user/code.dart 10:11                        Zop.zoop')
+      ]);
+
+      expect(chain.terse.toString(), equals(
+          'dart:core             Bar.baz\n'
+          '$userSlashCode 10:11  Bang.qux\n'
+          '===== asynchronous gap ===========================\n'
+          '$userSlashCode 10:11  Bang.qux\n'
+          'dart:core             Zip.zap\n'
+          '$userSlashCode 10:11  Zop.zoop\n'));
+    });
+
+    test('eliminates internal-only traces', () {
+      var chain = new Chain([
+        new Trace.parse(
+            'user/code.dart 10:11  Foo.bar\n'
+            'dart:core 10:11       Bar.baz'),
+        new Trace.parse(
+            'dart:core 10:11                             Foo.bar\n'
+            'package:stack_trace/stack_trace.dart 10:11  Bar.baz\n'
+            'dart:core 10:11                             Zip.zap'),
+        new Trace.parse(
+            'user/code.dart 10:11  Foo.bar\n'
+            'dart:core 10:11       Bar.baz')
+      ]);
+
+      expect(chain.terse.toString(), equals(
+          '$userSlashCode 10:11  Foo.bar\n'
+          '===== asynchronous gap ===========================\n'
+          '$userSlashCode 10:11  Foo.bar\n'));
+    });
+
+    test("doesn't return an empty chain", () {
+      var chain = new Chain([
+        new Trace.parse(
+            'dart:core 10:11                             Foo.bar\n'
+            'package:stack_trace/stack_trace.dart 10:11  Bar.baz\n'
+            'dart:core 10:11                             Zip.zap'),
+        new Trace.parse(
+            'dart:core 10:11                             A.b\n'
+            'package:stack_trace/stack_trace.dart 10:11  C.d\n'
+            'dart:core 10:11                             E.f')
+      ]);
+
+      expect(chain.terse.toString(), equals('dart:core  E.f\n'));
+    });
+  });
+
+  group('Chain.foldFrames', () {
+    test('folds each trace', () {
+      var chain = new Chain([
+        new Trace.parse(
+            'a.dart 10:11  Foo.bar\n'
+            'a.dart 10:11  Bar.baz\n'
+            'b.dart 10:11  Bang.qux\n'
+            'a.dart 10:11  Zip.zap\n'
+            'a.dart 10:11  Zop.zoop'),
+        new Trace.parse(
+            'a.dart 10:11  Foo.bar\n'
+            'a.dart 10:11  Bar.baz\n'
+            'a.dart 10:11  Bang.qux\n'
+            'a.dart 10:11  Zip.zap\n'
+            'b.dart 10:11  Zop.zoop')
+      ]);
+
+      var folded = chain.foldFrames((frame) => frame.library == 'a.dart');
+      expect(folded.toString(), equals(
+          'a.dart 10:11  Bar.baz\n'
+          'b.dart 10:11  Bang.qux\n'
+          'a.dart 10:11  Zop.zoop\n'
+          '===== asynchronous gap ===========================\n'
+          'a.dart 10:11  Zip.zap\n'
+          'b.dart 10:11  Zop.zoop\n'));
+    });
+
+    test('with terse: true, folds core frames as well', () {
+      var chain = new Chain([
+        new Trace.parse(
+            'a.dart 10:11                        Foo.bar\n'
+            'dart:async-patch/future.dart 10:11  Zip.zap\n'
+            'b.dart 10:11                        Bang.qux\n'
+            'dart:core 10:11                     Bar.baz\n'
+            'a.dart 10:11                        Zop.zoop'),
+        new Trace.parse(
+            'a.dart 10:11  Foo.bar\n'
+            'a.dart 10:11  Bar.baz\n'
+            'a.dart 10:11  Bang.qux\n'
+            'a.dart 10:11  Zip.zap\n'
+            'b.dart 10:11  Zop.zoop')
+      ]);
+
+      var folded = chain.foldFrames((frame) => frame.library == 'a.dart',
+          terse: true);
+      expect(folded.toString(), equals(
+          'dart:async    Zip.zap\n'
+          'b.dart 10:11  Bang.qux\n'
+          'a.dart        Zop.zoop\n'
+          '===== asynchronous gap ===========================\n'
+          'a.dart        Zip.zap\n'
+          'b.dart 10:11  Zop.zoop\n'));
+    });
+
+    test('eliminates completely-folded traces', () {
+      var chain = new Chain([
+        new Trace.parse(
+            'a.dart 10:11  Foo.bar\n'
+            'b.dart 10:11  Bang.qux'),
+        new Trace.parse(
+            'a.dart 10:11  Foo.bar\n'
+            'a.dart 10:11  Bang.qux'),
+        new Trace.parse(
+            'a.dart 10:11  Zip.zap\n'
+            'b.dart 10:11  Zop.zoop')
+      ]);
+
+      var folded = chain.foldFrames((frame) => frame.library == 'a.dart');
+      expect(folded.toString(), equals(
+          'a.dart 10:11  Foo.bar\n'
+          'b.dart 10:11  Bang.qux\n'
+          '===== asynchronous gap ===========================\n'
+          'a.dart 10:11  Zip.zap\n'
+          'b.dart 10:11  Zop.zoop\n'));
+    });
+
+    test("doesn't return an empty trace", () {
+      var chain = new Chain([
+        new Trace.parse(
+            'a.dart 10:11  Foo.bar\n'
+            'a.dart 10:11  Bang.qux')
+      ]);
+
+      var folded = chain.foldFrames((frame) => frame.library == 'a.dart');
+      expect(folded.toString(), equals('a.dart 10:11  Bang.qux\n'));
+    });
+  });
+
+  test('Chain.toTrace eliminates asynchronous gaps', () {
+    var trace = new Chain([
+      new Trace.parse(
+          'user/code.dart 10:11  Foo.bar\n'
+          'dart:core 10:11       Bar.baz'),
+      new Trace.parse(
+          'user/code.dart 10:11  Foo.bar\n'
+          'dart:core 10:11       Bar.baz')
+    ]).toTrace();
+
+    expect(trace.toString(), equals(
+        '$userSlashCode 10:11  Foo.bar\n'
+        'dart:core 10:11       Bar.baz\n'
+        '$userSlashCode 10:11  Foo.bar\n'
+        'dart:core 10:11       Bar.baz\n'));
+  });
+
+  group('Chain.track(Future)', () {
+    test('forwards the future value within Chain.capture()', () {
+      Chain.capture(() {
+        expect(Chain.track(new Future.value('value')),
+            completion(equals('value')));
+
+        var trace = new Trace.current();
+        expect(Chain.track(new Future.error('error', trace))
+            .catchError((e, stackTrace) {
+          expect(e, equals('error'));
+          expect(stackTrace.toString(), equals(trace.toString()));
+        }), completes);
+      });
+    });
+
+    test('forwards the future value outside of Chain.capture()', () {
+      expect(Chain.track(new Future.value('value')),
+          completion(equals('value')));
+
+      var trace = new Trace.current();
+      expect(Chain.track(new Future.error('error', trace))
+          .catchError((e, stackTrace) {
+        expect(e, equals('error'));
+        expect(stackTrace.toString(), equals(trace.toString()));
+      }), completes);
+    });
+  });
+
+  group('Chain.track(Stream)', () {
+    test('forwards stream values within Chain.capture()', () {
+      Chain.capture(() {
+        var controller = new StreamController()
+            ..add(1)..add(2)..add(3)..close();
+        expect(Chain.track(controller.stream).toList(),
+            completion(equals([1, 2, 3])));
+
+        var trace = new Trace.current();
+        controller = new StreamController()..addError('error', trace);
+        expect(Chain.track(controller.stream).toList()
+            .catchError((e, stackTrace) {
+          expect(e, equals('error'));
+          expect(stackTrace.toString(), equals(trace.toString()));
+        }), completes);
+      });
+    });
+
+    test('forwards stream values outside of Chain.capture()', () {
+      Chain.capture(() {
+        var controller = new StreamController()
+            ..add(1)..add(2)..add(3)..close();
+        expect(Chain.track(controller.stream).toList(),
+            completion(equals([1, 2, 3])));
+
+        var trace = new Trace.current();
+        controller = new StreamController()..addError('error', trace);
+        expect(Chain.track(controller.stream).toList()
+            .catchError((e, stackTrace) {
+          expect(e, equals('error'));
+          expect(stackTrace.toString(), equals(trace.toString()));
+        }), completes);
+      });
+    });
+  });
+}
\ No newline at end of file
diff --git a/mojo/public/dart/third_party/stack_trace/test/chain/dart2js_test.dart b/mojo/public/dart/third_party/stack_trace/test/chain/dart2js_test.dart
new file mode 100644
index 0000000..afb27fa
--- /dev/null
+++ b/mojo/public/dart/third_party/stack_trace/test/chain/dart2js_test.dart
@@ -0,0 +1,338 @@
+// Copyright (c) 2015, 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.
+
+// dart2js chain tests are separated out because dart2js stack traces are
+// inconsistent due to inlining and browser differences. These tests don't
+// assert anything about the content of the traces, just the number of traces in
+// a chain.
+@TestOn('js')
+
+import 'dart:async';
+
+import 'package:stack_trace/stack_trace.dart';
+import 'package:test/test.dart';
+
+import 'utils.dart';
+
+void main() {
+  group('capture() with onError catches exceptions', () {
+    test('thrown synchronously', () async {
+      var chain = await captureFuture(() => throw 'error');
+      expect(chain.traces, hasLength(1));
+    });
+
+    test('thrown in a microtask', () async {
+      var chain = await captureFuture(() => inMicrotask(() => throw 'error'));
+      expect(chain.traces, hasLength(2));
+    });
+
+    test('thrown in a one-shot timer', () async {
+      var chain = await captureFuture(
+          () => inOneShotTimer(() => throw 'error'));
+      expect(chain.traces, hasLength(2));
+    });
+
+    test('thrown in a periodic timer', () async {
+      var chain = await captureFuture(
+          () => inPeriodicTimer(() => throw 'error'));
+      expect(chain.traces, hasLength(2));
+    });
+
+    test('thrown in a nested series of asynchronous operations', () async {
+      var chain = await captureFuture(() {
+        inPeriodicTimer(() {
+          inOneShotTimer(() => inMicrotask(() => throw 'error'));
+        });
+      });
+
+      expect(chain.traces, hasLength(4));
+    });
+
+    test('thrown in a long future chain', () async {
+      var chain = await captureFuture(() => inFutureChain(() => throw 'error'));
+
+      // Despite many asynchronous operations, there's only one level of
+      // nested calls, so there should be only two traces in the chain. This
+      // is important; programmers expect stack trace memory consumption to be
+      // O(depth of program), not O(length of program).
+      expect(chain.traces, hasLength(2));
+    });
+
+    test('thrown in new Future()', () async {
+      var chain = await captureFuture(() => inNewFuture(() => throw 'error'));
+      expect(chain.traces, hasLength(3));
+    });
+
+    test('thrown in new Future.sync()', () async {
+      var chain = await captureFuture(() {
+        inMicrotask(() => inSyncFuture(() => throw 'error'));
+      });
+
+      expect(chain.traces, hasLength(3));
+    });
+
+    test('multiple times', () {
+      var completer = new Completer();
+      var first = true;
+
+      Chain.capture(() {
+        inMicrotask(() => throw 'first error');
+        inPeriodicTimer(() => throw 'second error');
+      }, onError: (error, chain) {
+        try {
+          if (first) {
+            expect(error, equals('first error'));
+            expect(chain.traces, hasLength(2));
+            first = false;
+          } else {
+            expect(error, equals('second error'));
+            expect(chain.traces, hasLength(2));
+            completer.complete();
+          }
+        } catch (error, stackTrace) {
+          completer.completeError(error, stackTrace);
+        }
+      });
+
+      return completer.future;
+    });
+
+    test('passed to a completer', () async {
+      var trace = new Trace.current();
+      var chain = await captureFuture(() {
+        inMicrotask(() => completerErrorFuture(trace));
+      });
+
+      expect(chain.traces, hasLength(3));
+
+      // The first trace is the trace that was manually reported for the
+      // error.
+      expect(chain.traces.first.toString(), equals(trace.toString()));
+    });
+
+    test('passed to a completer with no stack trace', () async {
+      var chain = await captureFuture(() {
+        inMicrotask(() => completerErrorFuture());
+      });
+
+      expect(chain.traces, hasLength(2));
+    });
+
+    test('passed to a stream controller', () async {
+      var trace = new Trace.current();
+      var chain = await captureFuture(() {
+        inMicrotask(() => controllerErrorStream(trace).listen(null));
+      });
+
+      expect(chain.traces, hasLength(3));
+      expect(chain.traces.first.toString(), equals(trace.toString()));
+    });
+
+    test('passed to a stream controller with no stack trace', () async {
+      var chain = await captureFuture(() {
+        inMicrotask(() => controllerErrorStream().listen(null));
+      });
+
+      expect(chain.traces, hasLength(2));
+    });
+
+    test('and relays them to the parent zone', () {
+      var completer = new Completer();
+
+      runZoned(() {
+        Chain.capture(() {
+          inMicrotask(() => throw 'error');
+        }, onError: (error, chain) {
+          expect(error, equals('error'));
+          expect(chain.traces, hasLength(2));
+          throw error;
+        });
+      }, onError: (error, chain) {
+        try {
+          expect(error, equals('error'));
+          expect(chain, new isInstanceOf<Chain>());
+          expect(chain.traces, hasLength(2));
+          completer.complete();
+        } catch (error, stackTrace) {
+          completer.completeError(error, stackTrace);
+        }
+      });
+
+      return completer.future;
+    });
+  });
+
+  test('capture() without onError passes exceptions to parent zone', () {
+    var completer = new Completer();
+
+    runZoned(() {
+      Chain.capture(() => inMicrotask(() => throw 'error'));
+    }, onError: (error, chain) {
+      try {
+        expect(error, equals('error'));
+        expect(chain, new isInstanceOf<Chain>());
+        expect(chain.traces, hasLength(2));
+        completer.complete();
+      } catch (error, stackTrace) {
+        completer.completeError(error, stackTrace);
+      }
+    });
+
+    return completer.future;
+  });
+
+  group('current() within capture()', () {
+    test('called in a microtask', () async {
+      var completer = new Completer();
+      Chain.capture(() {
+        inMicrotask(() => completer.complete(new Chain.current()));
+      });
+
+      var chain = await completer.future;
+      expect(chain.traces, hasLength(2));
+    });
+
+    test('called in a one-shot timer', () async {
+      var completer = new Completer();
+      Chain.capture(() {
+        inOneShotTimer(() => completer.complete(new Chain.current()));
+      });
+
+      var chain = await completer.future;
+      expect(chain.traces, hasLength(2));
+    });
+
+    test('called in a periodic timer', () async {
+      var completer = new Completer();
+      Chain.capture(() {
+        inPeriodicTimer(() => completer.complete(new Chain.current()));
+      });
+
+      var chain = await completer.future;
+      expect(chain.traces, hasLength(2));
+    });
+
+    test('called in a nested series of asynchronous operations', () async {
+      var completer = new Completer();
+      Chain.capture(() {
+        inPeriodicTimer(() {
+          inOneShotTimer(() {
+            inMicrotask(() => completer.complete(new Chain.current()));
+          });
+        });
+      });
+
+      var chain = await completer.future;
+      expect(chain.traces, hasLength(4));
+    });
+
+    test('called in a long future chain', () async {
+      var completer = new Completer();
+      Chain.capture(() {
+        inFutureChain(() => completer.complete(new Chain.current()));
+      });
+
+      var chain = await completer.future;
+      expect(chain.traces, hasLength(2));
+    });
+  });
+
+  test('current() outside of capture() returns a chain wrapping the current '
+      'trace', () {
+    // The test runner runs all tests with chains enabled, so to test without we
+    // have to do some zone munging.
+    return runZoned(() async {
+      var completer = new Completer();
+      inMicrotask(() => completer.complete(new Chain.current()));
+
+      var chain = await completer.future;
+      // Since the chain wasn't loaded within [Chain.capture], the full stack
+      // chain isn't available and it just returns the current stack when
+      // called.
+      expect(chain.traces, hasLength(1));
+    }, zoneValues: {#stack_trace.stack_zone.spec: null});
+  });
+
+  group('forTrace() within capture()', () {
+    test('called for a stack trace from a microtask', () async {
+      var chain = await Chain.capture(() {
+        return chainForTrace(inMicrotask, () => throw 'error');
+      });
+
+      // Because [chainForTrace] has to set up a future chain to capture the
+      // stack trace while still showing it to the zone specification, it adds
+      // an additional level of async nesting and so an additional trace.
+      expect(chain.traces, hasLength(3));
+    });
+
+    test('called for a stack trace from a one-shot timer', () async {
+      var chain = await Chain.capture(() {
+        return chainForTrace(inOneShotTimer, () => throw 'error');
+      });
+
+      expect(chain.traces, hasLength(3));
+    });
+
+    test('called for a stack trace from a periodic timer', () async {
+      var chain = await Chain.capture(() {
+        return chainForTrace(inPeriodicTimer, () => throw 'error');
+      });
+
+      expect(chain.traces, hasLength(3));
+    });
+
+    test('called for a stack trace from a nested series of asynchronous '
+        'operations', () async {
+      var chain = await Chain.capture(() {
+        return chainForTrace((callback) {
+          inPeriodicTimer(() => inOneShotTimer(() => inMicrotask(callback)));
+        }, () => throw 'error');
+      });
+
+      expect(chain.traces, hasLength(5));
+    });
+
+    test('called for a stack trace from a long future chain', () async {
+      var chain = await Chain.capture(() {
+        return chainForTrace(inFutureChain, () => throw 'error');
+      });
+
+      expect(chain.traces, hasLength(3));
+    });
+
+    test('called for an unregistered stack trace returns a chain wrapping that '
+        'trace', () {
+      var trace;
+      var chain = Chain.capture(() {
+        try {
+          throw 'error';
+        } catch (_, stackTrace) {
+          trace = stackTrace;
+          return new Chain.forTrace(stackTrace);
+        }
+      });
+
+      expect(chain.traces, hasLength(1));
+      expect(chain.traces.first.toString(),
+          equals(new Trace.from(trace).toString()));
+    });
+  });
+
+  test('forTrace() outside of capture() returns a chain wrapping the given '
+      'trace', () {
+    var trace;
+    var chain = Chain.capture(() {
+      try {
+        throw 'error';
+      } catch (_, stackTrace) {
+        trace = stackTrace;
+        return new Chain.forTrace(stackTrace);
+      }
+    });
+
+    expect(chain.traces, hasLength(1));
+    expect(chain.traces.first.toString(),
+        equals(new Trace.from(trace).toString()));
+  });
+}
diff --git a/mojo/public/dart/third_party/stack_trace/test/chain/utils.dart b/mojo/public/dart/third_party/stack_trace/test/chain/utils.dart
new file mode 100644
index 0000000..1e19eeb
--- /dev/null
+++ b/mojo/public/dart/third_party/stack_trace/test/chain/utils.dart
@@ -0,0 +1,93 @@
+// Copyright (c) 2015, 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 stack_trace.test.chain.utils;
+
+import 'dart:async';
+
+import 'package:stack_trace/stack_trace.dart';
+import 'package:test/test.dart';
+
+/// Runs [callback] in a microtask callback.
+void inMicrotask(callback()) => scheduleMicrotask(callback);
+
+/// Runs [callback] in a one-shot timer callback.
+void inOneShotTimer(callback()) => Timer.run(callback);
+
+/// Runs [callback] once in a periodic timer callback.
+void inPeriodicTimer(callback()) {
+  var count = 0;
+  new Timer.periodic(new Duration(milliseconds: 1), (timer) {
+    count++;
+    if (count != 5) return;
+    timer.cancel();
+    callback();
+  });
+}
+
+/// Runs [callback] within a long asynchronous Future chain.
+void inFutureChain(callback()) {
+  new Future(() {})
+      .then((_) => new Future(() {}))
+      .then((_) => new Future(() {}))
+      .then((_) => new Future(() {}))
+      .then((_) => new Future(() {}))
+      .then((_) => callback())
+      .then((_) => new Future(() {}));
+}
+
+void inNewFuture(callback()) {
+  new Future(callback);
+}
+
+void inSyncFuture(callback()) {
+  new Future.sync(callback);
+}
+
+/// Returns a Future that completes to an error using a completer.
+///
+/// If [trace] is passed, it's used as the stack trace for the error.
+Future completerErrorFuture([StackTrace trace]) {
+  var completer = new Completer();
+  completer.completeError('error', trace);
+  return completer.future;
+}
+
+/// Returns a Stream that emits an error using a controller.
+///
+/// If [trace] is passed, it's used as the stack trace for the error.
+Stream controllerErrorStream([StackTrace trace]) {
+  var controller = new StreamController();
+  controller.addError('error', trace);
+  return controller.stream;
+}
+
+/// Runs [callback] within [asyncFn], then converts any errors raised into a
+/// [Chain] with [Chain.forTrace].
+Future<Chain> chainForTrace(asyncFn(callback()), callback()) {
+  var completer = new Completer();
+  asyncFn(() {
+    // We use `new Future.value().then(...)` here as opposed to [new Future] or
+    // [new Future.sync] because those methods don't pass the exception through
+    // the zone specification before propagating it, so there's no chance to
+    // attach a chain to its stack trace. See issue 15105.
+    new Future.value().then((_) => callback())
+        .catchError(completer.completeError);
+  });
+  return completer.future
+      .catchError((_, stackTrace) => new Chain.forTrace(stackTrace));
+}
+
+/// Runs [callback] in a [Chain.capture] zone and returns a Future that
+/// completes to the stack chain for an error thrown by [callback].
+///
+/// [callback] is expected to throw the string `"error"`.
+Future<Chain> captureFuture(callback()) {
+  var completer = new Completer<Chain>();
+  Chain.capture(callback, onError: (error, chain) {
+    expect(error, equals('error'));
+    completer.complete(chain);
+  });
+  return completer.future;
+}
diff --git a/mojo/public/dart/third_party/stack_trace/test/chain/vm_test.dart b/mojo/public/dart/third_party/stack_trace/test/chain/vm_test.dart
new file mode 100644
index 0000000..70635b7
--- /dev/null
+++ b/mojo/public/dart/third_party/stack_trace/test/chain/vm_test.dart
@@ -0,0 +1,481 @@
+// Copyright (c) 2015, 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.
+
+// VM chain tests can rely on stronger guarantees about the contents of the
+// stack traces than dart2js.
+@TestOn('dart-vm')
+
+import 'dart:async';
+
+import 'package:stack_trace/stack_trace.dart';
+import 'package:test/test.dart';
+
+import '../utils.dart';
+import 'utils.dart';
+
+void main() {
+  group('capture() with onError catches exceptions', () {
+    test('thrown synchronously', () {
+      return captureFuture(() => throw 'error')
+          .then((chain) {
+        expect(chain.traces, hasLength(1));
+        expect(chain.traces.single.frames.first,
+            frameMember(startsWith('main')));
+      });
+    });
+
+    test('thrown in a microtask', () {
+      return captureFuture(() => inMicrotask(() => throw 'error'))
+          .then((chain) {
+        // Since there was only one asynchronous operation, there should be only
+        // two traces in the chain.
+        expect(chain.traces, hasLength(2));
+
+        // The first frame of the first trace should be the line on which the
+        // actual error was thrown.
+        expect(chain.traces[0].frames.first, frameMember(startsWith('main')));
+
+        // The second trace should describe the stack when the error callback
+        // was scheduled.
+        expect(chain.traces[1].frames,
+            contains(frameMember(startsWith('inMicrotask'))));
+      });
+    });
+
+    test('thrown in a one-shot timer', () {
+      return captureFuture(() => inOneShotTimer(() => throw 'error'))
+          .then((chain) {
+        expect(chain.traces, hasLength(2));
+        expect(chain.traces[0].frames.first, frameMember(startsWith('main')));
+        expect(chain.traces[1].frames,
+            contains(frameMember(startsWith('inOneShotTimer'))));
+      });
+    });
+
+    test('thrown in a periodic timer', () {
+      return captureFuture(() => inPeriodicTimer(() => throw 'error'))
+          .then((chain) {
+        expect(chain.traces, hasLength(2));
+        expect(chain.traces[0].frames.first, frameMember(startsWith('main')));
+        expect(chain.traces[1].frames,
+            contains(frameMember(startsWith('inPeriodicTimer'))));
+      });
+    });
+
+    test('thrown in a nested series of asynchronous operations', () {
+      return captureFuture(() {
+        inPeriodicTimer(() {
+          inOneShotTimer(() => inMicrotask(() => throw 'error'));
+        });
+      }).then((chain) {
+        expect(chain.traces, hasLength(4));
+        expect(chain.traces[0].frames.first, frameMember(startsWith('main')));
+        expect(chain.traces[1].frames,
+            contains(frameMember(startsWith('inMicrotask'))));
+        expect(chain.traces[2].frames,
+            contains(frameMember(startsWith('inOneShotTimer'))));
+        expect(chain.traces[3].frames,
+            contains(frameMember(startsWith('inPeriodicTimer'))));
+      });
+    });
+
+    test('thrown in a long future chain', () {
+      return captureFuture(() => inFutureChain(() => throw 'error'))
+          .then((chain) {
+        // Despite many asynchronous operations, there's only one level of
+        // nested calls, so there should be only two traces in the chain. This
+        // is important; programmers expect stack trace memory consumption to be
+        // O(depth of program), not O(length of program).
+        expect(chain.traces, hasLength(2));
+
+        expect(chain.traces[0].frames.first, frameMember(startsWith('main')));
+        expect(chain.traces[1].frames,
+            contains(frameMember(startsWith('inFutureChain'))));
+      });
+    });
+
+    test('thrown in new Future()', () {
+      return captureFuture(() => inNewFuture(() => throw 'error'))
+          .then((chain) {
+        expect(chain.traces, hasLength(3));
+        expect(chain.traces[0].frames.first, frameMember(startsWith('main')));
+
+        // The second trace is the one captured by
+        // [StackZoneSpecification.errorCallback]. Because that runs
+        // asynchronously within [new Future], it doesn't actually refer to the
+        // source file at all.
+        expect(chain.traces[1].frames,
+            everyElement(frameLibrary(isNot(contains('chain_test')))));
+
+        expect(chain.traces[2].frames,
+            contains(frameMember(startsWith('inNewFuture'))));
+      });
+    });
+
+    test('thrown in new Future.sync()', () {
+      return captureFuture(() {
+        inMicrotask(() => inSyncFuture(() => throw 'error'));
+      }).then((chain) {
+        expect(chain.traces, hasLength(3));
+        expect(chain.traces[0].frames.first, frameMember(startsWith('main')));
+        expect(chain.traces[1].frames,
+            contains(frameMember(startsWith('inSyncFuture'))));
+        expect(chain.traces[2].frames,
+            contains(frameMember(startsWith('inMicrotask'))));
+      });
+    });
+
+    test('multiple times', () {
+      var completer = new Completer();
+      var first = true;
+
+      Chain.capture(() {
+        inMicrotask(() => throw 'first error');
+        inPeriodicTimer(() => throw 'second error');
+      }, onError: (error, chain) {
+        try {
+          if (first) {
+            expect(error, equals('first error'));
+            expect(chain.traces[1].frames,
+                contains(frameMember(startsWith('inMicrotask'))));
+            first = false;
+          } else {
+            expect(error, equals('second error'));
+            expect(chain.traces[1].frames,
+                contains(frameMember(startsWith('inPeriodicTimer'))));
+            completer.complete();
+          }
+        } catch (error, stackTrace) {
+          completer.completeError(error, stackTrace);
+        }
+      });
+
+      return completer.future;
+    });
+
+    test('passed to a completer', () {
+      var trace = new Trace.current();
+      return captureFuture(() {
+        inMicrotask(() => completerErrorFuture(trace));
+      }).then((chain) {
+        expect(chain.traces, hasLength(3));
+
+        // The first trace is the trace that was manually reported for the
+        // error.
+        expect(chain.traces.first.toString(), equals(trace.toString()));
+
+        // The second trace is the trace that was captured when
+        // [Completer.addError] was called.
+        expect(chain.traces[1].frames,
+            contains(frameMember(startsWith('completerErrorFuture'))));
+
+        // The third trace is the automatically-captured trace from when the
+        // microtask was scheduled.
+        expect(chain.traces[2].frames,
+            contains(frameMember(startsWith('inMicrotask'))));
+      });
+    });
+
+    test('passed to a completer with no stack trace', () {
+      return captureFuture(() {
+        inMicrotask(() => completerErrorFuture());
+      }).then((chain) {
+        expect(chain.traces, hasLength(2));
+
+        // The first trace is the one captured when [Completer.addError] was
+        // called.
+        expect(chain.traces[0].frames,
+            contains(frameMember(startsWith('completerErrorFuture'))));
+
+        // The second trace is the automatically-captured trace from when the
+        // microtask was scheduled.
+        expect(chain.traces[1].frames,
+            contains(frameMember(startsWith('inMicrotask'))));
+      });
+    });
+
+    test('passed to a stream controller', () {
+      var trace = new Trace.current();
+      return captureFuture(() {
+        inMicrotask(() => controllerErrorStream(trace).listen(null));
+      }).then((chain) {
+        expect(chain.traces, hasLength(3));
+        expect(chain.traces.first.toString(), equals(trace.toString()));
+        expect(chain.traces[1].frames,
+            contains(frameMember(startsWith('controllerErrorStream'))));
+        expect(chain.traces[2].frames,
+            contains(frameMember(startsWith('inMicrotask'))));
+      });
+    });
+
+    test('passed to a stream controller with no stack trace', () {
+      return captureFuture(() {
+        inMicrotask(() => controllerErrorStream().listen(null));
+      }).then((chain) {
+        expect(chain.traces, hasLength(2));
+        expect(chain.traces[0].frames,
+            contains(frameMember(startsWith('controllerErrorStream'))));
+        expect(chain.traces[1].frames,
+            contains(frameMember(startsWith('inMicrotask'))));
+      });
+    });
+
+    test('and relays them to the parent zone', () {
+      var completer = new Completer();
+
+      runZoned(() {
+        Chain.capture(() {
+          inMicrotask(() => throw 'error');
+        }, onError: (error, chain) {
+          expect(error, equals('error'));
+          expect(chain.traces[1].frames,
+              contains(frameMember(startsWith('inMicrotask'))));
+          throw error;
+        });
+      }, onError: (error, chain) {
+        try {
+          expect(error, equals('error'));
+          expect(chain, new isInstanceOf<Chain>());
+          expect(chain.traces[1].frames,
+              contains(frameMember(startsWith('inMicrotask'))));
+          completer.complete();
+        } catch (error, stackTrace) {
+          completer.completeError(error, stackTrace);
+        }
+      });
+
+      return completer.future;
+    });
+  });
+
+  test('capture() without onError passes exceptions to parent zone', () {
+    var completer = new Completer();
+
+    runZoned(() {
+      Chain.capture(() => inMicrotask(() => throw 'error'));
+    }, onError: (error, chain) {
+      try {
+        expect(error, equals('error'));
+        expect(chain, new isInstanceOf<Chain>());
+        expect(chain.traces[1].frames,
+            contains(frameMember(startsWith('inMicrotask'))));
+        completer.complete();
+      } catch (error, stackTrace) {
+        completer.completeError(error, stackTrace);
+      }
+    });
+
+    return completer.future;
+  });
+
+  group('current() within capture()', () {
+    test('called in a microtask', () {
+      var completer = new Completer();
+      Chain.capture(() {
+        inMicrotask(() => completer.complete(new Chain.current()));
+      });
+
+      return completer.future.then((chain) {
+        expect(chain.traces, hasLength(2));
+        expect(chain.traces[0].frames.first, frameMember(startsWith('main')));
+        expect(chain.traces[1].frames,
+            contains(frameMember(startsWith('inMicrotask'))));
+      });
+    });
+
+    test('called in a one-shot timer', () {
+      var completer = new Completer();
+      Chain.capture(() {
+        inOneShotTimer(() => completer.complete(new Chain.current()));
+      });
+
+      return completer.future.then((chain) {
+        expect(chain.traces, hasLength(2));
+        expect(chain.traces[0].frames.first, frameMember(startsWith('main')));
+        expect(chain.traces[1].frames,
+            contains(frameMember(startsWith('inOneShotTimer'))));
+      });
+    });
+
+    test('called in a periodic timer', () {
+      var completer = new Completer();
+      Chain.capture(() {
+        inPeriodicTimer(() => completer.complete(new Chain.current()));
+      });
+
+      return completer.future.then((chain) {
+        expect(chain.traces, hasLength(2));
+        expect(chain.traces[0].frames.first, frameMember(startsWith('main')));
+        expect(chain.traces[1].frames,
+            contains(frameMember(startsWith('inPeriodicTimer'))));
+      });
+    });
+
+    test('called in a nested series of asynchronous operations', () {
+      var completer = new Completer();
+      Chain.capture(() {
+        inPeriodicTimer(() {
+          inOneShotTimer(() {
+            inMicrotask(() => completer.complete(new Chain.current()));
+          });
+        });
+      });
+
+      return completer.future.then((chain) {
+        expect(chain.traces, hasLength(4));
+        expect(chain.traces[0].frames.first, frameMember(startsWith('main')));
+        expect(chain.traces[1].frames,
+            contains(frameMember(startsWith('inMicrotask'))));
+        expect(chain.traces[2].frames,
+            contains(frameMember(startsWith('inOneShotTimer'))));
+        expect(chain.traces[3].frames,
+            contains(frameMember(startsWith('inPeriodicTimer'))));
+      });
+    });
+
+    test('called in a long future chain', () {
+      var completer = new Completer();
+      Chain.capture(() {
+        inFutureChain(() => completer.complete(new Chain.current()));
+      });
+
+      return completer.future.then((chain) {
+        expect(chain.traces, hasLength(2));
+        expect(chain.traces[0].frames.first, frameMember(startsWith('main')));
+        expect(chain.traces[1].frames,
+            contains(frameMember(startsWith('inFutureChain'))));
+      });
+    });
+  });
+
+  test('current() outside of capture() returns a chain wrapping the current '
+      'trace', () {
+    // The test runner runs all tests with chains enabled, so to test without we
+    // have to do some zone munging.
+    return runZoned(() {
+      var completer = new Completer();
+      inMicrotask(() => completer.complete(new Chain.current()));
+
+      return completer.future.then((chain) {
+        // Since the chain wasn't loaded within [Chain.capture], the full stack
+        // chain isn't available and it just returns the current stack when
+        // called.
+        expect(chain.traces, hasLength(1));
+        expect(chain.traces.first.frames.first,
+            frameMember(startsWith('main')));
+      });
+    }, zoneValues: {#stack_trace.stack_zone.spec: null});
+  });
+
+  group('forTrace() within capture()', () {
+    test('called for a stack trace from a microtask', () {
+      return Chain.capture(() {
+        return chainForTrace(inMicrotask, () => throw 'error');
+      }).then((chain) {
+        // Because [chainForTrace] has to set up a future chain to capture the
+        // stack trace while still showing it to the zone specification, it adds
+        // an additional level of async nesting and so an additional trace.
+        expect(chain.traces, hasLength(3));
+        expect(chain.traces[0].frames.first, frameMember(startsWith('main')));
+        expect(chain.traces[1].frames,
+            contains(frameMember(startsWith('chainForTrace'))));
+        expect(chain.traces[2].frames,
+            contains(frameMember(startsWith('inMicrotask'))));
+      });
+    });
+
+    test('called for a stack trace from a one-shot timer', () {
+      return Chain.capture(() {
+        return chainForTrace(inOneShotTimer, () => throw 'error');
+      }).then((chain) {
+        expect(chain.traces, hasLength(3));
+        expect(chain.traces[0].frames.first, frameMember(startsWith('main')));
+        expect(chain.traces[1].frames,
+            contains(frameMember(startsWith('chainForTrace'))));
+        expect(chain.traces[2].frames,
+            contains(frameMember(startsWith('inOneShotTimer'))));
+      });
+    });
+
+    test('called for a stack trace from a periodic timer', () {
+      return Chain.capture(() {
+        return chainForTrace(inPeriodicTimer, () => throw 'error');
+      }).then((chain) {
+        expect(chain.traces, hasLength(3));
+        expect(chain.traces[0].frames.first, frameMember(startsWith('main')));
+        expect(chain.traces[1].frames,
+            contains(frameMember(startsWith('chainForTrace'))));
+        expect(chain.traces[2].frames,
+            contains(frameMember(startsWith('inPeriodicTimer'))));
+      });
+    });
+
+    test('called for a stack trace from a nested series of asynchronous '
+        'operations', () {
+      return Chain.capture(() {
+        return chainForTrace((callback) {
+          inPeriodicTimer(() => inOneShotTimer(() => inMicrotask(callback)));
+        }, () => throw 'error');
+      }).then((chain) {
+        expect(chain.traces, hasLength(5));
+        expect(chain.traces[0].frames.first, frameMember(startsWith('main')));
+        expect(chain.traces[1].frames,
+            contains(frameMember(startsWith('chainForTrace'))));
+        expect(chain.traces[2].frames,
+            contains(frameMember(startsWith('inMicrotask'))));
+        expect(chain.traces[3].frames,
+            contains(frameMember(startsWith('inOneShotTimer'))));
+        expect(chain.traces[4].frames,
+            contains(frameMember(startsWith('inPeriodicTimer'))));
+      });
+    });
+
+    test('called for a stack trace from a long future chain', () {
+      return Chain.capture(() {
+        return chainForTrace(inFutureChain, () => throw 'error');
+      }).then((chain) {
+        expect(chain.traces, hasLength(3));
+        expect(chain.traces[0].frames.first, frameMember(startsWith('main')));
+        expect(chain.traces[1].frames,
+            contains(frameMember(startsWith('chainForTrace'))));
+        expect(chain.traces[2].frames,
+            contains(frameMember(startsWith('inFutureChain'))));
+      });
+    });
+
+    test('called for an unregistered stack trace returns a chain wrapping that '
+        'trace', () {
+      var trace;
+      var chain = Chain.capture(() {
+        try {
+          throw 'error';
+        } catch (_, stackTrace) {
+          trace = stackTrace;
+          return new Chain.forTrace(stackTrace);
+        }
+      });
+
+      expect(chain.traces, hasLength(1));
+      expect(chain.traces.first.toString(),
+          equals(new Trace.from(trace).toString()));
+    });
+  });
+
+  test('forTrace() outside of capture() returns a chain wrapping the given '
+      'trace', () {
+    var trace;
+    var chain = Chain.capture(() {
+      try {
+        throw 'error';
+      } catch (_, stackTrace) {
+        trace = stackTrace;
+        return new Chain.forTrace(stackTrace);
+      }
+    });
+
+    expect(chain.traces, hasLength(1));
+    expect(chain.traces.first.toString(),
+        equals(new Trace.from(trace).toString()));
+  });
+}
diff --git a/mojo/public/dart/third_party/stack_trace/test/frame_test.dart b/mojo/public/dart/third_party/stack_trace/test/frame_test.dart
new file mode 100644
index 0000000..63035e0
--- /dev/null
+++ b/mojo/public/dart/third_party/stack_trace/test/frame_test.dart
@@ -0,0 +1,573 @@
+// 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 frame_test;
+
+import 'package:path/path.dart' as path;
+import 'package:stack_trace/stack_trace.dart';
+import 'package:test/test.dart';
+
+void main() {
+  group('.parseVM', () {
+    test('parses a stack frame with column correctly', () {
+      var frame = new Frame.parseVM("#1      Foo._bar "
+          "(file:///home/nweiz/code/stuff.dart:42:21)");
+      expect(frame.uri,
+          equals(Uri.parse("file:///home/nweiz/code/stuff.dart")));
+      expect(frame.line, equals(42));
+      expect(frame.column, equals(21));
+      expect(frame.member, equals('Foo._bar'));
+    });
+
+    test('parses a stack frame without column correctly', () {
+      var frame = new Frame.parseVM("#1      Foo._bar "
+          "(file:///home/nweiz/code/stuff.dart:24)");
+      expect(frame.uri,
+          equals(Uri.parse("file:///home/nweiz/code/stuff.dart")));
+      expect(frame.line, equals(24));
+      expect(frame.column, null);
+      expect(frame.member, equals('Foo._bar'));
+    });
+
+    // This can happen with async stack traces. See issue 22009.
+    test('parses a stack frame without line or column correctly', () {
+      var frame = new Frame.parseVM("#1      Foo._bar "
+          "(file:///home/nweiz/code/stuff.dart)");
+      expect(frame.uri,
+          equals(Uri.parse("file:///home/nweiz/code/stuff.dart")));
+      expect(frame.line, isNull);
+      expect(frame.column, isNull);
+      expect(frame.member, equals('Foo._bar'));
+    });
+
+    test('converts "<anonymous closure>" to "<fn>"', () {
+      String parsedMember(String member) =>
+          new Frame.parseVM('#0 $member (foo:0:0)').member;
+
+      expect(parsedMember('Foo.<anonymous closure>'), equals('Foo.<fn>'));
+      expect(parsedMember('<anonymous closure>.<anonymous closure>.bar'),
+          equals('<fn>.<fn>.bar'));
+    });
+
+    test('converts "<<anonymous closure>_async_body>" to "<async>"', () {
+      var frame = new Frame.parseVM(
+          '#0 Foo.<<anonymous closure>_async_body> (foo:0:0)');
+      expect(frame.member, equals('Foo.<async>'));
+    });
+
+    test('converts "<function_name_async_body>" to "<async>"', () {
+      var frame = new Frame.parseVM(
+          '#0 Foo.<function_name_async_body> (foo:0:0)');
+      expect(frame.member, equals('Foo.<async>'));
+    });
+
+    test('parses a folded frame correctly', () {
+      var frame = new Frame.parseVM('...');
+
+      expect(frame.member, equals('...'));
+      expect(frame.uri, equals(new Uri()));
+      expect(frame.line, isNull);
+      expect(frame.column, isNull);
+    });
+
+    test('returns an UnparsedFrame for malformed frames', () {
+      expectIsUnparsed((text) => new Frame.parseV8(text), '');
+      expectIsUnparsed((text) => new Frame.parseV8(text), '#1');
+      expectIsUnparsed((text) => new Frame.parseV8(text), '#1      Foo');
+      expectIsUnparsed((text) => new Frame.parseV8(text),
+          '#1      (dart:async/future.dart:10:15)');
+      expectIsUnparsed((text) => new Frame.parseV8(text),
+          'Foo (dart:async/future.dart:10:15)');
+    });
+  });
+
+  group('.parseV8', () {
+    test('parses a stack frame correctly', () {
+      var frame = new Frame.parseV8("    at VW.call\$0 "
+          "(http://pub.dartlang.org/stuff.dart.js:560:28)");
+      expect(frame.uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.dart.js")));
+      expect(frame.line, equals(560));
+      expect(frame.column, equals(28));
+      expect(frame.member, equals('VW.call\$0'));
+    });
+
+    test('parses a stack frame with an absolute POSIX path correctly', () {
+      var frame = new Frame.parseV8("    at VW.call\$0 "
+          "(/path/to/stuff.dart.js:560:28)");
+      expect(frame.uri, equals(Uri.parse("file:///path/to/stuff.dart.js")));
+      expect(frame.line, equals(560));
+      expect(frame.column, equals(28));
+      expect(frame.member, equals('VW.call\$0'));
+    });
+
+    test('parses a stack frame with an absolute Windows path correctly', () {
+      var frame = new Frame.parseV8("    at VW.call\$0 "
+          r"(C:\path\to\stuff.dart.js:560:28)");
+      expect(frame.uri, equals(Uri.parse("file:///C:/path/to/stuff.dart.js")));
+      expect(frame.line, equals(560));
+      expect(frame.column, equals(28));
+      expect(frame.member, equals('VW.call\$0'));
+    });
+
+    test('parses a stack frame with a Windows UNC path correctly', () {
+      var frame = new Frame.parseV8("    at VW.call\$0 "
+          r"(\\mount\path\to\stuff.dart.js:560:28)");
+      expect(frame.uri,
+          equals(Uri.parse("file://mount/path/to/stuff.dart.js")));
+      expect(frame.line, equals(560));
+      expect(frame.column, equals(28));
+      expect(frame.member, equals('VW.call\$0'));
+    });
+
+    test('parses a stack frame with a relative POSIX path correctly', () {
+      var frame = new Frame.parseV8("    at VW.call\$0 "
+          "(path/to/stuff.dart.js:560:28)");
+      expect(frame.uri, equals(Uri.parse("path/to/stuff.dart.js")));
+      expect(frame.line, equals(560));
+      expect(frame.column, equals(28));
+      expect(frame.member, equals('VW.call\$0'));
+    });
+
+    test('parses a stack frame with a relative Windows path correctly', () {
+      var frame = new Frame.parseV8("    at VW.call\$0 "
+          r"(path\to\stuff.dart.js:560:28)");
+      expect(frame.uri, equals(Uri.parse("path/to/stuff.dart.js")));
+      expect(frame.line, equals(560));
+      expect(frame.column, equals(28));
+      expect(frame.member, equals('VW.call\$0'));
+    });
+
+    test('parses an anonymous stack frame correctly', () {
+      var frame = new Frame.parseV8(
+          "    at http://pub.dartlang.org/stuff.dart.js:560:28");
+      expect(frame.uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.dart.js")));
+      expect(frame.line, equals(560));
+      expect(frame.column, equals(28));
+      expect(frame.member, equals('<fn>'));
+    });
+
+    test('parses a native stack frame correctly', () {
+      var frame = new Frame.parseV8(
+          "    at Object.stringify (native)");
+      expect(frame.uri, Uri.parse('native'));
+      expect(frame.line, isNull);
+      expect(frame.column, isNull);
+      expect(frame.member, equals('Object.stringify'));
+    });
+
+    test('parses a stack frame with [as ...] correctly', () {
+      // Ignore "[as ...]", since other stack trace formats don't support a
+      // similar construct.
+      var frame = new Frame.parseV8("    at VW.call\$0 [as call\$4] "
+          "(http://pub.dartlang.org/stuff.dart.js:560:28)");
+      expect(frame.uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.dart.js")));
+      expect(frame.line, equals(560));
+      expect(frame.column, equals(28));
+      expect(frame.member, equals('VW.call\$0'));
+    });
+
+    test('parses a basic eval stack frame correctly', () {
+      var frame = new Frame.parseV8("    at eval (eval at <anonymous> "
+          "(http://pub.dartlang.org/stuff.dart.js:560:28))");
+      expect(frame.uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.dart.js")));
+      expect(frame.line, equals(560));
+      expect(frame.column, equals(28));
+      expect(frame.member, equals('eval'));
+    });
+
+    test('parses an IE10 eval stack frame correctly', () {
+      var frame = new Frame.parseV8("    at eval (eval at Anonymous function "
+          "(http://pub.dartlang.org/stuff.dart.js:560:28))");
+      expect(frame.uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.dart.js")));
+      expect(frame.line, equals(560));
+      expect(frame.column, equals(28));
+      expect(frame.member, equals('eval'));
+    });
+
+    test('parses an eval stack frame with inner position info correctly', () {
+      var frame = new Frame.parseV8("    at eval (eval at <anonymous> "
+          "(http://pub.dartlang.org/stuff.dart.js:560:28), <anonymous>:3:28)");
+      expect(frame.uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.dart.js")));
+      expect(frame.line, equals(560));
+      expect(frame.column, equals(28));
+      expect(frame.member, equals('eval'));
+    });
+
+    test('parses a nested eval stack frame correctly', () {
+      var frame = new Frame.parseV8("    at eval (eval at <anonymous> "
+          "(eval at sub (http://pub.dartlang.org/stuff.dart.js:560:28)))");
+      expect(frame.uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.dart.js")));
+      expect(frame.line, equals(560));
+      expect(frame.column, equals(28));
+      expect(frame.member, equals('eval'));
+    });
+
+    test('converts "<anonymous>" to "<fn>"', () {
+      String parsedMember(String member) =>
+          new Frame.parseV8('    at $member (foo:0:0)').member;
+
+      expect(parsedMember('Foo.<anonymous>'), equals('Foo.<fn>'));
+      expect(parsedMember('<anonymous>.<anonymous>.bar'),
+          equals('<fn>.<fn>.bar'));
+    });
+
+    test('returns an UnparsedFrame for malformed frames', () {
+      expectIsUnparsed((text) => new Frame.parseV8(text), '');
+      expectIsUnparsed((text) => new Frame.parseV8(text), '    at');
+      expectIsUnparsed((text) => new Frame.parseV8(text), '    at Foo');
+      expectIsUnparsed((text) => new Frame.parseV8(text),
+          '    at Foo (dart:async/future.dart)');
+      expectIsUnparsed((text) => new Frame.parseV8(text),
+          '    at (dart:async/future.dart:10:15)');
+      expectIsUnparsed((text) => new Frame.parseV8(text),
+          'Foo (dart:async/future.dart:10:15)');
+      expectIsUnparsed((text) => new Frame.parseV8(text),
+          '    at dart:async/future.dart');
+      expectIsUnparsed((text) => new Frame.parseV8(text),
+          '    at dart:async/future.dart:10');
+      expectIsUnparsed((text) => new Frame.parseV8(text),
+          'dart:async/future.dart:10:15');
+    });
+  });
+
+  group('.parseFirefox/.parseSafari', () {
+    test('parses a simple stack frame correctly', () {
+      var frame = new Frame.parseFirefox(
+          ".VW.call\$0@http://pub.dartlang.org/stuff.dart.js:560");
+      expect(frame.uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.dart.js")));
+      expect(frame.line, equals(560));
+      expect(frame.column, isNull);
+      expect(frame.member, equals('VW.call\$0'));
+    });
+
+    test('parses a stack frame with an absolute POSIX path correctly', () {
+      var frame = new Frame.parseFirefox(
+          ".VW.call\$0@/path/to/stuff.dart.js:560");
+      expect(frame.uri, equals(Uri.parse("file:///path/to/stuff.dart.js")));
+      expect(frame.line, equals(560));
+      expect(frame.column, isNull);
+      expect(frame.member, equals('VW.call\$0'));
+    });
+
+    test('parses a stack frame with an absolute Windows path correctly', () {
+      var frame = new Frame.parseFirefox(
+          r".VW.call$0@C:\path\to\stuff.dart.js:560");
+      expect(frame.uri, equals(Uri.parse("file:///C:/path/to/stuff.dart.js")));
+      expect(frame.line, equals(560));
+      expect(frame.column, isNull);
+      expect(frame.member, equals('VW.call\$0'));
+    });
+
+    test('parses a stack frame with a Windows UNC path correctly', () {
+      var frame = new Frame.parseFirefox(
+          r".VW.call$0@\\mount\path\to\stuff.dart.js:560");
+      expect(frame.uri,
+          equals(Uri.parse("file://mount/path/to/stuff.dart.js")));
+      expect(frame.line, equals(560));
+      expect(frame.column, isNull);
+      expect(frame.member, equals('VW.call\$0'));
+    });
+
+    test('parses a stack frame with a relative POSIX path correctly', () {
+      var frame = new Frame.parseFirefox(
+          ".VW.call\$0@path/to/stuff.dart.js:560");
+      expect(frame.uri, equals(Uri.parse("path/to/stuff.dart.js")));
+      expect(frame.line, equals(560));
+      expect(frame.column, isNull);
+      expect(frame.member, equals('VW.call\$0'));
+    });
+
+    test('parses a stack frame with a relative Windows path correctly', () {
+      var frame = new Frame.parseFirefox(
+          r".VW.call$0@path\to\stuff.dart.js:560");
+      expect(frame.uri, equals(Uri.parse("path/to/stuff.dart.js")));
+      expect(frame.line, equals(560));
+      expect(frame.column, isNull);
+      expect(frame.member, equals('VW.call\$0'));
+    });
+
+    test('parses a simple anonymous stack frame correctly', () {
+      var frame = new Frame.parseFirefox(
+          "@http://pub.dartlang.org/stuff.dart.js:560");
+      expect(frame.uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.dart.js")));
+      expect(frame.line, equals(560));
+      expect(frame.column, isNull);
+      expect(frame.member, equals("<fn>"));
+    });
+
+    test('parses a nested anonymous stack frame correctly', () {
+      var frame = new Frame.parseFirefox(
+          ".foo/<@http://pub.dartlang.org/stuff.dart.js:560");
+      expect(frame.uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.dart.js")));
+      expect(frame.line, equals(560));
+      expect(frame.column, isNull);
+      expect(frame.member, equals("foo.<fn>"));
+
+      frame = new Frame.parseFirefox(
+          ".foo/@http://pub.dartlang.org/stuff.dart.js:560");
+      expect(frame.uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.dart.js")));
+      expect(frame.line, equals(560));
+      expect(frame.column, isNull);
+      expect(frame.member, equals("foo.<fn>"));
+    });
+
+    test('parses a named nested anonymous stack frame correctly', () {
+      var frame = new Frame.parseFirefox(
+          ".foo/.name<@http://pub.dartlang.org/stuff.dart.js:560");
+      expect(frame.uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.dart.js")));
+      expect(frame.line, equals(560));
+      expect(frame.column, isNull);
+      expect(frame.member, equals("foo.<fn>"));
+
+      frame = new Frame.parseFirefox(
+          ".foo/.name@http://pub.dartlang.org/stuff.dart.js:560");
+      expect(frame.uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.dart.js")));
+      expect(frame.line, equals(560));
+      expect(frame.column, isNull);
+      expect(frame.member, equals("foo.<fn>"));
+    });
+
+    test('parses a stack frame with parameters correctly', () {
+      var frame = new Frame.parseFirefox(
+          '.foo(12, "@)()/<")@http://pub.dartlang.org/stuff.dart.js:560');
+      expect(frame.uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.dart.js")));
+      expect(frame.line, equals(560));
+      expect(frame.column, isNull);
+      expect(frame.member, equals("foo"));
+    });
+
+    test('parses a nested anonymous stack frame with parameters correctly', () {
+      var frame = new Frame.parseFirefox(
+          '.foo(12, "@)()/<")/.fn<@'
+          'http://pub.dartlang.org/stuff.dart.js:560');
+      expect(frame.uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.dart.js")));
+      expect(frame.line, equals(560));
+      expect(frame.column, isNull);
+      expect(frame.member, equals("foo.<fn>"));
+    });
+
+    test('parses a deeply-nested anonymous stack frame with parameters '
+        'correctly', () {
+      var frame = new Frame.parseFirefox(
+          '.convertDartClosureToJS/\$function</<@'
+          'http://pub.dartlang.org/stuff.dart.js:560');
+      expect(frame.uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.dart.js")));
+      expect(frame.line, equals(560));
+      expect(frame.column, isNull);
+      expect(frame.member, equals("convertDartClosureToJS.<fn>.<fn>"));
+    });
+
+    test('returns an UnparsedFrame for malformed frames', () {
+      expectIsUnparsed((text) => new Frame.parseFirefox(text), '');
+      expectIsUnparsed((text) => new Frame.parseFirefox(text), '.foo');
+      expectIsUnparsed((text) => new Frame.parseFirefox(text),
+          '.foo@dart:async/future.dart');
+      expectIsUnparsed((text) => new Frame.parseFirefox(text),
+          '.foo(@dart:async/future.dart:10');
+      expectIsUnparsed((text) => new Frame.parseFirefox(text),
+          '@dart:async/future.dart');
+    });
+
+    test('parses a simple stack frame correctly', () {
+      var frame = new Frame.parseFirefox(
+          "foo\$bar@http://dartlang.org/foo/bar.dart:10:11");
+      expect(frame.uri, equals(Uri.parse("http://dartlang.org/foo/bar.dart")));
+      expect(frame.line, equals(10));
+      expect(frame.column, equals(11));
+      expect(frame.member, equals('foo\$bar'));
+    });
+
+    test('parses an anonymous stack frame correctly', () {
+      var frame = new Frame.parseFirefox(
+          "http://dartlang.org/foo/bar.dart:10:11");
+      expect(frame.uri, equals(Uri.parse("http://dartlang.org/foo/bar.dart")));
+      expect(frame.line, equals(10));
+      expect(frame.column, equals(11));
+      expect(frame.member, equals('<fn>'));
+    });
+
+    test('parses a stack frame with no line correctly', () {
+      var frame = new Frame.parseFirefox(
+          "foo\$bar@http://dartlang.org/foo/bar.dart::11");
+      expect(frame.uri, equals(Uri.parse("http://dartlang.org/foo/bar.dart")));
+      expect(frame.line, isNull);
+      expect(frame.column, equals(11));
+      expect(frame.member, equals('foo\$bar'));
+    });
+
+    test('parses a stack frame with no column correctly', () {
+      var frame = new Frame.parseFirefox(
+          "foo\$bar@http://dartlang.org/foo/bar.dart:10:");
+      expect(frame.uri, equals(Uri.parse("http://dartlang.org/foo/bar.dart")));
+      expect(frame.line, equals(10));
+      expect(frame.column, isNull);
+      expect(frame.member, equals('foo\$bar'));
+    });
+
+    test('parses a stack frame with no line or column correctly', () {
+      var frame = new Frame.parseFirefox(
+          "foo\$bar@http://dartlang.org/foo/bar.dart:10:11");
+      expect(frame.uri, equals(Uri.parse("http://dartlang.org/foo/bar.dart")));
+      expect(frame.line, equals(10));
+      expect(frame.column, equals(11));
+      expect(frame.member, equals('foo\$bar'));
+    });
+  });
+
+  group('.parseFriendly', () {
+    test('parses a simple stack frame correctly', () {
+      var frame = new Frame.parseFriendly(
+          "http://dartlang.org/foo/bar.dart 10:11  Foo.<fn>.bar");
+      expect(frame.uri, equals(Uri.parse("http://dartlang.org/foo/bar.dart")));
+      expect(frame.line, equals(10));
+      expect(frame.column, equals(11));
+      expect(frame.member, equals('Foo.<fn>.bar'));
+    });
+
+    test('parses a stack frame with no line or column correctly', () {
+      var frame = new Frame.parseFriendly(
+          "http://dartlang.org/foo/bar.dart  Foo.<fn>.bar");
+      expect(frame.uri, equals(Uri.parse("http://dartlang.org/foo/bar.dart")));
+      expect(frame.line, isNull);
+      expect(frame.column, isNull);
+      expect(frame.member, equals('Foo.<fn>.bar'));
+    });
+
+    test('parses a stack frame with no line correctly', () {
+      var frame = new Frame.parseFriendly(
+          "http://dartlang.org/foo/bar.dart 10  Foo.<fn>.bar");
+      expect(frame.uri, equals(Uri.parse("http://dartlang.org/foo/bar.dart")));
+      expect(frame.line, equals(10));
+      expect(frame.column, isNull);
+      expect(frame.member, equals('Foo.<fn>.bar'));
+    });
+
+    test('parses a stack frame with a relative path correctly', () {
+      var frame = new Frame.parseFriendly("foo/bar.dart 10:11    Foo.<fn>.bar");
+      expect(frame.uri, equals(
+          path.toUri(path.absolute(path.join('foo', 'bar.dart')))));
+      expect(frame.line, equals(10));
+      expect(frame.column, equals(11));
+      expect(frame.member, equals('Foo.<fn>.bar'));
+    });
+
+    test('returns an UnparsedFrame for malformed frames', () {
+      expectIsUnparsed((text) => new Frame.parseFriendly(text), '');
+      expectIsUnparsed((text) => new Frame.parseFriendly(text), 'foo/bar.dart');
+      expectIsUnparsed((text) => new Frame.parseFriendly(text),
+          'foo/bar.dart 10:11');
+    });
+  });
+
+  test('only considers dart URIs to be core', () {
+    bool isCore(String library) =>
+      new Frame.parseVM('#0 Foo ($library:0:0)').isCore;
+
+    expect(isCore('dart:core'), isTrue);
+    expect(isCore('dart:async'), isTrue);
+    expect(isCore('dart:core/uri.dart'), isTrue);
+    expect(isCore('dart:async/future.dart'), isTrue);
+    expect(isCore('bart:core'), isFalse);
+    expect(isCore('sdart:core'), isFalse);
+    expect(isCore('darty:core'), isFalse);
+    expect(isCore('bart:core/uri.dart'), isFalse);
+  });
+
+  group('.library', () {
+    test('returns the URI string for non-file URIs', () {
+      expect(new Frame.parseVM('#0 Foo (dart:async/future.dart:0:0)').library,
+          equals('dart:async/future.dart'));
+      expect(new Frame.parseVM('#0 Foo '
+              '(http://dartlang.org/stuff/thing.dart:0:0)').library,
+          equals('http://dartlang.org/stuff/thing.dart'));
+    });
+
+    test('returns the relative path for file URIs', () {
+      expect(new Frame.parseVM('#0 Foo (foo/bar.dart:0:0)').library,
+          equals(path.join('foo', 'bar.dart')));
+    });
+
+    test('truncates data: URIs', () {
+      var frame = new Frame.parseVM(
+          '#0 Foo (data:application/dart;charset=utf-8,blah:0:0)');
+      expect(frame.library, equals('data:...'));
+    });
+  });
+
+  group('.location', () {
+    test('returns the library and line/column numbers for non-core '
+        'libraries', () {
+      expect(new Frame.parseVM('#0 Foo '
+              '(http://dartlang.org/thing.dart:5:10)').location,
+          equals('http://dartlang.org/thing.dart 5:10'));
+      expect(new Frame.parseVM('#0 Foo (foo/bar.dart:1:2)').location,
+          equals('${path.join('foo', 'bar.dart')} 1:2'));
+    });
+  });
+
+  group('.package', () {
+    test('returns null for non-package URIs', () {
+      expect(new Frame.parseVM('#0 Foo (dart:async/future.dart:0:0)').package,
+          isNull);
+      expect(new Frame.parseVM('#0 Foo '
+              '(http://dartlang.org/stuff/thing.dart:0:0)').package,
+          isNull);
+    });
+
+    test('returns the package name for package: URIs', () {
+      expect(new Frame.parseVM('#0 Foo (package:foo/foo.dart:0:0)').package,
+          equals('foo'));
+      expect(new Frame.parseVM('#0 Foo (package:foo/zap/bar.dart:0:0)').package,
+          equals('foo'));
+    });
+  });
+
+  group('.toString()', () {
+    test('returns the library and line/column numbers for non-core '
+        'libraries', () {
+      expect(new Frame.parseVM('#0 Foo (http://dartlang.org/thing.dart:5:10)')
+              .toString(),
+          equals('http://dartlang.org/thing.dart 5:10 in Foo'));
+    });
+
+    test('converts "<anonymous closure>" to "<fn>"', () {
+      expect(new Frame.parseVM('#0 Foo.<anonymous closure> '
+              '(dart:core/uri.dart:5:10)').toString(),
+          equals('dart:core/uri.dart 5:10 in Foo.<fn>'));
+    });
+
+    test('prints a frame without a column correctly', () {
+      expect(new Frame.parseVM('#0 Foo (dart:core/uri.dart:5)').toString(),
+          equals('dart:core/uri.dart 5 in Foo'));
+    });
+
+    test('prints relative paths as relative', () {
+      var relative = path.normalize('relative/path/to/foo.dart');
+      expect(new Frame.parseFriendly('$relative 5:10  Foo').toString(),
+          equals('$relative 5:10 in Foo'));
+    });
+  });
+}
+
+void expectIsUnparsed(Frame constructor(String text), String text) {
+  var frame = constructor(text);
+  expect(frame, new isInstanceOf<UnparsedFrame>());
+  expect(frame.toString(), equals(text));
+}
diff --git a/mojo/public/dart/third_party/stack_trace/test/trace_test.dart b/mojo/public/dart/third_party/stack_trace/test/trace_test.dart
new file mode 100644
index 0000000..d12bc71
--- /dev/null
+++ b/mojo/public/dart/third_party/stack_trace/test/trace_test.dart
@@ -0,0 +1,430 @@
+// 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 trace_test;
+
+import 'package:path/path.dart' as path;
+import 'package:stack_trace/stack_trace.dart';
+import 'package:test/test.dart';
+
+String getStackTraceString() {
+  try {
+    throw '';
+  } catch (_, stackTrace) {
+    return stackTrace.toString();
+  }
+}
+
+StackTrace getStackTraceObject() {
+  try {
+    throw '';
+  } catch (_, stackTrace) {
+    return stackTrace;
+  }
+}
+
+Trace getCurrentTrace([int level]) => new Trace.current(level);
+
+Trace nestedGetCurrentTrace(int level) => getCurrentTrace(level);
+
+void main() {
+  // This just shouldn't crash.
+  test('a native stack trace is parseable', () => new Trace.current());
+
+  group('.parse', () {
+    test('.parse parses a VM stack trace correctly', () {
+      var trace = new Trace.parse(
+          '#0      Foo._bar (file:///home/nweiz/code/stuff.dart:42:21)\n'
+          '#1      zip.<anonymous closure>.zap (dart:async/future.dart:0:2)\n'
+          '#2      zip.<anonymous closure>.zap (http://pub.dartlang.org/thing.'
+              'dart:1:100)');
+
+      expect(trace.frames[0].uri,
+          equals(Uri.parse("file:///home/nweiz/code/stuff.dart")));
+      expect(trace.frames[1].uri, equals(Uri.parse("dart:async/future.dart")));
+      expect(trace.frames[2].uri,
+          equals(Uri.parse("http://pub.dartlang.org/thing.dart")));
+    });
+
+    test('parses a V8 stack trace correctly', () {
+      var trace = new Trace.parse(
+          'Error\n'
+          '    at Foo._bar (http://pub.dartlang.org/stuff.js:42:21)\n'
+          '    at http://pub.dartlang.org/stuff.js:0:2\n'
+          '    at zip.<anonymous>.zap '
+              '(http://pub.dartlang.org/thing.js:1:100)');
+
+      expect(trace.frames[0].uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.js")));
+      expect(trace.frames[1].uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.js")));
+      expect(trace.frames[2].uri,
+          equals(Uri.parse("http://pub.dartlang.org/thing.js")));
+
+      trace = new Trace.parse(
+          "Exception: foo\n"
+          '    at Foo._bar (http://pub.dartlang.org/stuff.js:42:21)\n'
+          '    at http://pub.dartlang.org/stuff.js:0:2\n'
+          '    at zip.<anonymous>.zap '
+              '(http://pub.dartlang.org/thing.js:1:100)');
+
+      expect(trace.frames[0].uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.js")));
+      expect(trace.frames[1].uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.js")));
+      expect(trace.frames[2].uri,
+          equals(Uri.parse("http://pub.dartlang.org/thing.js")));
+
+      trace = new Trace.parse(
+          'Exception: foo\n'
+          '    bar\n'
+          '    at Foo._bar (http://pub.dartlang.org/stuff.js:42:21)\n'
+          '    at http://pub.dartlang.org/stuff.js:0:2\n'
+          '    at zip.<anonymous>.zap '
+              '(http://pub.dartlang.org/thing.js:1:100)');
+
+      expect(trace.frames[0].uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.js")));
+      expect(trace.frames[1].uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.js")));
+      expect(trace.frames[2].uri,
+          equals(Uri.parse("http://pub.dartlang.org/thing.js")));
+    });
+
+    // JavaScriptCore traces are just like V8, except that it doesn't have a
+    // header and it starts with a tab rather than spaces.
+    test('parses a JavaScriptCore stack trace correctly', () {
+      var trace = new Trace.parse(
+          '\tat Foo._bar (http://pub.dartlang.org/stuff.js:42:21)\n'
+          '\tat http://pub.dartlang.org/stuff.js:0:2\n'
+          '\tat zip.<anonymous>.zap '
+              '(http://pub.dartlang.org/thing.js:1:100)');
+
+      expect(trace.frames[0].uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.js")));
+      expect(trace.frames[1].uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.js")));
+      expect(trace.frames[2].uri,
+          equals(Uri.parse("http://pub.dartlang.org/thing.js")));
+
+      trace = new Trace.parse(
+          '\tat Foo._bar (http://pub.dartlang.org/stuff.js:42:21)\n'
+          '\tat \n'
+          '\tat zip.<anonymous>.zap '
+              '(http://pub.dartlang.org/thing.js:1:100)');
+
+      expect(trace.frames[0].uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.js")));
+      expect(trace.frames[1].uri,
+          equals(Uri.parse("http://pub.dartlang.org/thing.js")));
+    });
+
+    test('parses a Firefox/Safari stack trace correctly', () {
+      var trace = new Trace.parse(
+          'Foo._bar@http://pub.dartlang.org/stuff.js:42\n'
+          'zip/<@http://pub.dartlang.org/stuff.js:0\n'
+          'zip.zap(12, "@)()/<")@http://pub.dartlang.org/thing.js:1');
+
+      expect(trace.frames[0].uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.js")));
+      expect(trace.frames[1].uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.js")));
+      expect(trace.frames[2].uri,
+          equals(Uri.parse("http://pub.dartlang.org/thing.js")));
+
+      trace = new Trace.parse(
+          'zip/<@http://pub.dartlang.org/stuff.js:0\n'
+          'Foo._bar@http://pub.dartlang.org/stuff.js:42\n'
+          'zip.zap(12, "@)()/<")@http://pub.dartlang.org/thing.js:1');
+
+      expect(trace.frames[0].uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.js")));
+      expect(trace.frames[1].uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.js")));
+      expect(trace.frames[2].uri,
+          equals(Uri.parse("http://pub.dartlang.org/thing.js")));
+
+      trace = new Trace.parse(
+          'zip.zap(12, "@)()/<")@http://pub.dartlang.org/thing.js:1\n'
+          'zip/<@http://pub.dartlang.org/stuff.js:0\n'
+          'Foo._bar@http://pub.dartlang.org/stuff.js:42');
+
+      expect(trace.frames[0].uri,
+          equals(Uri.parse("http://pub.dartlang.org/thing.js")));
+      expect(trace.frames[1].uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.js")));
+      expect(trace.frames[2].uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.js")));
+    });
+
+    test('parses a Firefox/Safari stack trace containing native code correctly',
+        () {
+      var trace = new Trace.parse(
+          'Foo._bar@http://pub.dartlang.org/stuff.js:42\n'
+          'zip/<@http://pub.dartlang.org/stuff.js:0\n'
+          'zip.zap(12, "@)()/<")@http://pub.dartlang.org/thing.js:1\n'
+          '[native code]');
+
+      expect(trace.frames[0].uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.js")));
+      expect(trace.frames[1].uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.js")));
+      expect(trace.frames[2].uri,
+          equals(Uri.parse("http://pub.dartlang.org/thing.js")));
+      expect(trace.frames.length, equals(3));
+    });
+
+    test('parses a Firefox/Safari stack trace without a method name correctly',
+        () {
+      var trace = new Trace.parse(
+          'http://pub.dartlang.org/stuff.js:42\n'
+          'zip/<@http://pub.dartlang.org/stuff.js:0\n'
+          'zip.zap(12, "@)()/<")@http://pub.dartlang.org/thing.js:1');
+
+      expect(trace.frames[0].uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.js")));
+      expect(trace.frames[0].member, equals('<fn>'));
+      expect(trace.frames[1].uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.js")));
+      expect(trace.frames[2].uri,
+          equals(Uri.parse("http://pub.dartlang.org/thing.js")));
+    });
+
+    test('parses a Firefox/Safari stack trace with an empty line correctly',
+        () {
+      var trace = new Trace.parse(
+          'Foo._bar@http://pub.dartlang.org/stuff.js:42\n'
+          '\n'
+          'zip/<@http://pub.dartlang.org/stuff.js:0\n'
+          'zip.zap(12, "@)()/<")@http://pub.dartlang.org/thing.js:1');
+
+      expect(trace.frames[0].uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.js")));
+      expect(trace.frames[1].uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.js")));
+      expect(trace.frames[2].uri,
+          equals(Uri.parse("http://pub.dartlang.org/thing.js")));
+    });
+
+    test('parses a Firefox/Safari stack trace with a column number correctly',
+        () {
+      var trace = new Trace.parse(
+          'Foo._bar@http://pub.dartlang.org/stuff.js:42:2\n'
+          'zip/<@http://pub.dartlang.org/stuff.js:0\n'
+          'zip.zap(12, "@)()/<")@http://pub.dartlang.org/thing.js:1');
+
+      expect(trace.frames[0].uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.js")));
+      expect(trace.frames[0].line, equals(42));
+      expect(trace.frames[0].column, equals(2));
+      expect(trace.frames[1].uri,
+          equals(Uri.parse("http://pub.dartlang.org/stuff.js")));
+      expect(trace.frames[2].uri,
+          equals(Uri.parse("http://pub.dartlang.org/thing.js")));
+    });
+
+    test('parses a package:stack_trace stack trace correctly', () {
+      var trace = new Trace.parse(
+          'http://dartlang.org/foo/bar.dart 10:11  Foo.<fn>.bar\n'
+          'http://dartlang.org/foo/baz.dart        Foo.<fn>.bar');
+
+      expect(trace.frames[0].uri,
+          equals(Uri.parse("http://dartlang.org/foo/bar.dart")));
+      expect(trace.frames[1].uri,
+          equals(Uri.parse("http://dartlang.org/foo/baz.dart")));
+    });
+
+    test('parses a package:stack_trace stack chain correctly', () {
+      var trace = new Trace.parse(
+          'http://dartlang.org/foo/bar.dart 10:11  Foo.<fn>.bar\n'
+          'http://dartlang.org/foo/baz.dart        Foo.<fn>.bar\n'
+          '===== asynchronous gap ===========================\n'
+          'http://dartlang.org/foo/bang.dart 10:11  Foo.<fn>.bar\n'
+          'http://dartlang.org/foo/quux.dart        Foo.<fn>.bar');
+
+      expect(trace.frames[0].uri,
+          equals(Uri.parse("http://dartlang.org/foo/bar.dart")));
+      expect(trace.frames[1].uri,
+          equals(Uri.parse("http://dartlang.org/foo/baz.dart")));
+      expect(trace.frames[2].uri,
+          equals(Uri.parse("http://dartlang.org/foo/bang.dart")));
+      expect(trace.frames[3].uri,
+          equals(Uri.parse("http://dartlang.org/foo/quux.dart")));
+    });
+
+    test('parses a real package:stack_trace stack trace correctly', () {
+      var traceString = new Trace.current().toString();
+      expect(new Trace.parse(traceString).toString(), equals(traceString));
+    });
+
+    test('parses an empty string correctly', () {
+      var trace = new Trace.parse('');
+      expect(trace.frames, isEmpty);
+      expect(trace.toString(), equals(''));
+    });
+  });
+
+  test('.toString() nicely formats the stack trace', () {
+    var trace = new Trace.parse('''
+#0      Foo._bar (foo/bar.dart:42:21)
+#1      zip.<anonymous closure>.zap (dart:async/future.dart:0:2)
+#2      zip.<anonymous closure>.zap (http://pub.dartlang.org/thing.dart:1:100)
+''');
+
+    expect(trace.toString(), equals('''
+${path.join('foo', 'bar.dart')} 42:21                        Foo._bar
+dart:async/future.dart 0:2                zip.<fn>.zap
+http://pub.dartlang.org/thing.dart 1:100  zip.<fn>.zap
+'''));
+  });
+
+  test('.vmTrace returns a native-style trace', () {
+    var uri = path.toUri(path.absolute('foo'));
+    var trace = new Trace([
+      new Frame(uri, 10, 20, 'Foo.<fn>'),
+      new Frame(Uri.parse('http://dartlang.org/foo.dart'), null, null, 'bar'),
+      new Frame(Uri.parse('dart:async'), 15, null, 'baz'),
+    ]);
+
+    expect(trace.vmTrace.toString(), equals(
+        '#1      Foo.<anonymous closure> ($uri:10:20)\n'
+        '#2      bar (http://dartlang.org/foo.dart:0:0)\n'
+        '#3      baz (dart:async:15:0)\n'));
+  });
+
+  group("folding", () {
+    test('.terse folds core frames together bottom-up', () {
+      var trace = new Trace.parse('''
+#1 top (dart:async/future.dart:0:2)
+#2 bottom (dart:core/uri.dart:1:100)
+#0 notCore (foo.dart:42:21)
+#3 top (dart:io:5:10)
+#4 bottom (dart:async-patch/future.dart:9:11)
+#5 alsoNotCore (bar.dart:10:20)
+''');
+
+      expect(trace.terse.toString(), equals('''
+dart:core       bottom
+foo.dart 42:21  notCore
+dart:async      bottom
+bar.dart 10:20  alsoNotCore
+'''));
+    });
+
+    test('.terse folds empty async frames', () {
+      var trace = new Trace.parse('''
+#0 top (dart:async/future.dart:0:2)
+#1 empty.<<anonymous closure>_async_body> (bar.dart)
+#2 bottom (dart:async-patch/future.dart:9:11)
+#3 notCore (foo.dart:42:21)
+''');
+
+      expect(trace.terse.toString(), equals('''
+dart:async      bottom
+foo.dart 42:21  notCore
+'''));
+    });
+
+    test('.terse removes the bottom-most async frame', () {
+      var trace = new Trace.parse('''
+#0 notCore (foo.dart:42:21)
+#1 top (dart:async/future.dart:0:2)
+#2 bottom (dart:core/uri.dart:1:100)
+#3 top (dart:io:5:10)
+#4 bottom (dart:async-patch/future.dart:9:11)
+''');
+
+      expect(trace.terse.toString(), equals('''
+foo.dart 42:21  notCore
+'''));
+    });
+
+    test(".terse won't make a trace empty", () {
+      var trace = new Trace.parse('''
+#1 top (dart:async/future.dart:0:2)
+#2 bottom (dart:core/uri.dart:1:100)
+''');
+
+      expect(trace.terse.toString(), equals('''
+dart:core  bottom
+'''));
+    });
+
+    test(".terse won't panic on an empty trace", () {
+      expect(new Trace.parse("").terse.toString(), equals(""));
+    });
+
+    test('.foldFrames folds frames together bottom-up', () {
+      var trace = new Trace.parse('''
+#0 notFoo (foo.dart:42:21)
+#1 fooTop (bar.dart:0:2)
+#2 fooBottom (foo.dart:1:100)
+#3 alsoNotFoo (bar.dart:10:20)
+#4 fooTop (dart:io/socket.dart:5:10)
+#5 fooBottom (dart:async-patch/future.dart:9:11)
+''');
+
+      var folded = trace.foldFrames((frame) => frame.member.startsWith('foo'));
+      expect(folded.toString(), equals('''
+foo.dart 42:21                     notFoo
+foo.dart 1:100                     fooBottom
+bar.dart 10:20                     alsoNotFoo
+dart:async-patch/future.dart 9:11  fooBottom
+'''));
+    });
+
+    test('.foldFrames with terse: true folds core frames as well', () {
+      var trace = new Trace.parse('''
+#0 notFoo (foo.dart:42:21)
+#1 fooTop (bar.dart:0:2)
+#2 coreBottom (dart:async/future.dart:0:2)
+#3 alsoNotFoo (bar.dart:10:20)
+#4 fooTop (foo.dart:9:11)
+#5 coreBottom (dart:async-patch/future.dart:9:11)
+''');
+
+      var folded = trace.foldFrames((frame) => frame.member.startsWith('foo'),
+          terse: true);
+      expect(folded.toString(), equals('''
+foo.dart 42:21  notFoo
+dart:async      coreBottom
+bar.dart 10:20  alsoNotFoo
+'''));
+    });
+
+    test('.foldFrames with terse: true shortens folded frames', () {
+      var trace = new Trace.parse('''
+#0 notFoo (foo.dart:42:21)
+#1 fooTop (bar.dart:0:2)
+#2 fooBottom (package:foo/bar.dart:0:2)
+#3 alsoNotFoo (bar.dart:10:20)
+#4 fooTop (foo.dart:9:11)
+#5 fooBottom (foo/bar.dart:9:11)
+''');
+
+      var folded = trace.foldFrames((frame) => frame.member.startsWith('foo'),
+          terse: true);
+      expect(folded.toString(), equals('''
+foo.dart 42:21  notFoo
+package:foo     fooBottom
+bar.dart 10:20  alsoNotFoo
+foo             fooBottom
+'''));
+    });
+
+    test('.foldFrames will never fold unparsed frames', () {
+      var trace = new Trace.parse(r'''
+.g"cs$#:b";a#>sw{*{ul$"$xqwr`p
+%+j-?uppx<([j@#nu{{>*+$%x-={`{
+!e($b{nj)zs?cgr%!;bmw.+$j+pfj~
+''');
+
+      expect(trace.foldFrames((frame) => true).toString(), equals(r'''
+.g"cs$#:b";a#>sw{*{ul$"$xqwr`p
+%+j-?uppx<([j@#nu{{>*+$%x-={`{
+!e($b{nj)zs?cgr%!;bmw.+$j+pfj~
+'''));
+    });
+  });
+}
diff --git a/mojo/public/dart/third_party/stack_trace/test/utils.dart b/mojo/public/dart/third_party/stack_trace/test/utils.dart
new file mode 100644
index 0000000..912f8f4
--- /dev/null
+++ b/mojo/public/dart/third_party/stack_trace/test/utils.dart
@@ -0,0 +1,37 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library stack_trace.test.utils;
+
+import 'package:test/test.dart';
+
+/// Returns a matcher that runs [matcher] against a [Frame]'s `member` field.
+Matcher frameMember(matcher) =>
+  transform((frame) => frame.member, matcher, 'member');
+
+/// Returns a matcher that runs [matcher] against a [Frame]'s `library` field.
+Matcher frameLibrary(matcher) =>
+  transform((frame) => frame.library, matcher, 'library');
+
+/// Returns a matcher that runs [transformation] on its input, then matches
+/// the output against [matcher].
+///
+/// [description] should be a noun phrase that describes the relation of the
+/// output of [transformation] to its input.
+Matcher transform(transformation(value), matcher, String description) =>
+  new _TransformMatcher(transformation, wrapMatcher(matcher), description);
+
+class _TransformMatcher extends Matcher {
+  final Function _transformation;
+  final Matcher _matcher;
+  final String _description;
+
+  _TransformMatcher(this._transformation, this._matcher, this._description);
+
+  bool matches(item, Map matchState) =>
+    _matcher.matches(_transformation(item), matchState);
+
+  Description describe(Description description) =>
+    description.add(_description).add(' ').addDescriptionOf(_matcher);
+}
diff --git a/mojo/public/dart/third_party/stack_trace/test/vm_test.dart b/mojo/public/dart/third_party/stack_trace/test/vm_test.dart
new file mode 100644
index 0000000..947f14b
--- /dev/null
+++ b/mojo/public/dart/third_party/stack_trace/test/vm_test.dart
@@ -0,0 +1,121 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// This file tests stack_trace's ability to parse live stack traces. It's a
+/// dual of dartium_test.dart, since method names can differ somewhat from
+/// platform to platform. No similar file exists for dart2js since the specific
+/// method names there are implementation details.
+@TestOn('vm')
+
+import 'package:path/path.dart' as path;
+import 'package:stack_trace/stack_trace.dart';
+import 'package:test/test.dart';
+
+String getStackTraceString() {
+  try {
+    throw '';
+  } catch (_, stackTrace) {
+    return stackTrace.toString();
+  }
+}
+
+StackTrace getStackTraceObject() {
+  try {
+    throw '';
+  } catch (_, stackTrace) {
+    return stackTrace;
+  }
+}
+
+Frame getCaller([int level]) {
+  if (level == null) return new Frame.caller();
+  return new Frame.caller(level);
+}
+
+Frame nestedGetCaller(int level) => getCaller(level);
+
+Trace getCurrentTrace([int level]) => new Trace.current(level);
+
+Trace nestedGetCurrentTrace(int level) => getCurrentTrace(level);
+
+void main() {
+  group('Trace', () {
+    test('.parse parses a real stack trace correctly', () {
+      var string = getStackTraceString();
+      var trace = new Trace.parse(string);
+      expect(path.url.basename(trace.frames.first.uri.path),
+          equals('vm_test.dart'));
+      expect(trace.frames.first.member, equals('getStackTraceString'));
+    });
+
+    test('converts from a native stack trace correctly', () {
+      var trace = new Trace.from(getStackTraceObject());
+      expect(path.url.basename(trace.frames.first.uri.path),
+          equals('vm_test.dart'));
+      expect(trace.frames.first.member, equals('getStackTraceObject'));
+    });
+
+    test('.from handles a stack overflow trace correctly', () {
+      overflow() => overflow();
+
+      var trace;
+      try {
+        overflow();
+      } catch (_, stackTrace) {
+        trace = new Trace.from(stackTrace);
+      }
+
+      expect(trace.frames.first.member, equals('main.<fn>.<fn>.overflow'));
+    });
+
+    group('.current()', () {
+      test('with no argument returns a trace starting at the current frame',
+          () {
+        var trace = new Trace.current();
+        expect(trace.frames.first.member, equals('main.<fn>.<fn>.<fn>'));
+      });
+
+      test('at level 0 returns a trace starting at the current frame', () {
+        var trace = new Trace.current(0);
+        expect(trace.frames.first.member, equals('main.<fn>.<fn>.<fn>'));
+      });
+
+      test('at level 1 returns a trace starting at the parent frame', () {
+        var trace = getCurrentTrace(1);
+        expect(trace.frames.first.member, equals('main.<fn>.<fn>.<fn>'));
+      });
+
+      test('at level 2 returns a trace starting at the grandparent frame', () {
+        var trace = nestedGetCurrentTrace(2);
+        expect(trace.frames.first.member, equals('main.<fn>.<fn>.<fn>'));
+      });
+
+      test('throws an ArgumentError for negative levels', () {
+        expect(() => new Trace.current(-1), throwsArgumentError);
+      });
+    });
+  });
+
+  group('Frame.caller()', () {
+    test('with no argument returns the parent frame', () {
+      expect(getCaller().member, equals('main.<fn>.<fn>'));
+    });
+
+    test('at level 0 returns the current frame', () {
+      expect(getCaller(0).member, equals('getCaller'));
+    });
+
+    test('at level 1 returns the current frame', () {
+      expect(getCaller(1).member, equals('main.<fn>.<fn>'));
+    });
+
+    test('at level 2 returns the grandparent frame', () {
+      expect(nestedGetCaller(2).member, equals('main.<fn>.<fn>'));
+    });
+
+    test('throws an ArgumentError for negative levels', () {
+      expect(() => new Frame.caller(-1), throwsArgumentError);
+    });
+  });
+}