| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of polymer; | 5 part of polymer; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * **Warning**: this class is experiental and subject to change. | 8 * **Warning**: this class is experiental and subject to change. |
| 9 * | 9 * |
| 10 * The implementation for the `polymer-element` element. | 10 * The implementation for the `polymer-element` element. |
| (...skipping 29 matching lines...) Expand all Loading... |
| 40 // each level of the __proto__ like JS does. | 40 // each level of the __proto__ like JS does. |
| 41 PolymerDeclaration _super; | 41 PolymerDeclaration _super; |
| 42 PolymerDeclaration get superDeclaration => _super; | 42 PolymerDeclaration get superDeclaration => _super; |
| 43 | 43 |
| 44 String _extendsName; | 44 String _extendsName; |
| 45 | 45 |
| 46 String _name; | 46 String _name; |
| 47 String get name => _name; | 47 String get name => _name; |
| 48 | 48 |
| 49 /** | 49 /** |
| 50 * Map of publish properties. Can be a [VariableMirror] or a [MethodMirror] | 50 * Map of publish properties. Can be a field or a property getter, but if this |
| 51 * representing a getter. If it is a getter, there will also be a setter. | 51 * map contains a getter, is because it also has a corresponding setter. |
| 52 * | 52 * |
| 53 * Note: technically these are always single properties, so we could use | 53 * Note: technically these are always single properties, so we could use a |
| 54 * a Symbol instead of a PropertyPath. However there are lookups between | 54 * Symbol instead of a PropertyPath. However there are lookups between this |
| 55 * this map and [_observe] so it is easier to just track paths. | 55 * map and [_observe] so it is easier to just track paths. |
| 56 */ | 56 */ |
| 57 Map<PropertyPath, DeclarationMirror> _publish; | 57 Map<PropertyPath, smoke.Declaration> _publish; |
| 58 | 58 |
| 59 /** The names of published properties for this polymer-element. */ | 59 /** The names of published properties for this polymer-element. */ |
| 60 Iterable<String> get publishedProperties => | 60 Iterable<String> get publishedProperties => |
| 61 _publish != null ? _publish.keys.map((p) => '$p') : const []; | 61 _publish != null ? _publish.keys.map((p) => '$p') : const []; |
| 62 | 62 |
| 63 /** Same as [_publish] but with lower case names. */ | 63 /** Same as [_publish] but with lower case names. */ |
| 64 Map<String, DeclarationMirror> _publishLC; | 64 Map<String, smoke.Declaration> _publishLC; |
| 65 | 65 |
| 66 Map<PropertyPath, List<Symbol>> _observe; | 66 Map<PropertyPath, List<Symbol>> _observe; |
| 67 | 67 |
| 68 Map<String, Object> _instanceAttributes; | 68 Map<String, Object> _instanceAttributes; |
| 69 | 69 |
| 70 List<Element> _sheets; | 70 List<Element> _sheets; |
| 71 List<Element> get sheets => _sheets; | 71 List<Element> get sheets => _sheets; |
| 72 | 72 |
| 73 List<Element> _styles; | 73 List<Element> _styles; |
| 74 List<Element> get styles => _styles; | 74 List<Element> get styles => _styles; |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 182 * prototype, which simplifies this method. | 182 * prototype, which simplifies this method. |
| 183 */ | 183 */ |
| 184 void buildType(String name, String extendee) { | 184 void buildType(String name, String extendee) { |
| 185 // get our custom type | 185 // get our custom type |
| 186 _type = _getRegisteredType(name); | 186 _type = _getRegisteredType(name); |
| 187 | 187 |
| 188 // get basal prototype | 188 // get basal prototype |
| 189 _supertype = _getRegisteredType(extendee); | 189 _supertype = _getRegisteredType(extendee); |
| 190 if (_supertype != null) _super = _getDeclaration(extendee); | 190 if (_supertype != null) _super = _getDeclaration(extendee); |
| 191 | 191 |
| 192 var cls = reflectClass(_type); | 192 // transcribe `attributes` declarations onto own prototype's `publish` |
| 193 publishAttributes(_super); |
| 193 | 194 |
| 194 // transcribe `attributes` declarations onto own prototype's `publish` | 195 publishProperties(); |
| 195 publishAttributes(cls, _super); | |
| 196 | 196 |
| 197 publishProperties(_type); | 197 inferObservers(); |
| 198 | |
| 199 inferObservers(cls); | |
| 200 | 198 |
| 201 // desugar compound observer syntax, e.g. @ObserveProperty('a b c') | 199 // desugar compound observer syntax, e.g. @ObserveProperty('a b c') |
| 202 explodeObservers(cls); | 200 explodeObservers(); |
| 203 | 201 |
| 204 // Skip the rest in Dart: | 202 // Skip the rest in Dart: |
| 205 // chain various meta-data objects to inherited versions | 203 // chain various meta-data objects to inherited versions |
| 206 // chain custom api to inherited | 204 // chain custom api to inherited |
| 207 // build side-chained lists to optimize iterations | 205 // build side-chained lists to optimize iterations |
| 208 // inherit publishing meta-data | 206 // inherit publishing meta-data |
| 209 // x-platform fixup | 207 // x-platform fixup |
| 210 } | 208 } |
| 211 | 209 |
| 212 /** Implement various declarative features. */ | 210 /** Implement various declarative features. */ |
| (...skipping 10 matching lines...) Expand all Loading... |
| 223 // TODO(sorvell): install a helper method this.resolvePath to aid in | 221 // TODO(sorvell): install a helper method this.resolvePath to aid in |
| 224 // setting resource paths. e.g. | 222 // setting resource paths. e.g. |
| 225 // this.$.image.src = this.resolvePath('images/foo.png') | 223 // this.$.image.src = this.resolvePath('images/foo.png') |
| 226 // Potentially remove when spec bug is addressed. | 224 // Potentially remove when spec bug is addressed. |
| 227 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=21407 | 225 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=21407 |
| 228 // TODO(jmesserly): resolvePath not ported, see first comment in this class. | 226 // TODO(jmesserly): resolvePath not ported, see first comment in this class. |
| 229 | 227 |
| 230 // under ShadowDOMPolyfill, transforms to approximate missing CSS features | 228 // under ShadowDOMPolyfill, transforms to approximate missing CSS features |
| 231 _shimShadowDomStyling(templateContent, name, extendee); | 229 _shimShadowDomStyling(templateContent, name, extendee); |
| 232 | 230 |
| 233 var cls = reflectClass(type); | |
| 234 // TODO(jmesserly): this feels unnatrual in Dart. Since we have convenient | 231 // TODO(jmesserly): this feels unnatrual in Dart. Since we have convenient |
| 235 // lazy static initialization, can we get by without it? | 232 // lazy static initialization, can we get by without it? |
| 236 var registered = cls.declarations[#registerCallback]; | 233 if (smoke.hasStaticMethod(type, #registerCallback)) { |
| 237 if (registered != null && | 234 smoke.invoke(type, #registerCallback, [this]); |
| 238 registered is MethodMirror && | |
| 239 registered.isStatic && | |
| 240 registered.isRegularMethod) { | |
| 241 cls.invoke(#registerCallback, [this]); | |
| 242 } | 235 } |
| 243 } | 236 } |
| 244 | 237 |
| 245 // TODO(sorvell): remove when spec addressed: | 238 // TODO(sorvell): remove when spec addressed: |
| 246 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=22460 | 239 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=22460 |
| 247 // make <shadow></shadow> be <shadow><content></content></shadow> | 240 // make <shadow></shadow> be <shadow><content></content></shadow> |
| 248 void adjustShadowElement() { | 241 void adjustShadowElement() { |
| 249 // TODO(sorvell): avoid under SD polyfill until this bug is addressed: | 242 // TODO(sorvell): avoid under SD polyfill until this bug is addressed: |
| 250 // https://github.com/Polymer/ShadowDOM/issues/297 | 243 // https://github.com/Polymer/ShadowDOM/issues/297 |
| 251 if (!_hasShadowDomPolyfill) { | 244 if (!_hasShadowDomPolyfill) { |
| 252 final content = templateContent; | 245 final content = templateContent; |
| 253 if (content == null) return; | 246 if (content == null) return; |
| 254 | 247 |
| 255 for (var s in content.querySelectorAll('shadow')) { | 248 for (var s in content.querySelectorAll('shadow')) { |
| 256 if (s.nodes.isEmpty) s.append(new ContentElement()); | 249 if (s.nodes.isEmpty) s.append(new ContentElement()); |
| 257 } | 250 } |
| 258 } | 251 } |
| 259 } | 252 } |
| 260 | 253 |
| 261 void registerType(String name) { | 254 void registerType(String name) { |
| 262 var baseTag; | 255 var baseTag; |
| 263 var decl = this; | 256 var decl = this; |
| 264 while (decl != null) { | 257 while (decl != null) { |
| 265 baseTag = decl.attributes['extends']; | 258 baseTag = decl.attributes['extends']; |
| 266 decl = decl.superDeclaration; | 259 decl = decl.superDeclaration; |
| 267 } | 260 } |
| 268 document.register(name, type, extendsTag: baseTag); | 261 document.register(name, type, extendsTag: baseTag); |
| 269 } | 262 } |
| 270 | 263 |
| 271 void publishAttributes(ClassMirror cls, PolymerDeclaration superDecl) { | 264 void publishAttributes(PolymerDeclaration superDecl) { |
| 272 // get properties to publish | 265 // get properties to publish |
| 273 if (superDecl != null && superDecl._publish != null) { | 266 if (superDecl != null && superDecl._publish != null) { |
| 274 // Dart note: even though we walk the type hierarchy in | 267 // Dart note: even though we walk the type hierarchy in |
| 275 // _getPublishedProperties, this will additionally include any names | 268 // _getPublishedProperties, this will additionally include any names |
| 276 // published via the `attributes` attribute. | 269 // published via the `attributes` attribute. |
| 277 _publish = new Map.from(superDecl._publish); | 270 _publish = new Map.from(superDecl._publish); |
| 278 } | 271 } |
| 279 | 272 |
| 280 _publish = _getPublishedProperties(cls, _publish); | 273 _publish = _getPublishedProperties(_type, _publish); |
| 281 | 274 |
| 282 // merge names from 'attributes' attribute | 275 // merge names from 'attributes' attribute |
| 283 var attrs = attributes['attributes']; | 276 var attrs = attributes['attributes']; |
| 284 if (attrs != null) { | 277 if (attrs != null) { |
| 285 // names='a b c' or names='a,b,c' | 278 // names='a b c' or names='a,b,c' |
| 286 // record each name for publishing | 279 // record each name for publishing |
| 287 for (var attr in attrs.split(_ATTRIBUTES_REGEX)) { | 280 for (var attr in attrs.split(_ATTRIBUTES_REGEX)) { |
| 288 // remove excess ws | 281 // remove excess ws |
| 289 attr = attr.trim(); | 282 attr = attr.trim(); |
| 290 | 283 |
| 291 // do not override explicit entries | 284 // do not override explicit entries |
| 292 if (attr == '') continue; | 285 if (attr == '') continue; |
| 293 | 286 |
| 294 var property = new Symbol(attr); | 287 var property = new Symbol(attr); |
| 295 var path = new PropertyPath([property]); | 288 var path = new PropertyPath([property]); |
| 296 if (_publish != null && _publish.containsKey(path)) { | 289 if (_publish != null && _publish.containsKey(path)) { |
| 297 continue; | 290 continue; |
| 298 } | 291 } |
| 299 | 292 |
| 300 var mirror = _getProperty(cls, property); | 293 var decl = smoke.getDeclaration(_type, property); |
| 301 if (mirror == null) { | 294 if (decl == null || !decl.isProperty || decl.isFinal) { |
| 302 window.console.warn('property for attribute $attr of polymer-element ' | 295 window.console.warn('property for attribute $attr of polymer-element ' |
| 303 'name=$name not found.'); | 296 'name=$name not found.'); |
| 304 continue; | 297 continue; |
| 305 } | 298 } |
| 306 if (_publish == null) _publish = {}; | 299 if (_publish == null) _publish = {}; |
| 307 _publish[path] = mirror; | 300 _publish[path] = decl; |
| 308 } | 301 } |
| 309 } | 302 } |
| 310 | 303 |
| 311 // NOTE: the following is not possible in Dart; fields must be declared. | 304 // NOTE: the following is not possible in Dart; fields must be declared. |
| 312 // install 'attributes' as properties on the prototype, | 305 // install 'attributes' as properties on the prototype, |
| 313 // but don't override | 306 // but don't override |
| 314 } | 307 } |
| 315 | 308 |
| 316 void accumulateInstanceAttributes() { | 309 void accumulateInstanceAttributes() { |
| 317 // inherit instance attributes | 310 // inherit instance attributes |
| (...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 453 | 446 |
| 454 return new StyleElement() | 447 return new StyleElement() |
| 455 ..text = cssText | 448 ..text = cssText |
| 456 ..attributes[_STYLE_SCOPE_ATTRIBUTE] = '$name-$scopeDescriptor'; | 449 ..attributes[_STYLE_SCOPE_ATTRIBUTE] = '$name-$scopeDescriptor'; |
| 457 } | 450 } |
| 458 | 451 |
| 459 /** | 452 /** |
| 460 * Fetch a list of all *Changed methods so we can observe the associated | 453 * Fetch a list of all *Changed methods so we can observe the associated |
| 461 * properties. | 454 * properties. |
| 462 */ | 455 */ |
| 463 void inferObservers(ClassMirror cls) { | 456 void inferObservers() { |
| 464 if (cls == _htmlElementType) return; | 457 var options = const smoke.QueryOptions(includeProperties: false, |
| 465 inferObservers(cls.superclass); | 458 includeMethods: true, includeInherited: true); |
| 466 for (var method in cls.declarations.values) { | 459 for (var decl in smoke.query(_type, options)) { |
| 467 if (method is! MethodMirror || method.isStatic | 460 String name = smoke.symbolToName(decl.name); |
| 468 || !method.isRegularMethod) continue; | |
| 469 | |
| 470 String name = MirrorSystem.getName(method.simpleName); | |
| 471 if (name.endsWith(_OBSERVE_SUFFIX) && name != 'attributeChanged') { | 461 if (name.endsWith(_OBSERVE_SUFFIX) && name != 'attributeChanged') { |
| 472 // TODO(jmesserly): now that we have a better system, should we | 462 // TODO(jmesserly): now that we have a better system, should we |
| 473 // deprecate *Changed methods? | 463 // deprecate *Changed methods? |
| 474 if (_observe == null) _observe = new HashMap(); | 464 if (_observe == null) _observe = new HashMap(); |
| 475 name = name.substring(0, name.length - 7); | 465 name = name.substring(0, name.length - 7); |
| 476 _observe[new PropertyPath(name)] = [method.simpleName]; | 466 _observe[new PropertyPath(name)] = [decl.name]; |
| 477 } | 467 } |
| 478 } | 468 } |
| 479 } | 469 } |
| 480 | 470 |
| 481 /** | 471 /** |
| 482 * Fetch a list of all methods annotated with [ObserveProperty] so we can | 472 * Fetch a list of all methods annotated with [ObserveProperty] so we can |
| 483 * observe the associated properties. | 473 * observe the associated properties. |
| 484 */ | 474 */ |
| 485 void explodeObservers(ClassMirror cls) { | 475 void explodeObservers() { |
| 486 if (cls == _htmlElementType) return; | 476 var options = const smoke.QueryOptions(includeProperties: false, |
| 487 | 477 includeMethods: true, includeInherited: true, |
| 488 explodeObservers(cls.superclass); | 478 withAnnotations: const [ObserveProperty]); |
| 489 for (var method in cls.declarations.values) { | 479 for (var decl in smoke.query(_type, options)) { |
| 490 if (method is! MethodMirror || method.isStatic | 480 for (var meta in decl.annotations) { |
| 491 || !method.isRegularMethod) continue; | 481 if (meta is! ObserveProperty) continue; |
| 492 | |
| 493 for (var meta in method.metadata) { | |
| 494 if (meta.reflectee is! ObserveProperty) continue; | |
| 495 | |
| 496 if (_observe == null) _observe = new HashMap(); | 482 if (_observe == null) _observe = new HashMap(); |
| 497 | 483 for (String name in meta.names) { |
| 498 for (String name in meta.reflectee.names) { | 484 _observe.putIfAbsent(new PropertyPath(name), () => []).add(decl.name); |
| 499 _observe.putIfAbsent(new PropertyPath(name), () => []) | |
| 500 .add(method.simpleName); | |
| 501 } | 485 } |
| 502 } | 486 } |
| 503 } | 487 } |
| 504 } | 488 } |
| 505 | 489 |
| 506 void publishProperties(Type type) { | 490 void publishProperties() { |
| 507 // Dart note: _publish was already populated by publishAttributes | 491 // Dart note: _publish was already populated by publishAttributes |
| 508 if (_publish != null) _publishLC = _lowerCaseMap(_publish); | 492 if (_publish != null) _publishLC = _lowerCaseMap(_publish); |
| 509 } | 493 } |
| 510 | 494 |
| 511 Map<String, dynamic> _lowerCaseMap(Map<PropertyPath, dynamic> properties) { | 495 Map<String, dynamic> _lowerCaseMap(Map<PropertyPath, dynamic> properties) { |
| 512 final map = new Map<String, dynamic>(); | 496 final map = new Map<String, dynamic>(); |
| 513 properties.forEach((PropertyPath path, value) { | 497 properties.forEach((PropertyPath path, value) { |
| 514 map['$path'.toLowerCase()] = value; | 498 map['$path'.toLowerCase()] = value; |
| 515 }); | 499 }); |
| 516 return map; | 500 return map; |
| (...skipping 24 matching lines...) Expand all Loading... |
| 541 } | 525 } |
| 542 } | 526 } |
| 543 } | 527 } |
| 544 | 528 |
| 545 /// track document.register'ed tag names and their declarations | 529 /// track document.register'ed tag names and their declarations |
| 546 final Map _declarations = new Map<String, PolymerDeclaration>(); | 530 final Map _declarations = new Map<String, PolymerDeclaration>(); |
| 547 | 531 |
| 548 bool _isRegistered(String name) => _declarations.containsKey(name); | 532 bool _isRegistered(String name) => _declarations.containsKey(name); |
| 549 PolymerDeclaration _getDeclaration(String name) => _declarations[name]; | 533 PolymerDeclaration _getDeclaration(String name) => _declarations[name]; |
| 550 | 534 |
| 551 final _objectType = reflectClass(Object); | 535 Map<PropertyPath, smoke.Declaration> _getPublishedProperties( |
| 552 final _htmlElementType = reflectClass(HtmlElement); | 536 Type type, Map<PropertyPath, smoke.Declaration> props) { |
| 553 | 537 var options = const smoke.QueryOptions(includeInherited: true, |
| 554 Map _getPublishedProperties(ClassMirror cls, Map props) { | 538 withAnnotations: const [PublishedProperty]); |
| 555 if (cls == _htmlElementType) return props; | 539 for (var decl in smoke.query(type, options)) { |
| 556 props = _getPublishedProperties(cls.superclass, props); | 540 if (decl.isFinal) continue; |
| 557 for (var member in cls.declarations.values) { | 541 if (props == null) props = {}; |
| 558 if (member.isStatic || member.isPrivate) continue; | 542 props[new PropertyPath([decl.name])] = decl; |
| 559 | |
| 560 if (member is VariableMirror && !member.isFinal | |
| 561 || member is MethodMirror && member.isGetter) { | |
| 562 | |
| 563 for (var meta in member.metadata) { | |
| 564 if (meta.reflectee is PublishedProperty) { | |
| 565 // Note: we delay the setter check until we find @published because | |
| 566 // it's a tad expensive. | |
| 567 if (member is! MethodMirror || _hasSetter(cls, member)) { | |
| 568 if (props == null) props = {}; | |
| 569 props[new PropertyPath([member.simpleName])] = member; | |
| 570 } | |
| 571 break; | |
| 572 } | |
| 573 } | |
| 574 } | |
| 575 } | 543 } |
| 576 | |
| 577 return props; | 544 return props; |
| 578 } | 545 } |
| 579 | 546 |
| 580 DeclarationMirror _getProperty(ClassMirror cls, Symbol property) { | |
| 581 do { | |
| 582 var mirror = cls.declarations[property]; | |
| 583 if (mirror is MethodMirror && mirror.isGetter && _hasSetter(cls, mirror) | |
| 584 || mirror is VariableMirror) { | |
| 585 return mirror; | |
| 586 } | |
| 587 cls = cls.superclass; | |
| 588 | |
| 589 // It's generally a good idea to stop at Object, since we know it doesn't | |
| 590 // have what we want. | |
| 591 // TODO(jmesserly): This is also a workaround for what appears to be a V8 | |
| 592 // bug introduced between Chrome 31 and 32. After 32 | |
| 593 // JsClassMirror.declarations on Object calls | |
| 594 // JsClassMirror.typeVariables, which tries to get the _jsConstructor's | |
| 595 // .prototype["<>"]. This ends up getting the "" property instead, maybe | |
| 596 // because "<>" doesn't exist, and gets ";" which then blows up because | |
| 597 // the code later on expects a List of ints. | |
| 598 } while (cls != _objectType); | |
| 599 return null; | |
| 600 } | |
| 601 | |
| 602 bool _hasSetter(ClassMirror cls, MethodMirror getter) { | |
| 603 var setterName = new Symbol('${MirrorSystem.getName(getter.simpleName)}='); | |
| 604 var mirror = cls.declarations[setterName]; | |
| 605 return mirror is MethodMirror && mirror.isSetter; | |
| 606 } | |
| 607 | |
| 608 | |
| 609 /** Attribute prefix used for declarative event handlers. */ | 547 /** Attribute prefix used for declarative event handlers. */ |
| 610 const _EVENT_PREFIX = 'on-'; | 548 const _EVENT_PREFIX = 'on-'; |
| 611 | 549 |
| 612 /** Whether an attribute declares an event. */ | 550 /** Whether an attribute declares an event. */ |
| 613 bool _hasEventPrefix(String attr) => attr.startsWith(_EVENT_PREFIX); | 551 bool _hasEventPrefix(String attr) => attr.startsWith(_EVENT_PREFIX); |
| 614 | 552 |
| 615 String _removeEventPrefix(String name) => name.substring(_EVENT_PREFIX.length); | 553 String _removeEventPrefix(String name) => name.substring(_EVENT_PREFIX.length); |
| 616 | 554 |
| 617 /** | 555 /** |
| 618 * Using Polymer's platform/src/ShadowCSS.js passing the style tag's content. | 556 * Using Polymer's platform/src/ShadowCSS.js passing the style tag's content. |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 706 | 644 |
| 707 // Dart note: we need this function because we have additional renames JS does | 645 // Dart note: we need this function because we have additional renames JS does |
| 708 // not have. The JS renames are simply case differences, whereas we have ones | 646 // not have. The JS renames are simply case differences, whereas we have ones |
| 709 // like doubleclick -> dblclick and stripping the webkit prefix. | 647 // like doubleclick -> dblclick and stripping the webkit prefix. |
| 710 String _eventNameFromType(String eventType) { | 648 String _eventNameFromType(String eventType) { |
| 711 final result = _reverseEventTranslations[eventType]; | 649 final result = _reverseEventTranslations[eventType]; |
| 712 return result != null ? result : eventType; | 650 return result != null ? result : eventType; |
| 713 } | 651 } |
| 714 | 652 |
| 715 final _ATTRIBUTES_REGEX = new RegExp(r'\s|,'); | 653 final _ATTRIBUTES_REGEX = new RegExp(r'\s|,'); |
| OLD | NEW |