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 |