Support Promise to Future for both DDC and dart2js.

APIs in the newer Chrome IDLs support more JS style promises. The Dart web libraries now hookup those promises and return a Dart Future.
Additionally, a new type maplike is exposed in the IDL this is exposed too.

Change-Id: I44175877eb95f4d910586d42c0139fb182483f82
Reviewed-on: https://dart-review.googlesource.com/49800
Commit-Queue: Terry Lucas <terry@google.com>
Reviewed-by: Stephen Adams <sra@google.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3cee860..440f2f4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,21 @@
 ## 2.0.0-dev.XX.0
+
+* Support Javascript Promise APIs as a Dart Future.  In Javascript a Promise has two
+  callbacks one for success and one for failure.  For success the Future returns the
+  value e.g.,
+
+BackgroundFetchManager.get is exposed as:
+
+```dart
+  Future<BackgroundFetchRegistration> get(String id)
+```
+
+usage could be:
+
+   BackgroundFetchRegistration result = await fetchMgr.get('abc');
+
+  The underlying JS Promise to Future mechanism will be exposed as a public API in a future checkin.
+
 (Add new changes here, and they will be copied to the
  change section for the next dev version)
 
diff --git a/sdk/lib/html/dart2js/html_dart2js.dart b/sdk/lib/html/dart2js/html_dart2js.dart
index 1f622c4..c440394 100644
--- a/sdk/lib/html/dart2js/html_dart2js.dart
+++ b/sdk/lib/html/dart2js/html_dart2js.dart
@@ -101,6 +101,46 @@
 HtmlDocument get document =>
     JS('returns:HtmlDocument;depends:none;effects:none;gvn:true', 'document');
 
+// Supoort to convert JS Promise to a Dart Future.
+Future<T> promiseToFuture<T>(thePromise) {
+  var completer = new Completer<T>();
+
+  var thenSuccessCode = (promiseValue) => completer.complete(promiseValue);
+  var thenErrorCode = (promiseError) => completer.completeError(promiseError);
+
+  JS("", "#.then(#, #)", thePromise, convertDartClosureToJS(thenSuccessCode, 1),
+      convertDartClosureToJS(thenErrorCode, 1));
+
+  return completer.future;
+}
+
+// Supoort to convert JS Promise to a Dart Future that returns a MapLike (Class with Map mixin).
+Future promiseToFutureMap(thePromise) {
+  var completer = new Completer();
+
+  var thenSuccessCode = (promiseValue) => completer.complete(promiseValue);
+  var thenErrorCode = (promiseError) => completer.completeError(promiseError);
+
+  JS("", "#.then(#, #)", thePromise, convertDartClosureToJS(thenSuccessCode, 1),
+      convertDartClosureToJS(thenErrorCode, 1));
+
+  return completer.future;
+}
+
+// Supoort to convert JS Promise to a Dart Future that returns a Dictionary as a Dart Map.
+Future<Map> promiseToFutureDictionary(thePromise) {
+  var completer = new Completer<Map>();
+
+  var thenSuccessCode = (promiseValue) =>
+      completer.complete(convertNativeToDart_Dictionary(promiseValue));
+  var thenErrorCode = (promiseError) => completer.completeError(promiseError);
+
+  JS("", "#.then(#, #)", thePromise, convertDartClosureToJS(thenSuccessCode, 1),
+      convertDartClosureToJS(thenErrorCode, 1));
+
+  return completer.future;
+}
+
 // Workaround for tags like <cite> that lack their own Element subclass --
 // Dart issue 1990.
 @Native("HTMLElement")
@@ -129,7 +169,6 @@
 @Experimental() // untriaged
 typedef void FontFaceSetForEachCallback(
     FontFace fontFace, FontFace fontFaceAgain, FontFaceSet set);
-
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
@@ -1810,12 +1849,15 @@
   @DomName('BackgroundFetchManager.get')
   @DocsEditable()
   @Experimental() // untriaged
-  Future get(String id) native;
+  Future<BackgroundFetchRegistration> get(String id) =>
+      promiseToFuture<BackgroundFetchRegistration>(
+          JS("", "#.get(#)", this, id));
 
   @DomName('BackgroundFetchManager.getIds')
   @DocsEditable()
   @Experimental() // untriaged
-  Future getIds() native;
+  Future<List<String>> getIds() =>
+      promiseToFuture<List<String>>(JS("", "#.getIds()", this));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -1869,7 +1911,7 @@
   @DomName('BackgroundFetchRegistration.abort')
   @DocsEditable()
   @Experimental() // untriaged
-  Future abort() native;
+  Future<bool> abort() => promiseToFuture<bool>(JS("", "#.abort()", this));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -1932,7 +1974,8 @@
   @DomName('BackgroundFetchedEvent.updateUI')
   @DocsEditable()
   @Experimental() // untriaged
-  Future updateUI(String title) native;
+  Future updateUI(String title) =>
+      promiseToFuture(JS("", "#.updateUI(#)", this, title));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -1978,7 +2021,9 @@
   @DomName('BarcodeDetector.detect')
   @DocsEditable()
   @Experimental() // untriaged
-  Future detect(/*ImageBitmapSource*/ image) native;
+  Future<List<DetectedBarcode>> detect(/*ImageBitmapSource*/ image) =>
+      promiseToFuture<List<DetectedBarcode>>(
+          JS("", "#.detect(#)", this, image));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -2090,7 +2135,7 @@
   @DomName('BeforeInstallPromptEvent.prompt')
   @DocsEditable()
   @Experimental() // untriaged
-  Future prompt() native;
+  Future prompt() => promiseToFuture(JS("", "#.prompt()", this));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -2231,12 +2276,13 @@
   @DomName('BluetoothRemoteGATTDescriptor.readValue')
   @DocsEditable()
   @Experimental() // untriaged
-  Future readValue() native;
+  Future readValue() => promiseToFuture(JS("", "#.readValue()", this));
 
   @DomName('BluetoothRemoteGATTDescriptor.writeValue')
   @DocsEditable()
   @Experimental() // untriaged
-  Future writeValue(/*BufferSource*/ value) native;
+  Future writeValue(/*BufferSource*/ value) =>
+      promiseToFuture(JS("", "#.writeValue(#)", this, value));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -2260,27 +2306,28 @@
   @DomName('Body.arrayBuffer')
   @DocsEditable()
   @Experimental() // untriaged
-  Future arrayBuffer() native;
+  Future arrayBuffer() => promiseToFuture(JS("", "#.arrayBuffer()", this));
 
   @DomName('Body.blob')
   @DocsEditable()
   @Experimental() // untriaged
-  Future blob() native;
+  Future<Blob> blob() => promiseToFuture<Blob>(JS("", "#.blob()", this));
 
   @DomName('Body.formData')
   @DocsEditable()
   @Experimental() // untriaged
-  Future formData() native;
+  Future<FormData> formData() =>
+      promiseToFuture<FormData>(JS("", "#.formData()", this));
 
   @DomName('Body.json')
   @DocsEditable()
   @Experimental() // untriaged
-  Future json() native;
+  Future json() => promiseToFuture(JS("", "#.json()", this));
 
   @DomName('Body.text')
   @DocsEditable()
   @Experimental() // untriaged
-  Future text() native;
+  Future<String> text() => promiseToFuture<String>(JS("", "#.text()", this));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -2719,17 +2766,19 @@
   @DomName('CacheStorage.delete')
   @DocsEditable()
   @Experimental() // untriaged
-  Future delete(String cacheName) native;
+  Future delete(String cacheName) =>
+      promiseToFuture(JS("", "#.delete(#)", this, cacheName));
 
   @DomName('CacheStorage.has')
   @DocsEditable()
   @Experimental() // untriaged
-  Future has(String cacheName) native;
+  Future has(String cacheName) =>
+      promiseToFuture(JS("", "#.has(#)", this, cacheName));
 
   @DomName('CacheStorage.keys')
   @DocsEditable()
   @Experimental() // untriaged
-  Future keys() native;
+  Future keys() => promiseToFuture(JS("", "#.keys()", this));
 
   @DomName('CacheStorage.match')
   @DocsEditable()
@@ -2756,7 +2805,8 @@
   @DomName('CacheStorage.open')
   @DocsEditable()
   @Experimental() // untriaged
-  Future open(String cacheName) native;
+  Future open(String cacheName) =>
+      promiseToFuture(JS("", "#.open(#)", this, cacheName));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -4031,12 +4081,12 @@
   @DomName('Clients.claim')
   @DocsEditable()
   @Experimental() // untriaged
-  Future claim() native;
+  Future claim() => promiseToFuture(JS("", "#.claim()", this));
 
   @DomName('Clients.get')
   @DocsEditable()
   @Experimental() // untriaged
-  Future get(String id) native;
+  Future get(String id) => promiseToFuture(JS("", "#.get(#)", this, id));
 
   @DomName('Clients.matchAll')
   @DocsEditable()
@@ -4063,7 +4113,8 @@
   @DomName('Clients.openWindow')
   @DocsEditable()
   @Experimental() // untriaged
-  Future openWindow(String url) native;
+  Future<WindowClient> openWindow(String url) =>
+      promiseToFuture<WindowClient>(JS("", "#.openWindow(#)", this, url));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -4457,17 +4508,20 @@
   @DomName('CredentialsContainer.preventSilentAccess')
   @DocsEditable()
   @Experimental() // untriaged
