OLD | NEW |
(Empty) | |
| 1 part of html_common; |
| 2 |
| 3 convertDartToNative_PrepareForStructuredClone(value) => |
| 4 new _StructuredCloneDartium() |
| 5 .convertDartToNative_PrepareForStructuredClone(value); |
| 6 |
| 7 convertNativeToDart_AcceptStructuredClone(object, {mustCopy: false}) => |
| 8 new _AcceptStructuredCloneDartium() |
| 9 .convertNativeToDart_AcceptStructuredClone(object, mustCopy: mustCopy); |
| 10 |
| 11 class _StructuredCloneDartium extends _StructuredClone { |
| 12 newJsMap() => js.JsNative.newObject(); |
| 13 putIntoMap(map, key, value) => js.JsNative.setProperty(map, key, value); |
| 14 newJsList(length) => js.JsNative.newArray()..length = length; |
| 15 cloneNotRequired(e) => e is js.JSObject || e is TypedData || e is ByteBuffer; |
| 16 } |
| 17 |
| 18 /// A version of _AcceptStructuredClone, but using a different algorithm |
| 19 /// so we can take advantage of an identity HashMap on Dartium without |
| 20 /// the bad side-effect of modifying the JS source objects if we do the same in |
| 21 /// dart2js. |
| 22 /// |
| 23 /// This no longer inherits anything from _AcceptStructuredClone |
| 24 /// and is never used polymorphically with it, so it doesn't inherit. |
| 25 class _AcceptStructuredCloneDartium { |
| 26 newDartList(length) => new List(length); |
| 27 |
| 28 // As long as we stick to JSObject instead of intermingling legacy JsObject, |
| 29 // we can simply use identical. |
| 30 bool identicalInJs(a, b) => identical(a, b); |
| 31 |
| 32 void forEachJsField(jsObject, action) { |
| 33 var keys = js.JsNative.callMethod(_object, "keys", [jsObject]); |
| 34 for (var key in keys) { |
| 35 action(key, js.JsNative.getProperty(jsObject, key)); |
| 36 } |
| 37 } |
| 38 |
| 39 // Keep track of the clones, keyed by the original object. If we're |
| 40 // not copying, these may be the same. |
| 41 var clones = new HashMap.identity(); |
| 42 bool mustCopy = false; |
| 43 |
| 44 Object findSlot(value) { |
| 45 return clones.putIfAbsent(value, () => null); |
| 46 } |
| 47 |
| 48 writeSlot(original, x) { |
| 49 clones[original] = x; |
| 50 } |
| 51 |
| 52 walk(e) { |
| 53 if (e == null) return e; |
| 54 if (e is bool) return e; |
| 55 if (e is num) return e; |
| 56 if (e is String) return e; |
| 57 if (e is DateTime) return e; |
| 58 |
| 59 if (isJavaScriptRegExp(e)) { |
| 60 // TODO(sra). |
| 61 throw new UnimplementedError('structured clone of RegExp'); |
| 62 } |
| 63 |
| 64 if (isJavaScriptPromise(e)) { |
| 65 return convertNativePromiseToDartFuture(e); |
| 66 } |
| 67 |
| 68 if (isJavaScriptSimpleObject(e)) { |
| 69 // TODO(sra): If mustCopy is false, swizzle the prototype for one of a Map |
| 70 // implementation that uses the properies as storage. |
| 71 var copy = findSlot(e); |
| 72 if (copy != null) return copy; |
| 73 copy = {}; |
| 74 |
| 75 writeSlot(e, copy); |
| 76 forEachJsField(e, (key, value) => copy[key] = walk(value)); |
| 77 return copy; |
| 78 } |
| 79 |
| 80 if (isJavaScriptArray(e)) { |
| 81 var copy = findSlot(e); |
| 82 if (copy != null) return copy; |
| 83 |
| 84 int length = e.length; |
| 85 // Since a JavaScript Array is an instance of Dart List, we can modify it |
| 86 // in-place unless we must copy. |
| 87 copy = mustCopy ? newDartList(length) : e; |
| 88 writeSlot(e, copy); |
| 89 |
| 90 for (int i = 0; i < length; i++) { |
| 91 copy[i] = walk(e[i]); |
| 92 } |
| 93 return copy; |
| 94 } |
| 95 |
| 96 // Assume anything else is already a valid Dart object, either by having |
| 97 // already been processed, or e.g. a clonable native class. |
| 98 return e; |
| 99 } |
| 100 |
| 101 convertNativeToDart_AcceptStructuredClone(object, {mustCopy: false}) { |
| 102 this.mustCopy = mustCopy; |
| 103 var copy = walk(object); |
| 104 return copy; |
| 105 } |
| 106 } |
| 107 |
| 108 final _dateConstructor = js.JsNative.getProperty(window, "Date"); |
| 109 final _regexConstructor = js.JsNative.getProperty(window, "RegExp"); |
| 110 |
| 111 bool isJavaScriptDate(value) => |
| 112 value is js.JSObject && js.JsNative.instanceof(value, _dateConstructor); |
| 113 bool isJavaScriptRegExp(value) => |
| 114 value is js.JSObject && js.JsNative.instanceof(value, _regexConstructor); |
| 115 bool isJavaScriptArray(value) => value is js.JSArray; |
| 116 |
| 117 final _object = js.JsNative.getProperty(window, "Object"); |
| 118 final _getPrototypeOf = js.JsNative.getProperty(_object, "getPrototypeOf"); |
| 119 _getProto(object) { |
| 120 return _getPrototypeOf(object); |
| 121 } |
| 122 |
| 123 final _objectProto = js.JsNative.getProperty(_object, "prototype"); |
| 124 |
| 125 bool isJavaScriptSimpleObject(value) { |
| 126 if (value is! js.JSObject) return false; |
| 127 var proto = _getProto(value); |
| 128 return proto == _objectProto || proto == null; |
| 129 } |
| 130 |
| 131 // TODO(jacobr): this makes little sense unless we are doing something |
| 132 // ambitious to make Dartium and Dart2Js interop well with each other. |
| 133 bool isImmutableJavaScriptArray(value) => |
| 134 isJavaScriptArray(value) && |
| 135 js.JsNative.getProperty(value, "immutable$list") != null; |
| 136 |
| 137 final _promiseConstructor = js.JsNative.getProperty(window, 'Promise'); |
| 138 bool isJavaScriptPromise(value) => |
| 139 value is js.JSObject && |
| 140 identical( |
| 141 js.JsNative.getProperty(value, 'constructor'), _promiseConstructor); |
| 142 |
| 143 Future convertNativePromiseToDartFuture(js.JSObject promise) { |
| 144 var completer = new Completer(); |
| 145 var newPromise = js.JsNative.callMethod( |
| 146 js.JsNative.callMethod(promise, "then", |
| 147 [js.allowInterop((result) => completer.complete(result))]), |
| 148 "catch", |
| 149 [js.allowInterop((result) => completer.completeError(result))]); |
| 150 return completer.future; |
| 151 } |
| 152 |
| 153 convertDartToNative_DateTime(DateTime date) { |
| 154 return date; |
| 155 } |
| 156 |
| 157 /// Creates a Dart Rectangle from a Javascript object with properties |
| 158 /// left, top, width and height or a 4 element array of integers. Used internall
y in Dartium. |
| 159 Rectangle make_dart_rectangle(r) { |
| 160 if (r == null) return null; |
| 161 if (r is List) { |
| 162 return new Rectangle(r[0], r[1], r[2], r[3]); |
| 163 } |
| 164 |
| 165 return new Rectangle( |
| 166 js.JsNative.getProperty(r, 'left'), |
| 167 js.JsNative.getProperty(r, 'top'), |
| 168 js.JsNative.getProperty(r, 'width'), |
| 169 js.JsNative.getProperty(r, 'height')); |
| 170 } |
| 171 |
| 172 // Converts a flat Dart map into a JavaScript object with properties this is |
| 173 // is the Dartium only version it uses dart:js. |
| 174 // TODO(alanknight): This could probably be unified with the dart2js conversions |
| 175 // code in html_common and be more general. |
| 176 convertDartToNative_Dictionary(Map dict) { |
| 177 if (dict == null) return null; |
| 178 var jsObject = js.JsNative.newObject(); |
| 179 dict.forEach((String key, value) { |
| 180 if (value is List) { |
| 181 var jsArray = js.JsNative.newArray(); |
| 182 value.forEach((elem) { |
| 183 jsArray.add(elem is Map ? convertDartToNative_Dictionary(elem) : elem); |
| 184 }); |
| 185 js.JsNative.setProperty(jsObject, key, jsArray); |
| 186 } else { |
| 187 js.JsNative.setProperty(jsObject, key, value); |
| 188 } |
| 189 }); |
| 190 return jsObject; |
| 191 } |
| 192 |
| 193 // Creates a Dart class to allow members of the Map to be fetched (as if getters
exist). |
| 194 // TODO(terry): Need to use package:js but that's a problem in dart:html. Talk t
o |
| 195 // Jacob about how to do this properly using dart:js. |
| 196 class _ReturnedDictionary { |
| 197 Map _values; |
| 198 |
| 199 noSuchMethod(Invocation invocation) { |
| 200 var key = MirrorSystem.getName(invocation.memberName); |
| 201 if (invocation.isGetter) { |
| 202 return _values[key]; |
| 203 } else if (invocation.isSetter && key.endsWith('=')) { |
| 204 key = key.substring(0, key.length - 1); |
| 205 _values[key] = invocation.positionalArguments[0]; |
| 206 } |
| 207 } |
| 208 |
| 209 Map get toMap => _values; |
| 210 |
| 211 _ReturnedDictionary(Map value) : _values = value != null ? value : {}; |
| 212 } |
| 213 |
| 214 // Helper function to wrapped a returned dictionary from blink to a Dart looking |
| 215 // class. |
| 216 convertNativeDictionaryToDartDictionary(values) { |
| 217 if (values is! Map) { |
| 218 // TODO(jacobr): wish wwe didn't have to do this. |
| 219 values = convertNativeToDart_SerializedScriptValue(values); |
| 220 } |
| 221 return values != null ? new _ReturnedDictionary(values) : null; |
| 222 } |
| 223 |
| 224 convertNativeToDart_Dictionary(values) => |
| 225 convertNativeToDart_SerializedScriptValue(values); |
| 226 |
| 227 // Conversion function place holder (currently not used in dart2js or dartium). |
| 228 List convertDartToNative_StringArray(List<String> input) => input; |
| 229 |
| 230 // Converts a Dart list into a JsArray. For the Dartium version only. |
| 231 convertDartToNative_List(List input) => new js.JsArray()..addAll(input); |
| 232 |
| 233 // Incredibly slow implementation to lookup the runtime type for an object. |
| 234 // Fortunately, performance doesn't matter much as the results are cached |
| 235 // as long as the object being looked up has a valid prototype. |
| 236 // TODO(jacobr): we should track the # of lookups to ensure that things aren't |
| 237 // going off the rails due to objects with null prototypes, etc. |
| 238 // Note: unlike all other methods in this class, here we intentionally use |
| 239 // the old JsObject types to bootstrap the new typed bindings. |
| 240 Type lookupType(js.JsObject jsObject, bool isElement) { |
| 241 try { |
| 242 // TODO(jacobr): add static methods that return the runtime type of the patc
h |
| 243 // class so that this code works as expected. |
| 244 if (jsObject is js.JsArray) { |
| 245 return js.JSArray.instanceRuntimeType; |
| 246 } |
| 247 if (jsObject is js.JsFunction) { |
| 248 return js.JSFunction.instanceRuntimeType; |
| 249 } |
| 250 |
| 251 var constructor = js.JsNative.getProperty(jsObject, 'constructor'); |
| 252 if (constructor == null) { |
| 253 // Perfectly valid case for JavaScript objects where __proto__ has |
| 254 // intentionally been set to null. |
| 255 // We should track and warn about this case as peformance will be poor. |
| 256 return js.JSObject.instanceRuntimeType; |
| 257 } |
| 258 var jsTypeName = js.JsNative.getProperty(constructor, 'name'); |
| 259 if (jsTypeName is! String || jsTypeName.length == 0) { |
| 260 // Not an html type. |
| 261 return js.JSObject.instanceRuntimeType; |
| 262 } |
| 263 |
| 264 var dartClass_instance; |
| 265 var customElementClass = null; |
| 266 var extendsTag = ""; |
| 267 |
| 268 Type type = getHtmlCreateType(jsTypeName); |
| 269 if (type != null) return type; |
| 270 |
| 271 // Start walking the prototype chain looking for a JS class. |
| 272 var prototype = js.JsNative.getProperty(jsObject, '__proto__'); |
| 273 while (prototype != null) { |
| 274 // We're a Dart class that's pointing to a JS class. |
| 275 var constructor = js.JsNative.getProperty(prototype, 'constructor'); |
| 276 if (constructor != null) { |
| 277 jsTypeName = js.JsNative.getProperty(constructor, 'name'); |
| 278 type = getHtmlCreateType(jsTypeName); |
| 279 if (type != null) return type; |
| 280 } |
| 281 prototype = js.JsNative.getProperty(prototype, '__proto__'); |
| 282 } |
| 283 } catch (e) { |
| 284 // This case can happen for cross frame objects. |
| 285 if (js.JsNative.hasProperty(e, "postMessage")) { |
| 286 // assume this is a Window. To match Dart2JS, separate conversion code |
| 287 // in dart:html will switch the wrapper to a cross frame window as |
| 288 // required. |
| 289 // TODO(jacobr): we could consider removing this code completely. |
| 290 return Window.instanceRuntimeType; |
| 291 } |
| 292 } |
| 293 return js.JSObject.instanceRuntimeType; |
| 294 } |
OLD | NEW |