From a9362610ee429a0b69653635039696f86976f564 Mon Sep 17 00:00:00 2001 From: Alan Knight Date: Mon, 19 Oct 2015 11:28:53 -0700 Subject: [PATCH] Hide internal methods like wrap_jso from the dart:html public interface Issue https://github.com/dart-lang/sdk/issues/24602 R=terry@google.com Review URL: https://codereview.chromium.org/1409743003 . --- sdk/lib/html/dartium/html_dartium.dart | 359 +----------------- .../html/html_common/conversions_dart2js.dart | 6 + .../html/html_common/conversions_dartium.dart | 319 ++++++++++++++++ sdk/lib/html/html_common/html_common.dart | 1 + sdk/lib/js/dartium/js_dartium.dart | 7 +- tests/html/wrapping_collections_test.dart | 1 + .../dom/src/dartium_CustomElementSupport.dart | 6 +- .../html/dartium/html_dartium.darttemplate | 345 +---------------- .../html/impl/impl_HTMLDocument.darttemplate | 4 +- .../html/impl/impl_HTMLElement.darttemplate | 11 +- 10 files changed, 370 insertions(+), 689 deletions(-) diff --git a/sdk/lib/html/dartium/html_dartium.dart b/sdk/lib/html/dartium/html_dartium.dart index 142e5e750c1..73e0a053f3a 100644 --- a/sdk/lib/html/dartium/html_dartium.dart +++ b/sdk/lib/html/dartium/html_dartium.dart @@ -1107,71 +1107,14 @@ Function _getSvgFunction(String key) { ********** ********** ******************************************************************************/ -// List of known tagName to DartClass for custom elements, used for upgrade. -var _knownCustomElements = new Map>(); - -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; -} - String _getCustomElementExtends(object) { - var entry = _getCustomElementEntry(object); + var entry = getCustomElementEntry(object); if (entry != null) { return entry['extends']; } 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; -} - // Return the tag name or is attribute of the custom element or data binding. String _getCustomElementName(element) { var jsObject; @@ -1210,242 +1153,14 @@ class DartHtmlDomObject { } -// Flag to disable JS interop asserts. Setting to false will speed up the -// wrap_jso calls. -bool __interop_checks = false; - -/** Expando for JsObject, used by every Dart class associated with a Javascript - * class (e.g., DOM, WebAudio, etc.). - */ - -/** - * Return the JsObject associated with a Dart class [dartClass_instance]. - */ -unwrap_jso(dartClass_instance) => js.unwrap_jso(dartClass_instance); - -/** - * 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(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; - } -} - -// Upgrade a Dart HtmlElement to the user's Dart custom element class. +/// Upgrade a Dart HtmlElement to the user's Dart custom element class. _upgradeHtmlElement(dartInstance) { // Only try upgrading HtmlElement (Dart class) if there is a failure then // don't try it again - one failure is enough. - if (dartInstance.runtimeType == HtmlElement && !dartInstance._isBadUpgrade) { + if (dartInstance.runtimeType == HtmlElement && !dartInstance.isBadUpgrade) { // Must be exactly HtmlElement not something derived from it. - var customElementClass = _getCustomElementType(dartInstance); + var customElementClass = getCustomElementType(dartInstance); // Custom Element to upgrade. if (customElementClass != null) { @@ -1501,53 +1216,10 @@ Map convertNativeObjectToDartMap(js.JsObject jsObject) { return result; } -// 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; -} - -// Converts a Dart list into a JsArray. For the Dartium version only. -convertDartToNative_List(List input) => new js.JsArray()..addAll(input); - -// Conversion function place holder (currently not used in dart2js or dartium). -List convertDartToNative_StringArray(List input) => input; - -/** - * 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; -} - /** * Upgrade the JS HTMLElement to the Dart class. Used by Dart's Polymer. */ -createCustomUpgrader(Type customElementClass, $this) { +_createCustomUpgrader(Type customElementClass, $this) { var dartClass; try { dartClass = _blink.Blink_Utils.constructElement(customElementClass, $this); @@ -20514,7 +20186,7 @@ class HtmlDocument extends Document { var nativeElement = document.createElement(extendsTag); // Trying to extend a native element is it the Dart class consistent with the - // extendsTag? + // extendsTag? if (nativeElement.runtimeType != customClassType.reflectedType) { var nativeElementClassMirror = reflectClass(nativeElement.runtimeType); var customClassNativeElement = MirrorSystem.getName(customClassType.simpleName); @@ -20542,7 +20214,7 @@ class HtmlDocument extends Document { var elemProto = js.JsNative.callMethod(js.JsNative.getProperty(js.context, 'Object'), "create", [js.JsNative.getProperty(baseElement, 'prototype')]); // Remember for any upgrading done in wrap_jso. - _addCustomElementType(tag, customElementClass, extendsTag); + addCustomElementType(tag, customElementClass, extendsTag); // TODO(terry): Hack to stop recursion re-creating custom element when the // created() constructor of the custom element does e.g., @@ -21320,10 +20992,17 @@ class HtmlElement extends Element implements GlobalEventHandlers { @Experimental() // untriaged ElementStream get onWaiting => waitingEvent.forElement(this); - // Flags to only try upgrading once if there's a failure don't try upgrading + // Flags to only try upgrading once. If there's a failure don't try upgrading // anymore. bool _badUpgradeOccurred = false; - bool get _isBadUpgrade => _badUpgradeOccurred; + + /// Required for SDK Infrastructure. Internal use only. + /// + /// Did this encounter a failure attempting to upgrade to + /// a custom element. + @Deprecated("Required for SDK Infrastructure. Internal use only.") + bool get isBadUpgrade => _badUpgradeOccurred; + void _badUpgrade() { _badUpgradeOccurred = true; } } // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file @@ -47289,16 +46968,16 @@ class _VMElementUpgrader implements ElementUpgrader { // Element to extend is the real tag. tag = element.localName; - } else if (tag != null && element.localName != tag) { + } else if (tag != null && element.localName != tag) { throw new UnsupportedError('Element is incorrect type. Got ${element.runtimeType}, expected native Html or Svg element to extend.'); } else if (tag == null) { throw new UnsupportedError('Element is incorrect type. Got ${element.runtimeType}, expected HtmlElement/JsObjectImpl.'); } // Remember Dart class to tagName for any upgrading done in wrap_jso. - _addCustomElementType(tag, _type, _extendsTag); + addCustomElementType(tag, _type, _extendsTag); - return createCustomUpgrader(_type, jsObject); + return _createCustomUpgrader(_type, jsObject); } } diff --git a/sdk/lib/html/html_common/conversions_dart2js.dart b/sdk/lib/html/html_common/conversions_dart2js.dart index ac5b7554de3..3b3a8adf396 100644 --- a/sdk/lib/html/html_common/conversions_dart2js.dart +++ b/sdk/lib/html/html_common/conversions_dart2js.dart @@ -91,3 +91,9 @@ Future convertNativePromiseToDartFuture(promise) { var newPromise = JS('', '#.then(#).catch(#)', promise, then, error); return completer.future; } + +/// Wrap a JS object with an instance of the matching dart:html class. Used only in Dartium. +wrap_jso(jsObject) => jsObject; + +/// Find the underlying JS object for a dart:html Dart object. +unwrap_jso(dartClass_instance) => dartClass_instance; diff --git a/sdk/lib/html/html_common/conversions_dartium.dart b/sdk/lib/html/html_common/conversions_dartium.dart index 28225f089f6..1ed1e1348bc 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 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>(); + +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; +} diff --git a/sdk/lib/html/html_common/html_common.dart b/sdk/lib/html/html_common/html_common.dart index dfdfa27d0ce..c2e164497f6 100644 --- a/sdk/lib/html/html_common/html_common.dart +++ b/sdk/lib/html/html_common/html_common.dart @@ -9,6 +9,7 @@ import 'dart:collection'; import 'dart:html'; import 'dart:js' as js; import 'dart:_internal' show WhereIterable; +import 'dart:nativewrappers'; import 'dart:typed_data'; import 'dart:web_gl' as gl; diff --git a/sdk/lib/js/dartium/js_dartium.dart b/sdk/lib/js/dartium/js_dartium.dart index 13e70123072..26134dd084a 100644 --- a/sdk/lib/js/dartium/js_dartium.dart +++ b/sdk/lib/js/dartium/js_dartium.dart @@ -92,6 +92,7 @@ import 'dart:nativewrappers'; import 'dart:math' as math; import 'dart:mirrors' as mirrors; import 'dart:html' as html; +import 'dart:html_common' as html_common; import 'dart:indexed_db' as indexed_db; import 'dart:typed_data'; @@ -700,7 +701,7 @@ JsObject get context { } _maybeWrap(o) { - var wrapped = html.wrap_jso_no_SerializedScriptvalue(o); + var wrapped = html_common.wrap_jso_no_SerializedScriptvalue(o); if (identical(wrapped, o)) return o; return (wrapped is html.Blob || wrapped is html.Event @@ -819,7 +820,7 @@ class JsObject extends NativeFieldWrapperClass2 { static JsObject _jsify(object) native "JsObject_jsify"; - static JsObject _fromBrowserObject(object) => html.unwrap_jso(object); + static JsObject _fromBrowserObject(object) => html_common.unwrap_jso(object); /** * Returns the value associated with [property] from the proxied JavaScript @@ -858,7 +859,7 @@ class JsObject extends NativeFieldWrapperClass2 { operator ==(other) { var is_JsObject = other is JsObject; if (!is_JsObject) { - other = html.unwrap_jso(other); + other = html_common.unwrap_jso(other); is_JsObject = other is JsObject; } return is_JsObject && _identityEquality(this, other); diff --git a/tests/html/wrapping_collections_test.dart b/tests/html/wrapping_collections_test.dart index dc845083c28..176bcad308f 100644 --- a/tests/html/wrapping_collections_test.dart +++ b/tests/html/wrapping_collections_test.dart @@ -1,6 +1,7 @@ library wrapping_collection_test; import 'dart:html'; +import 'dart:html_common'; import 'dart:js' as js; import 'package:unittest/unittest.dart'; import 'package:unittest/html_config.dart'; diff --git a/tools/dom/src/dartium_CustomElementSupport.dart b/tools/dom/src/dartium_CustomElementSupport.dart index c91ab720340..aec7b4af1dc 100644 --- a/tools/dom/src/dartium_CustomElementSupport.dart +++ b/tools/dom/src/dartium_CustomElementSupport.dart @@ -53,16 +53,16 @@ class _VMElementUpgrader implements ElementUpgrader { // Element to extend is the real tag. tag = element.localName; - } else if (tag != null && element.localName != tag) { + } else if (tag != null && element.localName != tag) { throw new UnsupportedError('Element is incorrect type. Got ${element.runtimeType}, expected native Html or Svg element to extend.'); } else if (tag == null) { throw new UnsupportedError('Element is incorrect type. Got ${element.runtimeType}, expected HtmlElement/JsObjectImpl.'); } // Remember Dart class to tagName for any upgrading done in wrap_jso. - _addCustomElementType(tag, _type, _extendsTag); + addCustomElementType(tag, _type, _extendsTag); - return createCustomUpgrader(_type, jsObject); + return _createCustomUpgrader(_type, jsObject); } } diff --git a/tools/dom/templates/html/dartium/html_dartium.darttemplate b/tools/dom/templates/html/dartium/html_dartium.darttemplate index 70fd100ef88..4dc5fc93615 100644 --- a/tools/dom/templates/html/dartium/html_dartium.darttemplate +++ b/tools/dom/templates/html/dartium/html_dartium.darttemplate @@ -352,71 +352,14 @@ Function _getSvgFunction(String key) { ********** ********** ******************************************************************************/ -// List of known tagName to DartClass for custom elements, used for upgrade. -var _knownCustomElements = new Map>(); - -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; -} - String _getCustomElementExtends(object) { - var entry = _getCustomElementEntry(object); + var entry = getCustomElementEntry(object); if (entry != null) { return entry['extends']; } 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; -} - // Return the tag name or is attribute of the custom element or data binding. String _getCustomElementName(element) { var jsObject; @@ -455,242 +398,14 @@ class DartHtmlDomObject { } -// Flag to disable JS interop asserts. Setting to false will speed up the -// wrap_jso calls. -bool __interop_checks = false; - -/** Expando for JsObject, used by every Dart class associated with a Javascript - * class (e.g., DOM, WebAudio, etc.). - */ - -/** - * Return the JsObject associated with a Dart class [dartClass_instance]. - */ -unwrap_jso(dartClass_instance) => js.unwrap_jso(dartClass_instance); - -/** - * 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(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; - } -} - -// Upgrade a Dart HtmlElement to the user's Dart custom element class. +/// Upgrade a Dart HtmlElement to the user's Dart custom element class. _upgradeHtmlElement(dartInstance) { // Only try upgrading HtmlElement (Dart class) if there is a failure then // don't try it again - one failure is enough. - if (dartInstance.runtimeType == HtmlElement && !dartInstance._isBadUpgrade) { + if (dartInstance.runtimeType == HtmlElement && !dartInstance.isBadUpgrade) { // Must be exactly HtmlElement not something derived from it. - var customElementClass = _getCustomElementType(dartInstance); + var customElementClass = getCustomElementType(dartInstance); // Custom Element to upgrade. if (customElementClass != null) { @@ -746,53 +461,10 @@ Map convertNativeObjectToDartMap(js.JsObject jsObject) { return result; } -// 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; -} - -// Converts a Dart list into a JsArray. For the Dartium version only. -convertDartToNative_List(List input) => new js.JsArray()..addAll(input); - -// Conversion function place holder (currently not used in dart2js or dartium). -List convertDartToNative_StringArray(List input) => input; - -/** - * 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; -} - /** * Upgrade the JS HTMLElement to the Dart class. Used by Dart's Polymer. */ -createCustomUpgrader(Type customElementClass, $this) { +_createCustomUpgrader(Type customElementClass, $this) { var dartClass; try { dartClass = _blink.Blink_Utils.constructElement(customElementClass, $this); @@ -812,11 +484,6 @@ createCustomUpgrader(Type customElementClass, $this) { $else class DartHtmlDomObject extends NativeFieldWrapperClass2 {} -unwrap_jso(dartClass_instance) => dartClass_instance; -wrap_jso(jsObject) => jsObject; -convertDartToNative_Dictionary(Map dict) => dict; -List convertDartToNative_StringArray(List input) => input; -convertDartToNative_List(List input) => input; -createCustomUpgrader(Type customElementClass, $this) => $this; +_createCustomUpgrader(Type customElementClass, $this) => $this; $endif diff --git a/tools/dom/templates/html/impl/impl_HTMLDocument.darttemplate b/tools/dom/templates/html/impl/impl_HTMLDocument.darttemplate index 959c4949322..1df549ea47f 100644 --- a/tools/dom/templates/html/impl/impl_HTMLDocument.darttemplate +++ b/tools/dom/templates/html/impl/impl_HTMLDocument.darttemplate @@ -394,7 +394,7 @@ $else var nativeElement = document.createElement(extendsTag); // Trying to extend a native element is it the Dart class consistent with the - // extendsTag? + // extendsTag? if (nativeElement.runtimeType != customClassType.reflectedType) { var nativeElementClassMirror = reflectClass(nativeElement.runtimeType); var customClassNativeElement = MirrorSystem.getName(customClassType.simpleName); @@ -422,7 +422,7 @@ $else var elemProto = js.JsNative.callMethod(js.JsNative.getProperty(js.context, 'Object'), "create", [js.JsNative.getProperty(baseElement, 'prototype')]); // Remember for any upgrading done in wrap_jso. - _addCustomElementType(tag, customElementClass, extendsTag); + addCustomElementType(tag, customElementClass, extendsTag); // TODO(terry): Hack to stop recursion re-creating custom element when the // created() constructor of the custom element does e.g., diff --git a/tools/dom/templates/html/impl/impl_HTMLElement.darttemplate b/tools/dom/templates/html/impl/impl_HTMLElement.darttemplate index fdc305fd37b..1d126319e53 100644 --- a/tools/dom/templates/html/impl/impl_HTMLElement.darttemplate +++ b/tools/dom/templates/html/impl/impl_HTMLElement.darttemplate @@ -8,10 +8,17 @@ part of $LIBRARYNAME; $(ANNOTATIONS)$(NATIVESPEC)$(CLASS_MODIFIERS)class $CLASSNAME$EXTENDS$IMPLEMENTS { $!MEMBERS $if DARTIUM - // Flags to only try upgrading once if there's a failure don't try upgrading + // Flags to only try upgrading once. If there's a failure don't try upgrading // anymore. bool _badUpgradeOccurred = false; - bool get _isBadUpgrade => _badUpgradeOccurred; + + /// Required for SDK Infrastructure. Internal use only. + /// + /// Did this encounter a failure attempting to upgrade to + /// a custom element. + @Deprecated("Required for SDK Infrastructure. Internal use only.") + bool get isBadUpgrade => _badUpgradeOccurred; + void _badUpgrade() { _badUpgradeOccurred = true; } $endif }