-  Future preventSilentAccess() native;
+  Future preventSilentAccess() =>
+      promiseToFuture(JS("", "#.preventSilentAccess()", this));
 
   @DomName('CredentialsContainer.requireUserMediation')
   @DocsEditable()
   @Experimental() // untriaged
-  Future requireUserMediation() native;
+  Future requireUserMediation() =>
+      promiseToFuture(JS("", "#.requireUserMediation()", this));
 
   @DomName('CredentialsContainer.store')
   @DocsEditable()
   @Experimental() // untriaged
-  Future store(Credential credential) native;
+  Future store(Credential credential) =>
+      promiseToFuture(JS("", "#.store(#)", this, credential));
 }
 // 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
@@ -10270,7 +10324,8 @@
   @DomName('CustomElementRegistry.whenDefined')
   @DocsEditable()
   @Experimental() // untriaged
-  Future whenDefined(String name) native;
+  Future whenDefined(String name) =>
+      promiseToFuture(JS("", "#.whenDefined(#)", this, name));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -19259,7 +19314,8 @@
   @DomName('FaceDetector.detect')
   @DocsEditable()
   @Experimental() // untriaged
-  Future detect(/*ImageBitmapSource*/ image) native;
+  Future<List<DetectedFace>> detect(/*ImageBitmapSource*/ image) =>
+      promiseToFuture<List<DetectedFace>>(JS("", "#.detect(#)", this, image));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -20099,7 +20155,8 @@
   @DomName('FontFace.load')
   @DocsEditable()
   @Experimental() // untriaged
-  Future load() native;
+  Future<FontFace> load() =>
+      promiseToFuture<FontFace>(JS("", "#.load()", this));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -23312,17 +23369,21 @@
   @DomName('ImageCapture.getPhotoCapabilities')
   @DocsEditable()
   @Experimental() // untriaged
-  Future getPhotoCapabilities() native;
+  Future<PhotoCapabilities> getPhotoCapabilities() =>
+      promiseToFuture<PhotoCapabilities>(
+          JS("", "#.getPhotoCapabilities()", this));
 
   @DomName('ImageCapture.getPhotoSettings')
   @DocsEditable()
   @Experimental() // untriaged
-  Future getPhotoSettings() native;
+  Future<Map> getPhotoSettings() =>
+      promiseToFutureDictionary(JS("", "#.getPhotoSettings()", this));
 
   @DomName('ImageCapture.grabFrame')
   @DocsEditable()
   @Experimental() // untriaged
-  Future grabFrame() native;
+  Future<ImageBitmap> grabFrame() =>
+      promiseToFuture<ImageBitmap>(JS("", "#.grabFrame()", this));
 
   @DomName('ImageCapture.setOptions')
   @DocsEditable()
@@ -23504,7 +23565,7 @@
   @DomName('HTMLImageElement.decode')
   @DocsEditable()
   @Experimental() // untriaged
-  Future decode() native;
+  Future decode() => promiseToFuture(JS("", "#.decode()", this));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -25360,7 +25421,9 @@
   @DomName('MediaDevices.enumerateDevices')
   @DocsEditable()
   @Experimental() // untriaged
-  Future enumerateDevices() native;
+  Future<List<MediaDeviceInfo>> enumerateDevices() =>
+      promiseToFuture<List<MediaDeviceInfo>>(
+          JS("", "#.enumerateDevices()", this));
 
   @DomName('MediaDevices.getSupportedConstraints')
   @DocsEditable()
@@ -25615,17 +25678,19 @@
 
   @DomName('HTMLMediaElement.play')
   @DocsEditable()
-  Future play() native;
+  Future play() => promiseToFuture(JS("", "#.play()", this));
 
   @DomName('HTMLMediaElement.setMediaKeys')
   @DocsEditable()
   @Experimental() // untriaged
-  Future setMediaKeys(MediaKeys mediaKeys) native;
+  Future setMediaKeys(MediaKeys mediaKeys) =>
+      promiseToFuture(JS("", "#.setMediaKeys(#)", this, mediaKeys));
 
   @DomName('HTMLMediaElement.setSinkId')
   @DocsEditable()
   @Experimental() // untriaged
-  Future setSinkId(String sinkId) native;
+  Future setSinkId(String sinkId) =>
+      promiseToFuture(JS("", "#.setSinkId(#)", this, sinkId));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -25785,22 +25850,25 @@
 
   @DomName('MediaKeySession.close')
   @DocsEditable()
-  Future close() native;
+  Future close() => promiseToFuture(JS("", "#.close()", this));
 
   @DomName('MediaKeySession.generateRequest')
   @DocsEditable()
   @Experimental() // untriaged
-  Future generateRequest(String initDataType, /*BufferSource*/ initData) native;
+  Future generateRequest(String initDataType, /*BufferSource*/ initData) =>
+      promiseToFuture(
+          JS("", "#.generateRequest(#, #)", this, initDataType, initData));
 
   @DomName('MediaKeySession.load')
   @DocsEditable()
   @Experimental() // untriaged
-  Future load(String sessionId) native;
+  Future load(String sessionId) =>
+      promiseToFuture(JS("", "#.load(#)", this, sessionId));
 
   @DomName('MediaKeySession.remove')
   @DocsEditable()
   @Experimental() // untriaged
-  Future remove() native;
+  Future remove() => promiseToFuture(JS("", "#.remove()", this));
 
   @JSName('update')
   @DomName('MediaKeySession.update')
@@ -25863,7 +25931,8 @@
   @DomName('MediaKeySystemAccess.createMediaKeys')
   @DocsEditable()
   @Experimental() // untriaged
-  Future createMediaKeys() native;
+  Future createMediaKeys() =>
+      promiseToFuture(JS("", "#.createMediaKeys()", this));
 
   @DomName('MediaKeySystemAccess.getConfiguration')
   @DocsEditable()
@@ -25901,12 +25970,15 @@
   @DomName('MediaKeys.getStatusForPolicy')
   @DocsEditable()
   @Experimental() // untriaged
-  Future getStatusForPolicy(MediaKeysPolicy policy) native;
+  Future getStatusForPolicy(MediaKeysPolicy policy) =>
+      promiseToFuture(JS("", "#.getStatusForPolicy(#)", this, policy));
 
   @DomName('MediaKeys.setServerCertificate')
   @DocsEditable()
   @Experimental() // untriaged
-  Future setServerCertificate(/*BufferSource*/ serverCertificate) native;
+  Future setServerCertificate(/*BufferSource*/ serverCertificate) =>
+      promiseToFuture(
+          JS("", "#.setServerCertificate(#)", this, serverCertificate));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -27241,11 +27313,68 @@
 @DomName('MIDIInputMap')
 @Experimental() // untriaged
 @Native("MIDIInputMap")
