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 .
This commit is contained in:
Alan Knight 2015-10-19 11:28:53 -07:00
parent 568484d106
commit a9362610ee
10 changed files with 370 additions and 689 deletions

View file

@ -1107,71 +1107,14 @@ Function _getSvgFunction(String key) {
********** **********
******************************************************************************/
// 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;
}
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<String, dynamic> 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<String> 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<Event> 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);
}
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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;

View file

@ -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);

View file

@ -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';

View file

@ -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);
}
}

View file

@ -352,71 +352,14 @@ Function _getSvgFunction(String key) {
********** **********
******************************************************************************/
// 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;
}
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<String, dynamic> 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<String> 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<String> input) => input;
convertDartToNative_List(List input) => input;
createCustomUpgrader(Type customElementClass, $this) => $this;
_createCustomUpgrader(Type customElementClass, $this) => $this;
$endif

View file

@ -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.,

View file

@ -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
}