Properly handle errors thrown in [Chain.capture]'s [onError] handler.

The Zone infrastructure apparently doesn't automatically pipe errors
to the parent Zone (issue 18134).

R=rnystrom@google.com
BUG=

Review URL: https://codereview.chromium.org//232123003

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/stack_trace@34904 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/pkgs/stack_trace/CHANGELOG.md b/pkgs/stack_trace/CHANGELOG.md
index 5e2f2c7..0b45208 100644
--- a/pkgs/stack_trace/CHANGELOG.md
+++ b/pkgs/stack_trace/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 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`.
diff --git a/pkgs/stack_trace/lib/src/stack_zone_specification.dart b/pkgs/stack_trace/lib/src/stack_zone_specification.dart
index 36e0717..9a4f7c0 100644
--- a/pkgs/stack_trace/lib/src/stack_zone_specification.dart
+++ b/pkgs/stack_trace/lib/src/stack_zone_specification.dart
@@ -145,10 +145,21 @@
   /// [_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, chainFor(stackTrace));
-    } else {
-      _onError(error, chainFor(stackTrace));
+      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);
+      }
     }
   }
 
diff --git a/pkgs/stack_trace/pubspec.yaml b/pkgs/stack_trace/pubspec.yaml
index 99abed7..d0cc30f 100644
--- a/pkgs/stack_trace/pubspec.yaml
+++ b/pkgs/stack_trace/pubspec.yaml
@@ -1,5 +1,5 @@
 name: stack_trace
-version: 0.9.3
+version: 0.9.3+1
 author: "Dart Team <misc@dartlang.org>"
 homepage: http://www.dartlang.org
 description: >
diff --git a/pkgs/stack_trace/test/chain_test.dart b/pkgs/stack_trace/test/chain_test.dart
index d9915eb..683544f 100644
--- a/pkgs/stack_trace/test/chain_test.dart
+++ b/pkgs/stack_trace/test/chain_test.dart
@@ -107,6 +107,29 @@
 
       return completer.future;
     });
+
+    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) {
+        expect(error, equals('error'));
+        expect(chain, new isInstanceOf<Chain>());
+        expect(chain.traces[1].frames,
+            contains(frameMember(startsWith('inMicrotask'))));
+        completer.complete();
+      });
+
+      return completer.future;
+    });
   });
 
   test('capture() without onError passes exceptions to parent zone', () {