Index: sdk/lib/html/html_common/conversions_dartium.dart |
diff --git a/sdk/lib/html/html_common/conversions_dartium.dart b/sdk/lib/html/html_common/conversions_dartium.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..892cc1e081347760e1055a57ea6b4650f0a3630c |
--- /dev/null |
+++ b/sdk/lib/html/html_common/conversions_dartium.dart |
@@ -0,0 +1,294 @@ |
+part of html_common; |
+ |
+convertDartToNative_PrepareForStructuredClone(value) => |
+ new _StructuredCloneDartium() |
+ .convertDartToNative_PrepareForStructuredClone(value); |
+ |
+convertNativeToDart_AcceptStructuredClone(object, {mustCopy: false}) => |
+ new _AcceptStructuredCloneDartium() |
+ .convertNativeToDart_AcceptStructuredClone(object, mustCopy: mustCopy); |
+ |
+class _StructuredCloneDartium extends _StructuredClone { |
+ newJsMap() => js.JsNative.newObject(); |
+ putIntoMap(map, key, value) => js.JsNative.setProperty(map, key, value); |
+ newJsList(length) => js.JsNative.newArray()..length = length; |
+ cloneNotRequired(e) => e is js.JSObject || e is TypedData || e is ByteBuffer; |
+} |
+ |
+/// A version of _AcceptStructuredClone, but using a different algorithm |
+/// so we can take advantage of an identity HashMap on Dartium without |
+/// the bad side-effect of modifying the JS source objects if we do the same in |
+/// dart2js. |
+/// |
+/// This no longer inherits anything from _AcceptStructuredClone |
+/// and is never used polymorphically with it, so it doesn't inherit. |
+class _AcceptStructuredCloneDartium { |
+ newDartList(length) => new List(length); |
+ |
+ // As long as we stick to JSObject instead of intermingling legacy JsObject, |
+ // we can simply use identical. |
+ bool identicalInJs(a, b) => identical(a, b); |
+ |
+ void forEachJsField(jsObject, action) { |
+ var keys = js.JsNative.callMethod(_object, "keys", [jsObject]); |
+ for (var key in keys) { |
+ action(key, js.JsNative.getProperty(jsObject, key)); |
+ } |
+ } |
+ |
+ // Keep track of the clones, keyed by the original object. If we're |
+ // not copying, these may be the same. |
+ var clones = new HashMap.identity(); |
+ bool mustCopy = false; |
+ |
+ Object findSlot(value) { |
+ return clones.putIfAbsent(value, () => null); |
+ } |
+ |
+ writeSlot(original, x) { |
+ clones[original] = x; |
+ } |
+ |
+ walk(e) { |
+ if (e == null) return e; |
+ if (e is bool) return e; |
+ if (e is num) return e; |
+ if (e is String) return e; |
+ if (e is DateTime) return e; |
+ |
+ if (isJavaScriptRegExp(e)) { |
+ // TODO(sra). |
+ throw new UnimplementedError('structured clone of RegExp'); |
+ } |
+ |
+ if (isJavaScriptPromise(e)) { |
+ return convertNativePromiseToDartFuture(e); |
+ } |
+ |
+ if (isJavaScriptSimpleObject(e)) { |
+ // TODO(sra): If mustCopy is false, swizzle the prototype for one of a Map |
+ // implementation that uses the properies as storage. |
+ var copy = findSlot(e); |
+ if (copy != null) return copy; |
+ copy = {}; |
+ |
+ writeSlot(e, copy); |
+ forEachJsField(e, (key, value) => copy[key] = walk(value)); |
+ return copy; |
+ } |
+ |
+ if (isJavaScriptArray(e)) { |
+ var copy = findSlot(e); |
+ if (copy != null) return copy; |
+ |
+ int length = e.length; |
+ // Since a JavaScript Array is an instance of Dart List, we can modify it |
+ // in-place unless we must copy. |
+ copy = mustCopy ? newDartList(length) : e; |
+ writeSlot(e, copy); |
+ |
+ for (int i = 0; i < length; i++) { |
+ copy[i] = walk(e[i]); |
+ } |
+ return copy; |
+ } |
+ |
+ // Assume anything else is already a valid Dart object, either by having |
+ // already been processed, or e.g. a clonable native class. |
+ return e; |
+ } |
+ |
+ convertNativeToDart_AcceptStructuredClone(object, {mustCopy: false}) { |
+ this.mustCopy = mustCopy; |
+ var copy = walk(object); |
+ return copy; |
+ } |
+} |
+ |
+final _dateConstructor = js.JsNative.getProperty(window, "Date"); |
+final _regexConstructor = js.JsNative.getProperty(window, "RegExp"); |
+ |
+bool isJavaScriptDate(value) => |
+ value is js.JSObject && js.JsNative.instanceof(value, _dateConstructor); |
+bool isJavaScriptRegExp(value) => |
+ value is js.JSObject && js.JsNative.instanceof(value, _regexConstructor); |
+bool isJavaScriptArray(value) => value is js.JSArray; |
+ |
+final _object = js.JsNative.getProperty(window, "Object"); |
+final _getPrototypeOf = js.JsNative.getProperty(_object, "getPrototypeOf"); |
+_getProto(object) { |
+ return _getPrototypeOf(object); |
+} |
+ |
+final _objectProto = js.JsNative.getProperty(_object, "prototype"); |
+ |
+bool isJavaScriptSimpleObject(value) { |
+ if (value is! js.JSObject) return false; |
+ var proto = _getProto(value); |
+ return proto == _objectProto || proto == null; |
+} |
+ |
+// TODO(jacobr): this makes little sense unless we are doing something |
+// ambitious to make Dartium and Dart2Js interop well with each other. |
+bool isImmutableJavaScriptArray(value) => |
+ isJavaScriptArray(value) && |
+ js.JsNative.getProperty(value, "immutable$list") != null; |
+ |
+final _promiseConstructor = js.JsNative.getProperty(window, 'Promise'); |
+bool isJavaScriptPromise(value) => |
+ value is js.JSObject && |
+ identical( |
+ js.JsNative.getProperty(value, 'constructor'), _promiseConstructor); |
+ |
+Future convertNativePromiseToDartFuture(js.JSObject promise) { |
+ var completer = new Completer(); |
+ var newPromise = js.JsNative.callMethod( |
+ js.JsNative.callMethod(promise, "then", |
+ [js.allowInterop((result) => completer.complete(result))]), |
+ "catch", |
+ [js.allowInterop((result) => completer.completeError(result))]); |
+ return completer.future; |
+} |
+ |
+convertDartToNative_DateTime(DateTime date) { |
+ return date; |
+} |
+ |
+/// Creates a Dart Rectangle from a Javascript object with properties |
+/// left, top, width and height or a 4 element array of integers. Used internally in Dartium. |
+Rectangle make_dart_rectangle(r) { |
+ if (r == null) return null; |
+ if (r is List) { |
+ return new Rectangle(r[0], r[1], r[2], r[3]); |
+ } |
+ |
+ return new Rectangle( |
+ js.JsNative.getProperty(r, 'left'), |
+ js.JsNative.getProperty(r, 'top'), |
+ js.JsNative.getProperty(r, 'width'), |
+ js.JsNative.getProperty(r, 'height')); |
+} |
+ |
+// Converts a flat Dart map into a JavaScript object with properties this is |
+// is the Dartium only version it uses dart:js. |
+// TODO(alanknight): This could probably be unified with the dart2js conversions |
+// code in html_common and be more general. |
+convertDartToNative_Dictionary(Map dict) { |
+ if (dict == null) return null; |
+ var jsObject = js.JsNative.newObject(); |
+ dict.forEach((String key, value) { |
+ if (value is List) { |
+ var jsArray = js.JsNative.newArray(); |
+ value.forEach((elem) { |
+ jsArray.add(elem is Map ? convertDartToNative_Dictionary(elem) : elem); |
+ }); |
+ js.JsNative.setProperty(jsObject, key, jsArray); |
+ } else { |
+ js.JsNative.setProperty(jsObject, key, value); |
+ } |
+ }); |
+ return jsObject; |
+} |
+ |
+// Creates a Dart class to allow members of the Map to be fetched (as if getters exist). |
+// TODO(terry): Need to use package:js but that's a problem in dart:html. Talk to |
+// Jacob about how to do this properly using dart:js. |
+class _ReturnedDictionary { |
+ Map _values; |
+ |
+ noSuchMethod(Invocation invocation) { |
+ var key = MirrorSystem.getName(invocation.memberName); |
+ if (invocation.isGetter) { |
+ return _values[key]; |
+ } else if (invocation.isSetter && key.endsWith('=')) { |
+ key = key.substring(0, key.length - 1); |
+ _values[key] = invocation.positionalArguments[0]; |
+ } |
+ } |
+ |
+ Map get toMap => _values; |
+ |
+ _ReturnedDictionary(Map value) : _values = value != null ? value : {}; |
+} |
+ |
+// Helper function to wrapped a returned dictionary from blink to a Dart looking |
+// class. |
+convertNativeDictionaryToDartDictionary(values) { |
+ if (values is! Map) { |
+ // TODO(jacobr): wish wwe didn't have to do this. |
+ values = convertNativeToDart_SerializedScriptValue(values); |
+ } |
+ return values != null ? new _ReturnedDictionary(values) : null; |
+} |
+ |
+convertNativeToDart_Dictionary(values) => |
+ convertNativeToDart_SerializedScriptValue(values); |
+ |
+// Conversion function place holder (currently not used in dart2js or dartium). |
+List convertDartToNative_StringArray(List<String> input) => input; |
+ |
+// Converts a Dart list into a JsArray. For the Dartium version only. |
+convertDartToNative_List(List input) => new js.JsArray()..addAll(input); |
+ |
+// Incredibly slow implementation to lookup the runtime type for an object. |
+// Fortunately, performance doesn't matter much as the results are cached |
+// as long as the object being looked up has a valid prototype. |
+// TODO(jacobr): we should track the # of lookups to ensure that things aren't |
+// going off the rails due to objects with null prototypes, etc. |
+// Note: unlike all other methods in this class, here we intentionally use |
+// the old JsObject types to bootstrap the new typed bindings. |
+Type lookupType(js.JsObject jsObject, bool isElement) { |
+ try { |
+ // TODO(jacobr): add static methods that return the runtime type of the patch |
+ // class so that this code works as expected. |
+ if (jsObject is js.JsArray) { |
+ return js.JSArray.instanceRuntimeType; |
+ } |
+ if (jsObject is js.JsFunction) { |
+ return js.JSFunction.instanceRuntimeType; |
+ } |
+ |
+ var constructor = js.JsNative.getProperty(jsObject, 'constructor'); |
+ if (constructor == null) { |
+ // Perfectly valid case for JavaScript objects where __proto__ has |
+ // intentionally been set to null. |
+ // We should track and warn about this case as peformance will be poor. |
+ return js.JSObject.instanceRuntimeType; |
+ } |
+ var jsTypeName = js.JsNative.getProperty(constructor, 'name'); |
+ if (jsTypeName is! String || jsTypeName.length == 0) { |
+ // Not an html type. |
+ return js.JSObject.instanceRuntimeType; |
+ } |
+ |
+ var dartClass_instance; |
+ var customElementClass = null; |
+ var extendsTag = ""; |
+ |
+ Type type = getHtmlCreateType(jsTypeName); |
+ if (type != null) return type; |
+ |
+ // Start walking the prototype chain looking for a JS class. |
+ var prototype = js.JsNative.getProperty(jsObject, '__proto__'); |
+ while (prototype != null) { |
+ // We're a Dart class that's pointing to a JS class. |
+ var constructor = js.JsNative.getProperty(prototype, 'constructor'); |
+ if (constructor != null) { |
+ jsTypeName = js.JsNative.getProperty(constructor, 'name'); |
+ type = getHtmlCreateType(jsTypeName); |
+ if (type != null) return type; |
+ } |
+ prototype = js.JsNative.getProperty(prototype, '__proto__'); |
+ } |
+ } catch (e) { |
+ // This case can happen for cross frame objects. |
+ if (js.JsNative.hasProperty(e, "postMessage")) { |
+ // assume this is a Window. To match Dart2JS, separate conversion code |
+ // in dart:html will switch the wrapper to a cross frame window as |
+ // required. |
+ // TODO(jacobr): we could consider removing this code completely. |
+ return Window.instanceRuntimeType; |
+ } |
+ } |
+ return js.JSObject.instanceRuntimeType; |
+} |