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 |