| 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() => new js.JsObject(js.context["Object"]); |
| putIntoMap(map, key, value) => map[key] = value; |
| // TODO(alanknight): Don't create two extra lists to get a fixed-length JS list. |
| newJsList(length) => new js.JsArray.from(new List(length)); |
| cloneNotRequired(e) => e is js.JsObject; |
| } |
| |
| class _AcceptStructuredCloneDartium extends _AcceptStructuredClone { |
| newDartList(length) => new List(length); |
| |
| // JsObjects won't be identical, but will be equal only if the underlying |
| // Js entities are identical. |
| bool identicalInJs(a, b) => |
| (a is js.JsObject) ? a == b : identical(a, b); |
| |
| void forEachJsField(jsObject, action) { |
| var keys = js.context["Object"].callMethod("keys", [jsObject]); |
| for (var key in keys) { |
| action(key, jsObject[key]); |
| } |
| } |
| } |
| |
| final _dateConstructor = js.context["Date"]; |
| final _regexConstructor = js.context["RegExp"]; |
| |
| bool isJavaScriptDate(value) => value is js.JsObject && value.instanceof(_dateConstructor); |
| bool isJavaScriptRegExp(value) => value is js.JsObject && value.instanceof(_regexConstructor); |
| bool isJavaScriptArray(value) => value is js.JsArray; |
| |
| final _object = js.context["Object"]; |
| final _getPrototypeOf = _object["getPrototypeOf"]; |
| _getProto(object) { |
| return _getPrototypeOf.apply([object]); |
| } |
| final _objectProto = js.context["Object"]["prototype"]; |
| |
| bool isJavaScriptSimpleObject(value) { |
| if (value is! js.JsObject) return false; |
| var proto = _getProto(value); |
| return proto == _objectProto || proto == null; |
| } |
| bool isImmutableJavaScriptArray(value) => |
| isJavaScriptArray(value) && value["immutable$list"] != null; |
| |
| final _promiseConstructor = js.context['Promise']; |
| bool isJavaScriptPromise(value) => value is js.JsObject && value['constructor'] == _promiseConstructor; |
| |
| Future convertNativePromiseToDartFuture(js.JsObject promise) { |
| var completer = new Completer(); |
| var newPromise = promise |
| .callMethod("then", [(result) => completer.complete(result)]) |
| .callMethod("catch", [(result) => completer.completeError(result)]); |
| return completer.future; |
| } |
| |
| convertDartToNative_DateTime(DateTime date) { |
| return new js.JsObject(js.context["Date"], [date.millisecondsSinceEpoch]); |
| } |
| |
| /// Creates a Dart Rectangle from a Javascript object with properties |
| /// left, top, width and height. Used internally in Dartium. |
| Rectangle make_dart_rectangle(r) => |
| r == null ? null : 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 = new js.JsObject(js.JsNative.getProperty(js.context, 'Object')); |
| dict.forEach((String key, value) { |
| if (value is List) { |
| var jsArray = new js.JsArray(); |
| value.forEach((elem) { |
| jsArray.add(elem is Map ? convertDartToNative_Dictionary(elem): elem); |
| }); |
| jsObject[key] = jsArray; |
| } else { |
| 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(InvocationMirror 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; |
| } |
| |
| // Helper function to wrapped a returned dictionary from blink to a Dart looking |
| // class. |
| convertNativeDictionaryToDartDictionary(Map values) => new _ReturnedDictionary(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); |
| |
| /// Find the underlying JS object for a dart:html Dart object. |
| unwrap_jso(dartClass_instance) => js.unwrap_jso(dartClass_instance); |
| |
| // Flag to disable JS interop asserts. Setting to false will speed up the |
| // wrap_jso calls. |
| bool interop_checks = false; |
| |
| /// Wrap a JS object with an instance of the matching dart:html class. Used only in Dartium. |
| wrap_jso(jsObject) { |
| try { |
| if (jsObject is! js.JsObject || jsObject == null) { |
| // JS Interop converted the object to a Dart class e.g., Uint8ClampedList. |
| // or it's a simple type. |
| return jsObject; |
| } |
| |
| var wrapper = js.getDartHtmlWrapperFor(jsObject); |
| // if we have a wrapper return the Dart instance. |
| if (wrapper != null) { |
| return wrapper; |
| } |
| |
| if (jsObject is js.JsArray) { |
| wrapper = new js.JSArray.create(jsObject); |
| js.setDartHtmlWrapperFor(jsObject, wrapper); |
| return wrapper; |
| } |
| if (jsObject is js.JsFunction) { |
| wrapper = new js.JSFunction.create(jsObject); |
| js.setDartHtmlWrapperFor(jsObject, wrapper); |
| return wrapper; |
| } |
| |
| // Try the most general type conversions on it. |
| // TODO(alanknight): We may be able to do better. This maintains identity, |
| // which is useful, but expensive. And if we nest something that only |
| // this conversion handles, how does that work? e.g. a list of maps of elements. |
| var converted = convertNativeToDart_SerializedScriptValue(jsObject); |
| if (!identical(converted, jsObject)) { |
| return converted; |
| } |
| |
| var constructor = js.JsNative.getProperty(jsObject, 'constructor'); |
| if (constructor == null) { |
| // Perfectly valid case for JavaScript objects where __proto__ has |
| // intentionally been set to null. |
| js.setDartHtmlWrapperFor(jsObject, new js.JSObject.create(jsObject)); |
| return jsObject; |
| } |
| var jsTypeName = js.JsNative.getProperty(constructor, 'name'); |
| if (jsTypeName is! String || jsTypeName.length == 0) { |
| // Not an html type. |
| wrapper = new js.JSObject.create(jsObject); |
| js.setDartHtmlWrapperFor(jsObject, wrapper); |
| return wrapper; |
| } |
| |
| var dartClass_instance; |
| var customElementClass = null; |
| var extendsTag = ""; |
| var custom = getCustomElementEntry(jsObject); |
| if (custom != null) { |
| customElementClass = custom['type']; |
| extendsTag = custom['extends']; |
| } |
| |
| // Only allow custom elements to be created in the html or svg default |
| // namespace. |
| var func; |
| var defaultNS = jsObject['namespaceURI'] == 'http://www.w3.org/1999/xhtml' || |
| jsObject['namespaceURI'] == 'http://www.w3.org/2000/svg'; |
| if (customElementClass != null && extendsTag == "" && defaultNS) { |
| // The customElementClass is known but we can't create the real class so |
| // create the HtmlElement and it will get upgraded when registerElement's |
| // createdCallback is called. |
| func = getHtmlCreateFunction('HTMLElement'); |
| } else { |
| func = getHtmlCreateFunction(jsTypeName); |
| if (func == null) { |
| // Start walking the prototype chain looking for a JS class. |
| var prototype = jsObject['__proto__']; |
| var keepWalking = true; |
| while (keepWalking && prototype.hasProperty('__proto__')) { |
| prototype = prototype['__proto__']; |
| if (prototype != null && prototype is Element && |
| prototype.blink_jsObject != null) { |
| // We're a Dart class that's pointing to a JS class. |
| var blinkJso = prototype.blink_jsObject; |
| jsTypeName = blinkJso['constructor']['name']; |
| func = getHtmlCreateFunction(jsTypeName); |
| keepWalking = func == null; |
| } |
| } |
| } |
| } |
| |
| // Can we construct a Dart class? |
| if (func != null) { |
| dartClass_instance = func(); |
| |
| // Wrap our Dart instance in both directions. |
| dartClass_instance.blink_jsObject = jsObject; |
| js.setDartHtmlWrapperFor(jsObject, dartClass_instance); |
| } |
| |
| // TODO(jacobr): cache that this is not a dart:html JS class. |
| return dartClass_instance; |
| } catch (e, stacktrace) { |
| if (interop_checks) { |
| if (e is DebugAssertException) window.console |
| .log("${e.message}\n ${stacktrace}"); |
| else window.console.log("${stacktrace}"); |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Create Dart class that maps to the JS Type, add the JsObject as an expando |
| * on the Dart class and return the created Dart class. |
| */ |
| wrap_jso_no_SerializedScriptvalue(jsObject) { |
| try { |
| if (jsObject is! js.JsObject || jsObject == null) { |
| // JS Interop converted the object to a Dart class e.g., Uint8ClampedList. |
| // or it's a simple type. |
| return jsObject; |
| } |
| |
| // TODO(alanknight): With upgraded custom elements this causes a failure because |
| // we need a new wrapper after the type changes. We could possibly invalidate this |
| // if the constructor name didn't match? |
| var wrapper = js.getDartHtmlWrapperFor(jsObject); |
| if (wrapper != null) { |
| return wrapper; |
| } |
| |
| if (jsObject is js.JsArray) { |
| wrapper = new js.JSArray.create(jsObject); |
| js.setDartHtmlWrapperFor(jsObject, wrapper); |
| return wrapper; |
| } |
| if (jsObject is js.JsFunction) { |
| wrapper = new js.JSFunction.create(jsObject); |
| js.setDartHtmlWrapperFor(jsObject, wrapper); |
| return wrapper; |
| } |
| |
| var constructor = js.JsNative.getProperty(jsObject, 'constructor'); |
| if (constructor == null) { |
| // Perfectly valid case for JavaScript objects where __proto__ has |
| // intentionally been set to null. |
| js.setDartHtmlWrapperFor(jsObject, new js.JSObject.create(jsObject)); |
| return jsObject; |
| } |
| var jsTypeName = js.JsNative.getProperty(constructor, 'name'); |
| if (jsTypeName is! String || jsTypeName.length == 0) { |
| // Not an html type. |
| wrapper = new js.JSObject.create(jsObject); |
| js.setDartHtmlWrapperFor(jsObject, wrapper); |
| return wrapper; |
| } |
| |
| var func = getHtmlCreateFunction(jsTypeName); |
| if (func != null) { |
| var dartClass_instance = func(); |
| dartClass_instance.blink_jsObject = jsObject; |
| js.setDartHtmlWrapperFor(jsObject, dartClass_instance); |
| return dartClass_instance; |
| } |
| wrapper = new js.JSObject.create(jsObject); |
| js.setDartHtmlWrapperFor(jsObject, wrapper); |
| return wrapper; |
| } catch (e, stacktrace) { |
| if (interop_checks) { |
| if (e is DebugAssertException) window.console |
| .log("${e.message}\n ${stacktrace}"); |
| else window.console.log("${stacktrace}"); |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Create Dart class that maps to the JS Type that is the JS type being |
| * extended using JS interop createCallback (we need the base type of the |
| * custom element) not the Dart created constructor. |
| */ |
| wrap_jso_custom_element(jsObject) { |
| try { |
| if (jsObject is! js.JsObject) { |
| // JS Interop converted the object to a Dart class e.g., Uint8ClampedList. |
| return jsObject; |
| } |
| |
| // Find out what object we're extending. |
| var objectName = jsObject.toString(); |
| // Expect to see something like '[object HTMLElement]'. |
| if (!objectName.startsWith('[object ')) { |
| return jsObject; |
| } |
| |
| var extendsClass = objectName.substring(8, objectName.length - 1); |
| var func = getHtmlCreateFunction(extendsClass); |
| if (interop_checks) |
| debug_or_assert("func != null name = ${extendsClass}", func != null); |
| var dartClass_instance = func(); |
| dartClass_instance.blink_jsObject = jsObject; |
| return dartClass_instance; |
| } catch(e, stacktrace){ |
| if (interop_checks) { |
| if (e is DebugAssertException) |
| window.console.log("${e.message}\n ${stacktrace}"); |
| else |
| window.console.log("${stacktrace}"); |
| } |
| |
| // Problem? |
| return null; |
| } |
| } |
| |
| getCustomElementEntry(element) { |
| var hasAttribute = false; |
| |
| var jsObject; |
| var tag = ""; |
| var runtimeType = element.runtimeType; |
| if (runtimeType == HtmlElement) { |
| tag = element.localName; |
| } else if (runtimeType == TemplateElement) { |
| // Data binding with a Dart class. |
| tag = element.attributes['is']; |
| } else if (runtimeType == js.JsObject) { |
| // It's a Polymer core element (written in JS). |
| // Make sure it's an element anything else we can ignore. |
| if (element.hasProperty('nodeType') && element['nodeType'] == 1) { |
| if (js.JsNative.callMethod(element, 'hasAttribute', ['is'])) { |
| hasAttribute = true; |
| // It's data binding use the is attribute. |
| tag = js.JsNative.callMethod(element, 'getAttribute', ['is']); |
| } else { |
| // It's a custom element we want the local name. |
| tag = element['localName']; |
| } |
| } |
| } else { |
| throw new UnsupportedError( |
| 'Element is incorrect type. Got ${runtimeType}, expected HtmlElement/HtmlTemplate/JsObject.'); |
| } |
| |
| var entry = _knownCustomElements[tag]; |
| if (entry != null) { |
| // If there's an 'is' attribute then check if the extends tag registered |
| // matches the tag if so then return the entry that's registered for this |
| // extendsTag or if there's no 'is' tag then return the entry found. |
| if ((hasAttribute && entry['extends'] == tag) || !hasAttribute) { |
| return entry; |
| } |
| } |
| |
| return null; |
| } |
| |
| // List of known tagName to DartClass for custom elements, used for upgrade. |
| var _knownCustomElements = new Map<String, Map<Type, String>>(); |
| |
| void addCustomElementType(String tagName, Type dartClass, [String extendTag]) { |
| _knownCustomElements[tagName] = |
| {'type': dartClass, 'extends': extendTag != null ? extendTag : "" }; |
| } |
| |
| Type getCustomElementType(object) { |
| var entry = getCustomElementEntry(object); |
| if (entry != null) { |
| return entry['type']; |
| } |
| return null; |
| } |