-class MidiInputMap extends Interceptor {
+class MidiInputMap extends Interceptor with MapMixin<String, dynamic> {
   // To suppress missing implicit constructor warnings.
   factory MidiInputMap._() {
     throw new UnsupportedError("Not supported");
   }
+
+  Map _getItem(String key) =>
+      convertNativeToDart_Dictionary(JS('', '#.get(#)', this, key));
+
+  void addAll(Map<String, dynamic> other) {
+    throw new UnsupportedError("Not supported");
+  }
+
+  bool containsValue(dynamic value) => values.any((e) => e == value);
+
+  bool containsKey(dynamic key) => _getItem(key) != null;
+
+  Map operator [](dynamic key) => _getItem(key);
+
+  void forEach(void f(String key, dynamic value)) {
+    var entries = JS('', '#.entries()', this);
+    while (true) {
+      var entry = JS('', '#.next()', entries);
+      if (JS('bool', '#.done', entry)) return;
+      f(JS('String', '#.value[0]', entry),
+          convertNativeToDart_Dictionary(JS('', '#.value[1]', entry)));
+    }
+  }
+
+  Iterable<String> get keys {
+    final keys = <String>[];
+    forEach((k, v) => keys.add(k));
+    return keys;
+  }
+
+  Iterable<Map> get values {
+    final values = <Map>[];
+    forEach((k, v) => values.add(v));
+    return values;
+  }
+
+  int get length => JS('int', '#.size', this);
+
+  bool get isEmpty => length == 0;
+
+  bool get isNotEmpty => !isEmpty;
+
+  void operator []=(String key, dynamic value) {
+    throw new UnsupportedError("Not supported");
+  }
+
+  dynamic putIfAbsent(String key, dynamic ifAbsent()) {
+    throw new UnsupportedError("Not supported");
+  }
+
+  String remove(dynamic key) {
+    throw new UnsupportedError("Not supported");
+  }
+
+  void clear() {
+    throw new UnsupportedError("Not supported");
+  }
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -27307,11 +27436,68 @@
 @DomName('MIDIOutputMap')
 @Experimental() // untriaged
 @Native("MIDIOutputMap")
-class MidiOutputMap extends Interceptor {
+class MidiOutputMap extends Interceptor with MapMixin<String, dynamic> {
   // To suppress missing implicit constructor warnings.
   factory MidiOutputMap._() {
     throw new UnsupportedError("Not supported");
   }
+
+  Map _getItem(String key) =>
+      convertNativeToDart_Dictionary(JS('', '#.get(#)', this, key));
+
+  void addAll(Map<String, dynamic> other) {
+    throw new UnsupportedError("Not supported");
+  }
+
+  bool containsValue(dynamic value) => values.any((e) => e == value);
+
+  bool containsKey(dynamic key) => _getItem(key) != null;
+
+  Map operator [](dynamic key) => _getItem(key);
+
+  void forEach(void f(String key, dynamic value)) {
+    var entries = JS('', '#.entries()', this);
+    while (true) {
+      var entry = JS('', '#.next()', entries);
+      if (JS('bool', '#.done', entry)) return;
+      f(JS('String', '#.value[0]', entry),
+          convertNativeToDart_Dictionary(JS('', '#.value[1]', entry)));
+    }
+  }
+
+  Iterable<String> get keys {
+    final keys = <String>[];
+    forEach((k, v) => keys.add(k));
+    return keys;
+  }
+
+  Iterable<Map> get values {
+    final values = <Map>[];
+    forEach((k, v) => values.add(v));
+    return values;
+  }
+
+  int get length => JS('int', '#.size', this);
+
+  bool get isEmpty => length == 0;
+
+  bool get isNotEmpty => !isEmpty;
+
+  void operator []=(String key, dynamic value) {
+    throw new UnsupportedError("Not supported");
+  }
+
+  dynamic putIfAbsent(String key, dynamic ifAbsent()) {
+    throw new UnsupportedError("Not supported");
+  }
+
+  String remove(dynamic key) {
+    throw new UnsupportedError("Not supported");
+  }
+
+  void clear() {
+    throw new UnsupportedError("Not supported");
+  }
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -27361,12 +27547,12 @@
   @DomName('MIDIPort.close')
   @DocsEditable()
   @Experimental() // untriaged
-  Future close() native;
+  Future close() => promiseToFuture(JS("", "#.close()", this));
 
   @DomName('MIDIPort.open')
   @DocsEditable()
   @Experimental() // untriaged
-  Future open() native;
+  Future open() => promiseToFuture(JS("", "#.open()", this));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -28057,17 +28243,18 @@
   @DomName('NavigationPreloadManager.disable')
   @DocsEditable()
   @Experimental() // untriaged
-  Future disable() native;
+  Future disable() => promiseToFuture(JS("", "#.disable()", this));
 
   @DomName('NavigationPreloadManager.enable')
   @DocsEditable()
   @Experimental() // untriaged
-  Future enable() native;
+  Future enable() => promiseToFuture(JS("", "#.enable()", this));
 
   @DomName('NavigationPreloadManager.getState')
   @DocsEditable()
   @Experimental() // untriaged
-  Future getState() native;
+  Future<Map> getState() =>
+      promiseToFutureDictionary(JS("", "#.getState()", this));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -28307,7 +28494,7 @@
   @DomName('Navigator.getBattery')
   @DocsEditable()
   @Experimental() // untriaged
-  Future getBattery() native;
+  Future getBattery() => promiseToFuture(JS("", "#.getBattery()", this));
 
   @JSName('getGamepads')
   @DomName('Navigator.getGamepads')
@@ -28320,12 +28507,14 @@
   @DomName('Navigator.getInstalledRelatedApps')
   @DocsEditable()
   @Experimental() // untriaged
-  Future getInstalledRelatedApps() native;
+  Future<RelatedApplication> getInstalledRelatedApps() =>
+      promiseToFuture<RelatedApplication>(
+          JS("", "#.getInstalledRelatedApps()", this));
 
   @DomName('Navigator.getVRDisplays')
   @DocsEditable()
   @Experimental() // untriaged
-  Future getVRDisplays() native;
+  Future getVRDisplays() => promiseToFuture(JS("", "#.getVRDisplays()", this));
 
   @DomName('Navigator.registerProtocolHandler')
   @DocsEditable()
@@ -28380,7 +28569,9 @@
   @DocsEditable()
   @Experimental() // untriaged
   Future requestMediaKeySystemAccess(
-      String keySystem, List<Map> supportedConfigurations) native;
+          String keySystem, List<Map> supportedConfigurations) =>
+      promiseToFuture(JS("", "#.requestMediaKeySystemAccess(#, #)", this,
+          keySystem, supportedConfigurations));
 
   @DomName('Navigator.sendBeacon')
   @DocsEditable()
@@ -30139,7 +30330,7 @@
   @DomName('OffscreenCanvasRenderingContext2D.commit')
   @DocsEditable()
   @Experimental() // untriaged
-  Future commit() native;
+  Future commit() => promiseToFuture(JS("", "#.commit()", this));
 
   @DomName('OffscreenCanvasRenderingContext2D.createImageData')
   @DocsEditable()
@@ -31380,27 +31571,31 @@
   @DomName('PaymentInstruments.clear')
   @DocsEditable()
   @Experimental() // untriaged
-  Future clear() native;
+  Future clear() => promiseToFuture(JS("", "#.clear()", this));
 
   @DomName('PaymentInstruments.delete')
   @DocsEditable()
   @Experimental() // untriaged
-  Future delete(String instrumentKey) native;
+  Future<bool> delete(String instrumentKey) =>
+      promiseToFuture<bool>(JS("", "#.delete(#)", this, instrumentKey));
 
   @DomName('PaymentInstruments.get')
   @DocsEditable()
   @Experimental() // untriaged
-  Future get(String instrumentKey) native;
+  Future<Map> get(String instrumentKey) =>
+      promiseToFutureDictionary(JS("", "#.get(#)", this, instrumentKey));
 
   @DomName('PaymentInstruments.has')
   @DocsEditable()
   @Experimental() // untriaged
-  Future has(String instrumentKey) native;
+  Future has(String instrumentKey) =>
+      promiseToFuture(JS("", "#.has(#)", this, instrumentKey));
 
   @DomName('PaymentInstruments.keys')
   @DocsEditable()
   @Experimental() // untriaged
-  Future keys() native;
+  Future<List<String>> keys() =>
+      promiseToFuture<List<String>>(JS("", "#.keys()", this));
 
   @DomName('PaymentInstruments.set')
   @DocsEditable()
@@ -31500,17 +31695,19 @@
   @DomName('PaymentRequest.abort')
   @DocsEditable()
   @Experimental() // untriaged
-  Future abort() native;
+  Future abort() => promiseToFuture(JS("", "#.abort()", this));
 
   @DomName('PaymentRequest.canMakePayment')
   @DocsEditable()
   @Experimental() // untriaged
-  Future canMakePayment() native;
+  Future<bool> canMakePayment() =>
+      promiseToFuture<bool>(JS("", "#.canMakePayment()", this));
 
   @DomName('PaymentRequest.show')
   @DocsEditable()
   @Experimental() // untriaged
-  Future show() native;
+  Future<PaymentResponse> show() =>
+      promiseToFuture<PaymentResponse>(JS("", "#.show()", this));
 }
 
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
@@ -31577,7 +31774,8 @@
   @DomName('PaymentRequestEvent.openWindow')
   @DocsEditable()
   @Experimental() // untriaged
-  Future openWindow(String url) native;
+  Future<WindowClient> openWindow(String url) =>
+      promiseToFuture<WindowClient>(JS("", "#.openWindow(#)", this, url));
 
   @DomName('PaymentRequestEvent.respondWith')
   @DocsEditable()
@@ -31677,7 +31875,8 @@
   @DomName('PaymentResponse.complete')
   @DocsEditable()
   @Experimental() // untriaged
-  Future complete([String paymentResult]) native;
+  Future complete([String paymentResult]) =>
+      promiseToFuture(JS("", "#.complete(#)", this, paymentResult));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -32347,7 +32546,9 @@
   @DomName('Permissions.requestAll')
   @DocsEditable()
   @Experimental() // untriaged
-  Future requestAll(List<Map> permissions) native;
+  Future<PermissionStatus> requestAll(List<Map> permissions) =>
+      promiseToFuture<PermissionStatus>(
+          JS("", "#.requestAll(#)", this, permissions));
 
   @DomName('Permissions.revoke')
   @DocsEditable()
@@ -32984,17 +33185,22 @@
   @DomName('PresentationRequest.getAvailability')
   @DocsEditable()
   @Experimental() // untriaged
-  Future getAvailability() native;
+  Future<PresentationAvailability> getAvailability() =>
+      promiseToFuture<PresentationAvailability>(
+          JS("", "#.getAvailability()", this));
 
   @DomName('PresentationRequest.reconnect')
   @DocsEditable()
   @Experimental() // untriaged
-  Future reconnect(String id) native;
+  Future<PresentationConnection> reconnect(String id) =>
+      promiseToFuture<PresentationConnection>(
+          JS("", "#.reconnect(#)", this, id));
 
   @DomName('PresentationRequest.start')
   @DocsEditable()
   @Experimental() // untriaged
-  Future start() native;
+  Future<PresentationConnection> start() =>
+      promiseToFuture<PresentationConnection>(JS("", "#.start()", this));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -33221,7 +33427,8 @@
   @DomName('PushManager.getSubscription')
   @DocsEditable()
   @Experimental() // untriaged
-  Future getSubscription() native;
+  Future<PushSubscription> getSubscription() =>
+      promiseToFuture<PushSubscription>(JS("", "#.getSubscription()", this));
 
   @DomName('PushManager.permissionState')
   @DocsEditable()
@@ -33338,7 +33545,8 @@
   @DomName('PushSubscription.unsubscribe')
   @DocsEditable()
   @Experimental() // untriaged
-  Future unsubscribe() native;
+  Future<bool> unsubscribe() =>
+      promiseToFuture<bool>(JS("", "#.unsubscribe()", this));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -33687,17 +33895,19 @@
   @DomName('RemotePlayback.cancelWatchAvailability')
   @DocsEditable()
   @Experimental() // untriaged
-  Future cancelWatchAvailability([int id]) native;
+  Future cancelWatchAvailability([int id]) =>
+      promiseToFuture(JS("", "#.cancelWatchAvailability(#)", this, id));
 
   @DomName('RemotePlayback.prompt')
   @DocsEditable()
   @Experimental() // untriaged
-  Future prompt() native;
+  Future prompt() => promiseToFuture(JS("", "#.prompt()", this));
 
   @DomName('RemotePlayback.watchAvailability')
   @DocsEditable()
   @Experimental() // untriaged
-  Future watchAvailability(RemotePlaybackAvailabilityCallback callback) native;
+  Future<int> watchAvailability(RemotePlaybackAvailabilityCallback callback) =>
+      promiseToFuture<int>(JS("", "#.watchAvailability(#)", this, callback));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -34301,15 +34511,6 @@
     return completer.future;
   }
 
-  @DomName('RTCPeerConnection.getStats')
-  Future<RtcStatsResponse> getStats(MediaStreamTrack selector) {
-    var completer = new Completer<RtcStatsResponse>();
-    _getStats((value) {
-      completer.complete(value);
-    }, selector);
-    return completer.future;
-  }
-
   @DomName('RTCPeerConnection.generateCertificate')
   @DocsEditable()
   @Experimental() // untriaged
@@ -34422,8 +34623,10 @@
   @DomName('RTCPeerConnection.addIceCandidate')
   @DocsEditable()
   Future addIceCandidate(Object candidate,
-      [VoidCallback successCallback,
-      RtcPeerConnectionErrorCallback failureCallback]) native;
+          [VoidCallback successCallback,
+          RtcPeerConnectionErrorCallback failureCallback]) =>
+      promiseToFuture(JS("", "#.addIceCandidate(#, #, #)", this, candidate,
+          successCallback, failureCallback));
 
   @DomName('RTCPeerConnection.addStream')
   @DocsEditable()
