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 |
index 28225f089f67d3c50b89df6cf00a9db5a88c8778..1ed1e1348bc7e99415693b7b4965eae05555d696 100644 |
--- a/sdk/lib/html/html_common/conversions_dartium.dart |
+++ b/sdk/lib/html/html_common/conversions_dartium.dart |
@@ -75,3 +75,322 @@ Rectangle make_dart_rectangle(r) => |
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; |
+} |
+ |
+// 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) { |
+ var customElementClass = getCustomElementType(wrapper.blink_jsObject); |
+ if (wrapper.runtimeType != customElementClass && customElementClass != null) { |
+ if (wrapper.runtimeType == HtmlElement && !wrapper.isBadUpgrade) { |
+ // We're a Dart instance if it's HtmlElement and we have a customElement |
+ // class then we need to upgrade. |
+ if (customElementClass != null) { |
+ var dartClass_instance; |
+ try { |
+ dartClass_instance = _blink.Blink_Utils.constructElement(customElementClass, jsObject); |
+ } finally { |
+ dartClass_instance.blink_jsObject = jsObject; |
+ return dartClass_instance; |
+ } |
+ } |
+ } |
+ } |
+ |
+ return wrapper; |
+ } |
+ |
+ if (jsObject is js.JsArray) { |
+ var wrappingList = new DartHtmlWrappingList(jsObject); |
+ js.setDartHtmlWrapperFor(jsObject, wrappingList); |
+ return wrappingList; |
+ } |
+ |
+ // 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, jsObject); |
+ return jsObject; |
+ } |
+ var jsTypeName = js.JsNative.getProperty(constructor, 'name'); |
+ if (jsTypeName is! String || jsTypeName.length == 0) { |
+ // Not an html type. |
+ js.setDartHtmlWrapperFor(jsObject, jsObject); |
+ return jsObject; |
+ } |
+ |
+ var dartClass_instance; |
+ var customElementClass = null; |
+ var extendsTag = ""; |
+ var custom = getCustomElementEntry(jsObject); |
+ if (custom != null) { |
+ customElementClass = custom['type']; |
+ extendsTag = custom['extends']; |
+ } |
+ |
+ // Custom Element to upgrade. |
+ // Only allow custome elements to be created in the html or svg default |
+ // namespace. |
+ var defaultNS = jsObject['namespaceURI'] == 'http://www.w3.org/1999/xhtml' || |
+ jsObject['namespaceURI'] == 'http://www.w3.org/2000/svg'; |
+ if (customElementClass != null && extendsTag == "" && defaultNS) { |
+ try { |
+ dartClass_instance = _blink.Blink_Utils.constructElement(customElementClass, jsObject); |
+ } finally { |
+ dartClass_instance.blink_jsObject = jsObject; |
+ js.setDartHtmlWrapperFor(jsObject, dartClass_instance); |
+ } |
+ } else { |
+ var func = getHtmlCreateFunction(jsTypeName); |
+ if (func == null) { |
+ if (jsTypeName == 'auto-binding') { |
+ func = getHtmlCreateFunction("HTMLTemplateElement"); |
+ } else if (jsObject.toString() == "[object HTMLElement]") { |
+ // One last ditch effort could be a JS custom element. |
+ func = getHtmlCreateFunction("HTMLElement"); |
+ } |
+ } |
+ if (func != null) { |
+ dartClass_instance = func(); |
+ 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) { |
+ var wrappingList = new DartHtmlWrappingList(jsObject); |
+ js.setDartHtmlWrapperFor(jsObject, wrappingList); |
+ return wrappingList; |
+ } |
+ |
+ 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, jsObject); |
+ return jsObject; |
+ } |
+ var jsTypeName = js.JsNative.getProperty(constructor, 'name'); |
+ if (jsTypeName is! String || jsTypeName.length == 0) { |
+ // Not an html type. |
+ js.setDartHtmlWrapperFor(jsObject, jsObject); |
+ return jsObject; |
+ } |
+ |
+ var func = getHtmlCreateFunction(jsTypeName); |
+ if (func != null) { |
+ var dartClass_instance = func(); |
+ dartClass_instance.blink_jsObject = jsObject; |
+ js.setDartHtmlWrapperFor(jsObject, dartClass_instance); |
+ return dartClass_instance; |
+ } |
+ return jsObject; |
+ } 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.JsObjectImpl) { |
+ // 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/JsObjectImpl.'); |
+ } |
+ |
+ 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; |
+} |
+ |
+/** |
+ * Wraps a JsArray and will call wrap_jso on its entries. |
+ */ |
+class DartHtmlWrappingList extends ListBase implements NativeFieldWrapperClass2 { |
+ DartHtmlWrappingList(this.blink_jsObject); |
+ |
+ final js.JsArray blink_jsObject; |
+ |
+ operator [](int index) => wrap_jso(js.JsNative.getArrayIndex(blink_jsObject, index)); |
+ |
+ operator []=(int index, value) => blink_jsObject[index] = value; |
+ |
+ int get length => blink_jsObject.length; |
+ int set length(int newLength) => blink_jsObject.length = newLength; |
+} |