@@ -34601,11 +34804,9 @@
   @Experimental() // untriaged
   List<RtcRtpSender> getSenders() native;
 
-  @JSName('getStats')
   @DomName('RTCPeerConnection.getStats')
   @DocsEditable()
-  Future _getStats(
-      [RtcStatsCallback successCallback, MediaStreamTrack selector]) native;
+  Future getStats() => promiseToFutureMap(JS("", "#.getStats()", this));
 
   @DomName('RTCPeerConnection.removeStream')
   @DocsEditable()
@@ -34876,11 +35077,68 @@
 // http://dev.w3.org/2011/webrtc/editor/webrtc.html#idl-def-RTCStatsReport
 @Experimental()
 @Native("RTCStatsReport")
-class RtcStatsReport extends Interceptor {
+class RtcStatsReport extends Interceptor with MapMixin<String, dynamic> {
   // To suppress missing implicit constructor warnings.
   factory RtcStatsReport._() {
     throw new UnsupportedError("Not supported");
   }
+
+  Map _getItem(String key) =>
+      convertNativeToDart_Dictionary(JS('', '#.get(#)', this, key));
+
+  void addAll(Map<String, dynamic> other) {
+    throw new UnsupportedError("Not supported");
+  }
+
+  bool containsValue(dynamic value) => values.any((e) => e == value);
+
+  bool containsKey(dynamic key) => _getItem(key) != null;
+
+  Map operator [](dynamic key) => _getItem(key);
+
+  void forEach(void f(String key, dynamic value)) {
+    var entries = JS('', '#.entries()', this);
+    while (true) {
+      var entry = JS('', '#.next()', entries);
+      if (JS('bool', '#.done', entry)) return;
+      f(JS('String', '#.value[0]', entry),
+          convertNativeToDart_Dictionary(JS('', '#.value[1]', entry)));
+    }
+  }
+
+  Iterable<String> get keys {
+    final keys = <String>[];
+    forEach((k, v) => keys.add(k));
+    return keys;
+  }
+
+  Iterable<Map> get values {
+    final values = <Map>[];
+    forEach((k, v) => values.add(v));
+    return values;
+  }
+
+  int get length => JS('int', '#.size', this);
+
+  bool get isEmpty => length == 0;
+
+  bool get isNotEmpty => !isEmpty;
+
+  void operator []=(String key, dynamic value) {
+    throw new UnsupportedError("Not supported");
+  }
+
+  dynamic putIfAbsent(String key, dynamic ifAbsent()) {
+    throw new UnsupportedError("Not supported");
+  }
+
+  String remove(dynamic key) {
+    throw new UnsupportedError("Not supported");
+  }
+
+  void clear() {
+    throw new UnsupportedError("Not supported");
+  }
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -35043,7 +35301,8 @@
   @DomName('ScreenOrientation.lock')
   @DocsEditable()
   @Experimental() // untriaged
-  Future lock(String orientation) native;
+  Future lock(String orientation) =>
+      promiseToFuture(JS("", "#.lock(#)", this, orientation));
 
   @DomName('ScreenOrientation.unlock')
   @DocsEditable()
@@ -35771,12 +36030,16 @@
   @DomName('ServiceWorkerContainer.getRegistration')
   @DocsEditable()
   @Experimental() // untriaged
-  Future getRegistration([String documentURL]) native;
+  Future<ServiceWorkerRegistration> getRegistration([String documentURL]) =>
+      promiseToFuture<ServiceWorkerRegistration>(
+          JS("", "#.getRegistration(#)", this, documentURL));
 
   @DomName('ServiceWorkerContainer.getRegistrations')
   @DocsEditable()
   @Experimental() // untriaged
-  Future getRegistrations() native;
+  Future<List<ServiceWorkerRegistration>> getRegistrations() =>
+      promiseToFuture<List<ServiceWorkerRegistration>>(
+          JS("", "#.getRegistrations()", this));
 
   @DomName('ServiceWorkerContainer.register')
   @DocsEditable()
@@ -35862,7 +36125,7 @@
   @DomName('ServiceWorkerGlobalScope.skipWaiting')
   @DocsEditable()
   @Experimental() // untriaged
-  Future skipWaiting() native;
+  Future skipWaiting() => promiseToFuture(JS("", "#.skipWaiting()", this));
 
   @DomName('ServiceWorkerGlobalScope.onactivate')
   @DocsEditable()
@@ -35996,12 +36259,13 @@
   @DomName('ServiceWorkerRegistration.unregister')
   @DocsEditable()
   @Experimental() // untriaged
-  Future unregister() native;
+  Future<bool> unregister() =>
+      promiseToFuture<bool>(JS("", "#.unregister()", this));
 
   @DomName('ServiceWorkerRegistration.update')
   @DocsEditable()
   @Experimental() // untriaged
-  Future update() native;
+  Future update() => promiseToFuture(JS("", "#.update()", this));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -37639,17 +37903,19 @@
   @DomName('StorageManager.estimate')
   @DocsEditable()
   @Experimental() // untriaged
-  Future estimate() native;
+  Future<Map> estimate() =>
+      promiseToFutureDictionary(JS("", "#.estimate()", this));
 
   @DomName('StorageManager.persist')
   @DocsEditable()
   @Experimental() // untriaged
-  Future persist() native;
+  Future<bool> persist() => promiseToFuture<bool>(JS("", "#.persist()", this));
 
   @DomName('StorageManager.persisted')
   @DocsEditable()
   @Experimental() // untriaged
-  Future persisted() native;
+  Future<bool> persisted() =>
+      promiseToFuture<bool>(JS("", "#.persisted()", this));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -37892,12 +38158,14 @@
   @DomName('SyncManager.getTags')
   @DocsEditable()
   @Experimental() // untriaged
-  Future getTags() native;
+  Future<List<String>> getTags() =>
+      promiseToFuture<List<String>>(JS("", "#.getTags()", this));
 
   @DomName('SyncManager.register')
   @DocsEditable()
   @Experimental() // untriaged
-  Future register(String tag) native;
+  Future register(String tag) =>
+      promiseToFuture(JS("", "#.register(#)", this, tag));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -38591,7 +38859,8 @@
   @DomName('TextDetector.detect')
   @DocsEditable()
   @Experimental() // untriaged
-  Future detect(/*ImageBitmapSource*/ image) native;
+  Future<List<DetectedText>> detect(/*ImageBitmapSource*/ image) =>
+      promiseToFuture<List<DetectedText>>(JS("", "#.detect(#)", this, image));
 }
 // 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
@@ -39863,7 +40132,8 @@
   @DomName('UnderlyingSourceBase.cancel')
   @DocsEditable()
   @Experimental() // untriaged
-  Future cancel(Object reason) native;
+  Future cancel(Object reason) =>
+      promiseToFuture(JS("", "#.cancel(#)", this, reason));
 
   @DomName('UnderlyingSourceBase.notifyLockAcquired')
   @DocsEditable()
@@ -39878,12 +40148,13 @@
   @DomName('UnderlyingSourceBase.pull')
   @DocsEditable()
   @Experimental() // untriaged
-  Future pull() native;
+  Future pull() => promiseToFuture(JS("", "#.pull()", this));
 
   @DomName('UnderlyingSourceBase.start')
   @DocsEditable()
   @Experimental() // untriaged
-  Future start(Object stream) native;
+  Future start(Object stream) =>
+      promiseToFuture(JS("", "#.start(#)", this, stream));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -40107,7 +40378,7 @@
   @DomName('VR.getDevices')
   @DocsEditable()
   @Experimental() // untriaged
-  Future getDevices() native;
+  Future getDevices() => promiseToFuture(JS("", "#.getDevices()", this));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -40281,7 +40552,7 @@
   @DomName('VRDisplay.exitPresent')
   @DocsEditable()
   @Experimental() // untriaged
-  Future exitPresent() native;
+  Future exitPresent() => promiseToFuture(JS("", "#.exitPresent()", this));
 
   @DomName('VRDisplay.getEyeParameters')
   @DocsEditable()
@@ -40306,7 +40577,8 @@
   @DomName('VRDisplay.requestPresent')
   @DocsEditable()
   @Experimental() // untriaged
-  Future requestPresent(List<Map> layers) native;
+  Future requestPresent(List<Map> layers) =>
+      promiseToFuture(JS("", "#.requestPresent(#)", this, layers));
 
   @DomName('VRDisplay.submitFrame')
   @DocsEditable()
@@ -40577,7 +40849,7 @@
   @DomName('VRSession.end')
   @DocsEditable()
   @Experimental() // untriaged
-  Future end() native;
+  Future end() => promiseToFuture(JS("", "#.end()", this));
 
   @DomName('VRSession.requestFrameOfReference')
   @DocsEditable()
@@ -43905,12 +44177,14 @@
   @DomName('WindowClient.focus')
   @DocsEditable()
   @Experimental() // untriaged
-  Future focus() native;
+  Future<WindowClient> focus() =>
+      promiseToFuture<WindowClient>(JS("", "#.focus()", this));
 
   @DomName('WindowClient.navigate')
   @DocsEditable()
   @Experimental() // untriaged
-  Future navigate(String url) native;
+  Future<WindowClient> navigate(String url) =>
+      promiseToFuture<WindowClient>(JS("", "#.navigate(#)", this, url));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -44787,17 +45061,20 @@
   @DomName('BudgetService.getBudget')
   @DocsEditable()
   @Experimental() // untriaged
-  Future getBudget() native;
+  Future<BudgetState> getBudget() =>
+      promiseToFuture<BudgetState>(JS("", "#.getBudget()", this));
 
   @DomName('BudgetService.getCost')
   @DocsEditable()
   @Experimental() // untriaged
-  Future getCost(String operation) native;
+  Future<double> getCost(String operation) =>
+      promiseToFuture<double>(JS("", "#.getCost(#)", this, operation));
 
   @DomName('BudgetService.reserve')
   @DocsEditable()
   @Experimental() // untriaged
-  Future reserve(String operation) native;
+  Future<bool> reserve(String operation) =>
+      promiseToFuture<bool>(JS("", "#.reserve(#)", this, operation));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -44842,22 +45119,26 @@
   @DomName('Clipboard.read')
   @DocsEditable()
   @Experimental() // untriaged
-  Future read() native;
+  Future<DataTransfer> read() =>
+      promiseToFuture<DataTransfer>(JS("", "#.read()", this));
 
   @DomName('Clipboard.readText')
   @DocsEditable()
   @Experimental() // untriaged
-  Future readText() native;
+  Future<String> readText() =>
+      promiseToFuture<String>(JS("", "#.readText()", this));
 
   @DomName('Clipboard.write')
   @DocsEditable()
   @Experimental() // untriaged
-  Future write(DataTransfer data) native;
+  Future write(DataTransfer data) =>
+      promiseToFuture(JS("", "#.write(#)", this, data));
 
   @DomName('Clipboard.writeText')
   @DocsEditable()
   @Experimental() // untriaged
-  Future writeText(String data) native;
+  Future writeText(String data) =>
+      promiseToFuture(JS("", "#.writeText(#)", this, data));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
diff --git a/sdk/lib/svg/dart2js/svg_dart2js.dart b/sdk/lib/svg/dart2js/svg_dart2js.dart
index 16ba803..23c0a70 100644
--- a/sdk/lib/svg/dart2js/svg_dart2js.dart
+++ b/sdk/lib/svg/dart2js/svg_dart2js.dart
@@ -2683,7 +2683,7 @@
   @DomName('SVGImageElement.decode')
   @DocsEditable()
   @Experimental() // untriaged
-  Future decode() native;
+  Future decode() => promiseToFuture(JS("", "#.decode()", this));
 
   // From SVGURIReference
 
diff --git a/sdk/lib/web_audio/dart2js/web_audio_dart2js.dart b/sdk/lib/web_audio/dart2js/web_audio_dart2js.dart
index c927559..c12fe76 100644
--- a/sdk/lib/web_audio/dart2js/web_audio_dart2js.dart
+++ b/sdk/lib/web_audio/dart2js/web_audio_dart2js.dart
@@ -240,7 +240,7 @@
   @DomName('AudioContext.close')
   @DocsEditable()
   @Experimental() // untriaged
-  Future close() native;
+  Future close() => promiseToFuture(JS("", "#.close()", this));
 
   @DomName('AudioContext.getOutputTimestamp')
   @DocsEditable()
@@ -258,7 +258,7 @@
   @DomName('AudioContext.suspend')
   @DocsEditable()
   @Experimental() // untriaged
-  Future suspend() native;
+  Future suspend() => promiseToFuture(JS("", "#.suspend()", this));
 
   factory AudioContext() => JS('AudioContext',
       'new (window.AudioContext || window.webkitAudioContext)()');
@@ -534,11 +534,68 @@
 @DomName('AudioParamMap')
 @Experimental() // untriaged
 @Native("AudioParamMap")
-class AudioParamMap extends Interceptor {
+class AudioParamMap extends Interceptor with MapMixin<String, dynamic> {
   // To suppress missing implicit constructor warnings.
   factory AudioParamMap._() {
     throw new UnsupportedError("Not supported");
   }
+
+  Map _getItem(String key) =>
+      convertNativeToDart_Dictionary(JS('', '#.get(#)', this, key));
+
+  void addAll(Map<String, dynamic> other) {
+    throw new UnsupportedError("Not supported");
+  }
+
+  bool containsValue(dynamic value) => values.any((e) => e == value);
+
+  bool containsKey(dynamic key) => _getItem(key) != null;
+
+  Map operator [](dynamic key) => _getItem(key);
+
+  void forEach(void f(String key, dynamic value)) {
+    var entries = JS('', '#.entries()', this);
+    while (true) {
+      var entry = JS('', '#.next()', entries);
+      if (JS('bool', '#.done', entry)) return;
+      f(JS('String', '#.value[0]', entry),
+          convertNativeToDart_Dictionary(JS('', '#.value[1]', entry)));
+    }
+  }
+
+  Iterable<String> get keys {
+    final keys = <String>[];
+    forEach((k, v) => keys.add(k));
+    return keys;
+  }
+
+  Iterable<Map> get values {
+    final values = <Map>[];
+    forEach((k, v) => values.add(v));
+    return values;
+  }
+
+  int get length => JS('int', '#.size', this);
+
+  bool get isEmpty => length == 0;
+
+  bool get isNotEmpty => !isEmpty;
+
+  void operator []=(String key, dynamic value) {
+    throw new UnsupportedError("Not supported");
+  }
+
+  dynamic putIfAbsent(String key, dynamic ifAbsent()) {
+    throw new UnsupportedError("Not supported");
+  }
+
+  String remove(dynamic key) {
+    throw new UnsupportedError("Not supported");
+  }
+
+  void clear() {
+    throw new UnsupportedError("Not supported");
+  }
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -955,14 +1012,16 @@
   @DomName('BaseAudioContext.decodeAudioData')
   @DocsEditable()
   @Experimental() // untriaged
-  Future decodeAudioData(ByteBuffer audioData,
-      [DecodeSuccessCallback successCallback,
-      DecodeErrorCallback errorCallback]) native;
+  Future<AudioBuffer> decodeAudioData(ByteBuffer audioData,
+          [DecodeSuccessCallback successCallback,
+          DecodeErrorCallback errorCallback]) =>
+      promiseToFuture<AudioBuffer>(JS("", "#.decodeAudioData(#, #, #)", this,
+          audioData, successCallback, errorCallback));
 
   @DomName('BaseAudioContext.resume')
   @DocsEditable()
   @Experimental() // untriaged
-  Future resume() native;
+  Future resume() => promiseToFuture(JS("", "#.resume()", this));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -1481,13 +1540,15 @@
   @DomName('OfflineAudioContext.startRendering')
   @DocsEditable()
   @Experimental() // untriaged
-  Future startRendering() native;
+  Future<AudioBuffer> startRendering() =>
+      promiseToFuture<AudioBuffer>(JS("", "#.startRendering()", this));
 
   @JSName('suspend')
   @DomName('OfflineAudioContext.suspend')
   @DocsEditable()
   @Experimental() // untriaged
-  Future suspendFor(num suspendTime) native;
+  Future suspendFor(num suspendTime) =>
+      promiseToFuture(JS("", "#.suspendFor(#)", this, suspendTime));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
diff --git a/sdk/lib/web_gl/dart2js/web_gl_dart2js.dart b/sdk/lib/web_gl/dart2js/web_gl_dart2js.dart
index 657d0a8..b3d8ba3 100644
--- a/sdk/lib/web_gl/dart2js/web_gl_dart2js.dart
+++ b/sdk/lib/web_gl/dart2js/web_gl_dart2js.dart
@@ -1387,7 +1387,9 @@
   @DocsEditable()
   @Experimental() // untriaged
   Future getBufferSubDataAsync(int target, int srcByteOffset, TypedData dstData,
-      [int dstOffset, int length]) native;
+          [int dstOffset, int length]) =>
+      promiseToFuture(JS("", "#.getBufferSubDataAsync(#, #, #, #, #)", this,
+          target, srcByteOffset, dstData, dstOffset, length));
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
@@ -2895,7 +2897,7 @@
   @DomName('WebGLRenderingContext.commit')
   @DocsEditable()
   @Experimental() // untriaged
-  Future commit() native;
+  Future commit() => promiseToFuture(JS("", "#.commit()", this));
 
   @DomName('WebGLRenderingContext.compileShader')
   @DocsEditable()
@@ -6709,7 +6711,7 @@
   @DomName('WebGL2RenderingContext.commit')
   @DocsEditable()
   @Experimental() // untriaged
-  Future commit() native;
+  Future commit() => promiseToFuture(JS("", "#.commit()", this));
 
   @DomName('WebGL2RenderingContext.compileShader')
   @DocsEditable()
diff --git a/tests/lib_2/html/interactive_media_test.dart b/tests/lib_2/html/interactive_media_test.dart
index 1ad71c0..25d43d4 100644
--- a/tests/lib_2/html/interactive_media_test.dart
+++ b/tests/lib_2/html/interactive_media_test.dart
@@ -6,14 +6,31 @@
 
 import 'dart:async';
 import 'dart:html';
-import 'package:unittest/unittest.dart';
-import 'package:unittest/html_individual_config.dart';
-import 'utils.dart';
 
-main() {
-  useHtmlIndividualConfiguration();
+import 'package:unittest/unittest.dart';
+import 'package:unittest/html_config.dart';
+import 'package:async_helper/async_helper.dart';
+
+// NOTE: To test enable chrome://flags/#enable-experimental-web-platform-features
+
+main() async {
+  useHtmlConfiguration();
 
   if (MediaStream.supported) {
+    test('getUserMedia audio', () async {
+      var mediaStream = await window.navigator.getUserMedia(audio: true);
+      expect(mediaStream, isNotNull);
+      expect(mediaStream is MediaStream, true);
+      var devices = window.navigator.mediaDevices;
+      var enumDevices = await devices.enumerateDevices();
+      expect(enumDevices.length > 1, true);
+      for (var device in enumDevices) {
+        var goodDevLabel = device.label.endsWith('Built-in Output') ||
+            device.label.endsWith('Built-in Microphone');
+        expect(goodDevLabel, true);
+      }
+    });
+
     test('getUserMedia', () {
       return window.navigator.getUserMedia(video: true).then((stream) {
         expect(stream, isNotNull);
diff --git a/tests/lib_2/html/storage_promise_test.dart b/tests/lib_2/html/storage_promise_test.dart
new file mode 100644
index 0000000..b1e1b13
--- /dev/null
+++ b/tests/lib_2/html/storage_promise_test.dart
@@ -0,0 +1,42 @@
+// 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 interactive_test;
+
+import 'dart:async';
+import 'dart:html';
+
+import 'package:unittest/unittest.dart';
+import 'package:unittest/html_config.dart';
+import 'package:async_helper/async_helper.dart';
+
+main() async {
+  useHtmlConfiguration();
+
+  bool thenEstimateBefore, thenEstimateAfter, thenEstimateDone = false;
+  Map thenEstimate;
+  test('Basic Promise Test', () async {
+    try {
+      thenEstimateBefore = true;
+      window.navigator.storage.estimate().then((value) {
+        thenEstimate = value;
+        thenEstimateDone = true;
+      });
+      thenEstimateAfter = true;
+    } catch (msg) {
+      fail("StorageManger failed: $msg");
+    }
+
+    Map estimate = await window.navigator.storage.estimate();
+
+    expect(thenEstimate['usage'] >= 0, true);
+    expect(thenEstimate['quota'] > 1, true);
+    expect(thenEstimate['usage'], estimate['usage']);
+    expect(thenEstimate['quota'], estimate['quota']);
+
+    expect(thenEstimateBefore, true);
+    expect(thenEstimateAfter, true);
+    expect(thenEstimateDone, true);
+  });
+}
diff --git a/tests/lib_2/lib_2_dart2js.status b/tests/lib_2/lib_2_dart2js.status
index 9177102..2c9460f 100644
--- a/tests/lib_2/lib_2_dart2js.status
+++ b/tests/lib_2/lib_2_dart2js.status
@@ -293,6 +293,7 @@
 html/shadow_dom_test: RuntimeError
 html/shadowroot_test: RuntimeError
 html/speechrecognition_test: RuntimeError
+html/storage_promise_test: RuntimeError
 html/storage_test: RuntimeError
 html/streams_test: RuntimeError
 html/svg_test: RuntimeError
@@ -341,7 +342,7 @@
 html/fontface_loaded_test: RuntimeError
 html/html_mock_test: RuntimeError # Issue 31038
 html/input_element_attributes_test: RuntimeError
-html/interactive_media_test: RuntimeError
+html/interactive_media_test: RuntimeError # # See NOTE in test on how to run.
 html/js_dart_functions_test: RuntimeError # does not implement Dart 2 implicit `.call` tearoff
 html/js_extend_class_test: RuntimeError
 
diff --git a/tests/lib_2/lib_2_dartdevc.status b/tests/lib_2/lib_2_dartdevc.status
index c9e3342..e2910bb 100644
--- a/tests/lib_2/lib_2_dartdevc.status
+++ b/tests/lib_2/lib_2_dartdevc.status
@@ -26,7 +26,7 @@
 
 [ $system == linux && ($compiler == dartdevc || $compiler == dartdevk) ]
 html/interactive_geolocation_test: Skip # Requires allowing geo location.
-html/interactive_media_test: RuntimeError
+html/interactive_media_test: RuntimeError # # See NOTE in test on how to run.
 
 [ $system == macos && ($compiler == dartdevc || $compiler == dartdevk) ]
 html/client_rect_test: Pass, RuntimeError # Issue 31019
@@ -85,7 +85,7 @@
 html/element_classes_svg_test: RuntimeError # Issue 29922
 html/element_classes_test: RuntimeError # Issue 29922
 html/fontface_loaded_test: RuntimeError
-html/interactive_media_test: Skip # requests interactive permissions (camera, geolocation)
+html/interactive_media_test: RuntimeError # See NOTE in test on how to run and interactive permissions (camera, geolocation)
 html/isolates_test: RuntimeError # Issue 29922
 html/js_typed_interop_default_arg_test/default_value: MissingCompileTimeError # Issue 29922
 html/js_typed_interop_side_cast_exp_test/01: RuntimeError # Requires --experimental-trust-js-interop-type-annotations flag.
diff --git a/tools/dom/dom.json b/tools/dom/dom.json
index 92995fc..cf6254c 100644
--- a/tools/dom/dom.json
+++ b/tools/dom/dom.json
@@ -14628,6 +14628,9 @@
         "support_level": "untriaged"
       },
       "getStats": {},
+      "getStats2": {
+        "support_level": "untriaged"
+      },
       "getStreamById": {},
       "iceConnectionState": {},
       "iceGatheringState": {},
diff --git a/tools/dom/idl/dart/dart.idl b/tools/dom/idl/dart/dart.idl
index 6773b11..a713b78 100644
--- a/tools/dom/idl/dart/dart.idl
+++ b/tools/dom/idl/dart/dart.idl
@@ -133,6 +133,9 @@
 [DartSupplemental]
 interface RTCPeerConnection {
   [DartSuppress, RaisesException] void addIceCandidate(RTCIceCandidate candidate);
+  [DartSuppress] Promise<void> getStats(RTCStatsCallback successCallback, optional MediaStreamTrack? selector);
+  [DartSuppress] Promise<void> setLocalDescription(RTCSessionDescriptionInit description);
+  [DartSuppress] Promise<void> setRemoteDescription(RTCSessionDescriptionInit description);
 };
 
 // See implementation in tools/dom/templates for canvas and offscreenCanvas.
@@ -565,12 +568,6 @@
     [DartName=inch] readonly attribute double? in;
 };
 
-[DartSupplemental]
-interface RTCPeerConnection {
-    [DartSuppress] Promise<void> setLocalDescription(RTCSessionDescriptionInit description);
-    [DartSuppress] Promise<void> setRemoteDescription(RTCSessionDescriptionInit description);
-};
-
 [DartSuppress]
 interface DragEvent {};
 
diff --git a/tools/dom/scripts/htmlrenamer.py b/tools/dom/scripts/htmlrenamer.py
index cde90fb..d0cb493 100644
--- a/tools/dom/scripts/htmlrenamer.py
+++ b/tools/dom/scripts/htmlrenamer.py
@@ -187,7 +187,7 @@
   html_interface_renames[interface] = '_' + interface
 
 convert_to_future_members = monitored.Set(
-    'htmlrenamer.converted_to_future_members', [
+  'htmlrenamer.converted_to_future_members', [
   'DataTransferItem.getAsString',
   'DirectoryEntry.getDirectory',
   'DirectoryEntry.getFile',
@@ -425,7 +425,6 @@
   'Range.getClientRects',
   'RTCPeerConnection.createAnswer',
   'RTCPeerConnection.createOffer',
-  'RTCPeerConnection.getStats',
   'Screen.availHeight',
   'Screen.availLeft',
   'Screen.availTop',
diff --git a/tools/dom/scripts/idlnode.py b/tools/dom/scripts/idlnode.py
index a9b7fd7..b2e06d2 100644
--- a/tools/dom/scripts/idlnode.py
+++ b/tools/dom/scripts/idlnode.py
@@ -854,6 +854,13 @@
     if not (self._find_first(ast, 'Partial') is None):
       self.is_supplemental = True
       self.ext_attrs['DartSupplemental'] = None
+    self.isMaplike = False
+    self.isMaplike_ro = False
+    self.maplike_key_value = [None, None]
+    if ast is not None and ast.maplike is not None:
+       self.isMaplike = True
+       self.isMaplike_ro = ast.maplike.is_read_only
+       self.maplike_key_value = [IDLType(ast.maplike.key_type), IDLType(ast.maplike.value_type)]
 
     self.operations = self._convert_all(ast, 'Operation',
       lambda ast: IDLOperation(ast, self.doc_js_name))
diff --git a/tools/dom/scripts/systemhtml.py b/tools/dom/scripts/systemhtml.py
index e100908..036b320 100644
--- a/tools/dom/scripts/systemhtml.py
+++ b/tools/dom/scripts/systemhtml.py
@@ -666,6 +666,14 @@
              self._interface.id == 'CSSStyleDeclaration')):
             base_class = 'DartHtmlDomObject'
 
+    maplikeKeyType = ''
+    maplikeValueType = ''
+    if self._interface.isMaplike:
+        maplikeKeyType = self._type_registry.\
+            _TypeInfo(self._interface.maplike_key_value[0].id).dart_type()
+        maplikeValueType = 'dynamic'
+        mixins_str = " with MapMixin<%s, %s>" % (maplikeKeyType, maplikeValueType)
+
     implementation_members_emitter = implementation_emitter.Emit(
         self._backend.ImplementationTemplate(),
         LIBRARYNAME='dart.dom.%s' % self._library_name,
@@ -676,7 +684,9 @@
         IMPLEMENTS=implements_str,
         MIXINS=mixins_str,
         DOMNAME=self._interface.doc_js_name,
-        NATIVESPEC=native_spec)
+        NATIVESPEC=native_spec,
+        KEYTYPE=maplikeKeyType,
+        VALUETYPE=maplikeValueType)
     stream_getter_signatures_emitter = None
     element_stream_getters_emitter = None
     if type(implementation_members_emitter) == tuple:
@@ -753,6 +763,102 @@
 
 # ------------------------------------------------------------------------------
 
+''' TODO(terry): Current idl_parser (Chrome) doesn't keep the Promise type e.g.,
+                 Promise<T> in the AST so there is no way to pull this out.  Need
+                 to investigate getting the Chrome folks to fix.  However, they
+                 don't use this in the C++ code generation and don't have a need
+                 for this feature.  For now I have a table that maps to the
+                 parameterized Promise type.
+'''
+promise_attributes = monitored.Dict('systemhtml.promise_attr_type', {
+    "Animation.finished": {"type": "Animation"},
+    "Animation.ready": {"type": "Animation"},
+    "FontFace.loaded": {"type": "FontFace"},
+    "FontFaceSet.ready": {"type": "FontFaceSet"},
+    "PresentationReceiver.connectionList": {"type": "PresentationConnectionList"},
+    "ServiceWorkerContainer.ready": {"type": "ServiceWorkerRegistration"},
+})
+
+promise_operations = monitored.Dict('systemhtml.promise_oper_type', {
+  "Clipboard.read": { "type": "DataTransfer" },
+  "Clipboard.readText": { "type": "String" },
+  "FontFace.load": { "type": "FontFace"},
+  "FontFaceSet.load": { "type": "List<FontFace>" },
+  "OffscreenCanvas.load": { "type": "Blob" },
+  "BackgroundFetchManager.fetch": { "type": "BackgroundFetchRegistration" },
+  "BackgroundFetchManager.get": { "type": "BackgroundFetchRegistration" },
+  "BackgroundFetchManager.getIds": { "type": "List<String>" },
+  "BackgroundFetchRegistration.abort": { "type": "bool" },
+  "SyncManager.getTags": { "type": "List<String>" },
+  "BudgetService.getCost": { "type": "double" },
+  "BudgetService.getBudget": { "type": "BudgetState" },
+  "BudgetService.reserve": { "type": "bool" },
+  "Body.blob": { "type": "Blob" },
+  "Body.formData": { "type": "FormData" },
+  "Body.text": { "type": "String" },
+  "ImageCapture.getPhotoCapabilities": { "type": "PhotoCapabilities" },
+  "ImageCapture.getPhotoSettings": { "type": "dictionary" },
+  "ImageCapture.takePhoto": { "type": "Blob" },
+  "ImageCapture.grabFrame": { "type": "ImageBitmap" },
+  "Navigator.getInstalledRelatedApps": { "type": "RelatedApplication" },
+  "MediaCapabilities.decodingInfo": { "type": "MediaCapabilitiesInfo" },
+  "MediaCapabilities.encodingInfo": { "type": "MediaCapabilitiesInfo" },
+  "MediaDevices.enumerateDevices": { "type": "List<MediaDeviceInfo>" },
+  "MediaDevices.getUserMedia": { "type": "MediaStream" },
+  "MediaStreamTrack.applyConstraints": { "type": "MediaTrackConstraints" },
+  "ServiceWorkerRegistration.getNotifications": { "type": "List<Notification>" },
+  "PaymentInstruments.delete": { "type": "bool" },
+  "PaymentInstruments.get": { "type": "dictionary" },
+  "PaymentInstruments.keys": { "type": "List<String>" },
+  "PaymentInstrumentshas.": { "type": "bool" },
+  "PaymentRequest.show": { "type": "PaymentResponse" },
+  "PaymentRequest.canMakePayment": { "type": "bool" },
+  "PaymentRequestEvent.openWindow": { "type": "WindowClient" },
+  "RTCPeerConnection.createOffer": { "type": "RtcSessionDescription" },
+  "RTCPeerConnection.createAnswer": { "type": "RtcSessionDescription" },
+  "RTCPeerConnection.getStats": { "type": "dictionary", "maplike": "RTCStatsReport"},
+  "RTCPeerConnection.generateCertificate": { "type": "RtcCertificate" },
+  "Permissions.query": { "type": "PermissionStatus" },
+  "Permissions.request": { "type": "PermissionStatus" },
+  "Permissions.revoke": { "type": "PermissionStatus" },
+  "Permissions.requestAll": { "type": "PermissionStatus" },
+  "PresentationRequest.start": { "type": "PresentationConnection" },
+  "PresentationRequest.reconnect": { "type": "PresentationConnection" },
+  "PresentationRequest.getAvailability": { "type": "PresentationAvailability" },
+  "PushManager.subscribe": { "type": "PushSubscription" },
+  "PushManager.getSubscription": { "type": "PushSubscription" },
+  "PushSubscription.unsubscribe": { "type": "bool" },
+  "StorageManager.persisted": { "type": "bool" },
+  "StorageManager.persist": { "type": "bool" },
+  "StorageManager.estimate": { "type": "dictionary" },
+  "RemotePlayback.watchAvailability": { "type": "int" },
+  "Clients.matchAll": { "type": "List<Client>" },
+  "Clients.openWindow": { "type": "WindowClient" },
+  "NavigationPreloadManager.getState": { "type": "dictionary" },
+  "ServiceWorkerContainer.register": { "type": "ServiceWorkerRegistration" },
+  "ServiceWorkerContainer.getRegistration": { "type": "ServiceWorkerRegistration" },
+  "ServiceWorkerContainer.getRegistrations": { "type": "List<ServiceWorkerRegistration>" },
+  "ServiceWorkerGlobalScope.fetch": { "type": "Response" },
+  "ServiceWorkerRegistration.unregister": { "type": "bool" },
+  "WindowClient.focus": { "type": "WindowClient" },
+  "WindowClient.navigate": { "type": "WindowClient" },
+  "BarcodeDetector.detect": { "type": "List<DetectedBarcode>" },
+  "FaceDetector.detect": { "type": "List<DetectedFace>" },
+  "TextDetector.detect": { "type": "List<DetectedText>" },
+  "BaseAudioContext.decodeAudioData": { "type": "AudioBuffer" },
+  "OfflineAudioContext.startRendering": { "type": "AudioBuffer" },
+})
+
+def _GetPromiseOperationType(interface_operation):
+  if interface_operation in promise_operations:
+    return promise_operations[interface_operation]
+  return None
+
+def _GetPromiseAttributeType(interface_operation):
+  if interface_operation in promise_attributes:
+    return promise_attributes[interface_operation]
+  return None
+
 class Dart2JSBackend(HtmlDartGenerator):
   """Generates a dart2js class for the dart:html library from a DOM IDL
   interface.
@@ -792,8 +898,14 @@
   def ImplementationTemplate(self):
     template_file = ('impl_%s.darttemplate' %
                      self._interface.doc_js_name)
-    return (self._template_loader.TryLoad(template_file) or
-            self._template_loader.Load('dart2js_impl.darttemplate'))
+    template_file_content = self._template_loader.TryLoad(template_file)
+    if not(template_file_content):
+      if self._interface.isMaplike and self._interface.isMaplike_ro:
+          # TODO(terry): There are no mutable maplikes yet.
+          template_file_content = self._template_loader.Load('dart2js_maplike_impl.darttemplate')
+      else:
+        template_file_content = self._template_loader.Load('dart2js_impl.darttemplate')
+    return template_file_content
 
   def StartInterface(self, members_emitter):
     self._members_emitter = members_emitter
@@ -1150,13 +1262,61 @@
             resultType = 'Function'
     return resultType
 
+  def _zeroArgs(self, argsNames):
+    return 'JS("", "#.$NAME()", this)'
+
+  def _manyArgs(self, numberArgs, argsNames):
+    argsPound = "#" if numberArgs == 1 else ("#, " * numberArgs)[:-2]
+    return '    JS("", "#.$NAME(%s)", this, %s)' % (argsPound, argsNames)
+
+  def _promiseToFutureCode(self, argsNames):
+    numberArgs = argsNames.count(',') + 1
+    jsCall = self._zeroArgs(argsNames) if len(argsNames) == 0 else \
+        self._manyArgs(numberArgs, argsNames)
+
+    futureTemplate = [
+    '\n'
+    '  $RENAME$METADATA$MODIFIERS $TYPE $NAME($PARAMS) => $PROMISE_CALL(',
+    jsCall,
+    ');\n'
+    ]
+    return "".join(futureTemplate)
 
   def _AddDirectNativeOperation(self, info, html_name):
     force_optional = True if html_name.startswith('_') else False
-
     resultType = self._computeResultType(info.type_name)
 
-    self._members_emitter.Emit(
+    if info.type_name == 'Promise' and not(force_optional):
+      lookupOp = "%s.%s" % (self._interface.id, html_name)
+      promiseFound = _GetPromiseOperationType(lookupOp)
+      promiseType = 'Future'
+      promiseCall = 'promiseToFuture'
+      if promiseFound is not(None):
+        if 'maplike' in promiseFound:
+          promiseCall = 'promiseToFutureMap'
+          promiseType = 'Future'
+        elif promiseFound['type'] == 'dictionary':
+          # It's a dictionary so return as a Map.
+          promiseCall = 'promiseToFutureDictionary'
+          promiseType = 'Future<Map>'
+        else:
+          paramType = promiseFound['type']
+          promiseCall = 'promiseToFuture<%s>' % paramType
+          promiseType = 'Future<%s>' % paramType
+
+      argsNames = info.ParametersAsArgumentList()
+      codeTemplate = self._promiseToFutureCode(argsNames)
+      self._members_emitter.Emit(codeTemplate,
+        RENAME=self._RenamingAnnotation(info.declared_name, html_name),
+        METADATA=self._Metadata(info.type_name, info.declared_name,
+            self.SecureOutputType(info.type_name)),
+        MODIFIERS='static ' if info.IsStatic() else '',
+        TYPE=promiseType,
+        PROMISE_CALL=promiseCall,
+        NAME=html_name,
+        PARAMS=info.ParametersAsDeclaration(self._NarrowInputType, force_optional))
+    else:
+        self._members_emitter.Emit(
         '\n'
         '  $RENAME$METADATA$MODIFIERS$TYPE $NAME($PARAMS) native;\n',
         RENAME=self._RenamingAnnotation(info.declared_name, html_name),
diff --git a/tools/dom/templates/dart2js_maplike_impl.darttemplate b/tools/dom/templates/dart2js_maplike_impl.darttemplate
new file mode 100644
index 0000000..c2f8f91
--- /dev/null
+++ b/tools/dom/templates/dart2js_maplike_impl.darttemplate
@@ -0,0 +1,67 @@
+// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of $LIBRARYNAME;
+
+@DocsEditable()
+$(ANNOTATIONS)$(NATIVESPEC)$(CLASS_MODIFIERS)class $CLASSNAME$EXTENDS$MIXINS$IMPLEMENTS {
+$!MEMBERS
+
+  Map _getItem($KEYTYPE key) =>
+      convertNativeToDart_Dictionary(JS('', '#.get(#)', this, key));
+
+  void addAll(Map<$KEYTYPE, $VALUETYPE> other) {
+    throw new UnsupportedError("Not supported");
+  }
+
+  bool containsValue($VALUETYPE value) => values.any((e) => e == value);
+
+  bool containsKey($VALUETYPE key) => _getItem(key) != null;
+
+  Map operator []($VALUETYPE key) => _getItem(key);
+
+  void forEach(void f($KEYTYPE key, $VALUETYPE value)) {
+    var entries = JS('', '#.entries()', this);
+    while (true) {
+      var entry = JS('', '#.next()', entries);
+      if (JS('bool', '#.done', entry)) return;
+      f(JS('$KEYTYPE', '#.value[0]', entry),
+          convertNativeToDart_Dictionary(JS('', '#.value[1]', entry)));
+    }
+  }
+
+  Iterable<$KEYTYPE> get keys {
+    final keys = <$KEYTYPE>[];
+    forEach((k, v) => keys.add(k));
+    return keys;
+  }
+
+  Iterable<Map> get values {
+    final values = <Map>[];
+    forEach((k, v) => values.add(v));
+    return values;
+  }
+
+  int get length => JS('int', '#.size', this);
+
+  bool get isEmpty => length == 0;
+
+  bool get isNotEmpty => !isEmpty;
+
+  void operator []=($KEYTYPE key, $VALUETYPE value) {
+    throw new UnsupportedError("Not supported");
+  }
+
+  dynamic putIfAbsent($KEYTYPE key, $VALUETYPE ifAbsent()) {
+    throw new UnsupportedError("Not supported");
+  }
+
+  $KEYTYPE remove($VALUETYPE key) {
+    throw new UnsupportedError("Not supported");
+  }
+
+  void clear() {
+    throw new UnsupportedError("Not supported");
+  }
+}
diff --git a/tools/dom/templates/html/dart2js/html_dart2js.darttemplate b/tools/dom/templates/html/dart2js/html_dart2js.darttemplate
index 2baac67..2f5c1e9 100644
--- a/tools/dom/templates/html/dart2js/html_dart2js.darttemplate
+++ b/tools/dom/templates/html/dart2js/html_dart2js.darttemplate
@@ -121,6 +121,45 @@
 HtmlDocument get document =>
     JS('returns:HtmlDocument;depends:none;effects:none;gvn:true', 'document');
 
+// Supoort to convert JS Promise to a Dart Future.
+Future<T> promiseToFuture<T>(thePromise) {
+  var completer = new Completer<T>();
+
+  var thenSuccessCode = (promiseValue) => completer.complete(promiseValue);
+  var thenErrorCode = (promiseError) => completer.completeError(promiseError);
+
+  JS("", "#.then(#, #)", thePromise, convertDartClosureToJS(thenSuccessCode, 1), convertDartClosureToJS(thenErrorCode, 1));
+
+  return completer.future;
+}
+
+// Supoort to convert JS Promise to a Dart Future that returns a MapLike (Class with Map mixin).
+Future promiseToFutureMap(thePromise) {
+  var completer = new Completer();
+
+  var thenSuccessCode = (promiseValue) => completer.complete(promiseValue);
+  var thenErrorCode = (promiseError) => completer.completeError(promiseError);
+
+  JS("", "#.then(#, #)", thePromise, convertDartClosureToJS(thenSuccessCode, 1),
+      convertDartClosureToJS(thenErrorCode, 1));
+
+  return completer.future;
+}
+
+// Supoort to convert JS Promise to a Dart Future that returns a Dictionary as a Dart Map.
+Future<Map> promiseToFutureDictionary(thePromise) {
+  var completer = new Completer<Map>();
+
+  var thenSuccessCode = (promiseValue) =>
+      completer.complete(convertNativeToDart_Dictionary(promiseValue));
+  var thenErrorCode = (promiseError) => completer.completeError(promiseError);
+
+  JS("", "#.then(#, #)", thePromise, convertDartClosureToJS(thenSuccessCode, 1),
+      convertDartClosureToJS(thenErrorCode, 1));
+
+  return completer.future;
+}
+
 // Workaround for tags like <cite> that lack their own Element subclass --
 // Dart issue 1990.
 @Native("HTMLElement")
@@ -147,4 +186,3 @@
 @Experimental() // untriaged
 typedef void FontFaceSetForEachCallback(
     FontFace fontFace, FontFace fontFaceAgain, FontFaceSet set);
-
diff --git a/tools/dom/templates/html/impl/impl_RTCPeerConnection.darttemplate b/tools/dom/templates/html/impl/impl_RTCPeerConnection.darttemplate
index ae468155..c1ed25a 100644
--- a/tools/dom/templates/html/impl/impl_RTCPeerConnection.darttemplate
+++ b/tools/dom/templates/html/impl/impl_RTCPeerConnection.darttemplate
@@ -50,13 +50,6 @@
     return completer.future;
   }
 
-  @DomName('RTCPeerConnection.getStats')
-  Future<RtcStatsResponse> getStats(MediaStreamTrack selector) {
-    var completer = new Completer<RtcStatsResponse>();
-    _getStats((value) { completer.complete(value); }, selector);
-    return completer.future;
-  }
-
   @DomName('RTCPeerConnection.generateCertificate')
   @DocsEditable()
   @Experimental() // untriaged