blob: e8cbb907da5a65396e5d5d39c25ea641deb03acb [file] [log] [blame]
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
part of polymer;
/** Annotation used to automatically register polymer elements. */
class CustomTag {
final String tagName;
const CustomTag(this.tagName);
}
/**
* Metadata used to label static or top-level methods that are called
* automatically when loading the library of a custom element.
*/
const initMethod = const _InitMethodAnnotation();
/**
* Initializes a polymer application as follows:
* * set up up polling for observable changes
* * initialize MDV
* * for each library in [libraries], register custom elements labeled with
* [CustomTag] and invoke the initialization method on it.
*
* The initialization on each library is either a method named `main` or
* a top-level function and annotated with [initMethod].
*
* The urls in [libraries] can be absolute or relative to [srcUrl].
*/
void initPolymer(List<String> libraries, [String srcUrl]) {
runMicrotask(() {
// DOM events don't yet go through microtasks, so we catch those here.
new Timer.periodic(new Duration(milliseconds: 125),
(_) => performMicrotaskCheckpoint());
// TODO(jmesserly): mdv should use initMdv instead of mdv.initialize.
mdv.initialize();
registerCustomElement('polymer-element', () => new PolymerDeclaration());
for (var lib in libraries) {
_loadLibrary(lib, srcUrl);
}
// TODO(jmesserly): this should be in the custom_element polyfill, not here.
// notify the system that we are bootstrapped
document.body.dispatchEvent(
new CustomEvent('WebComponentsReady', canBubble: true));
});
}
/** All libraries in the current isolate. */
final _libs = currentMirrorSystem().libraries;
/**
* Reads the library at [uriString] (which can be an absolute URI or a relative
* URI from [srcUrl]), and:
*
* * If present, invokes `main`.
*
* * If present, invokes any top-level and static functions marked
* with the [initMethod] annotation (in the order they appear).
*
* * Registers any [PolymerElement] that is marked with the [CustomTag]
* annotation.
*/
void _loadLibrary(String uriString, [String srcUrl]) {
var uri = Uri.parse(uriString);
if (uri.scheme == '' && srcUrl != null) {
uri = Uri.parse(path.normalize(path.join(path.dirname(srcUrl), uriString)));
}
var lib = _libs[uri];
if (lib == null) {
print('warning: $uri library not found');
return;
}
// Invoke `main`, if present.
if (lib.functions[const Symbol('main')] != null) {
lib.invoke(const Symbol('main'), const []);
}
// Search top-level functions marked with @initMethod
for (var f in lib.functions.values) {
_maybeInvoke(lib, f);
}
for (var c in lib.classes.values) {
// Search for @CustomTag on classes
for (var m in c.metadata) {
var meta = m.reflectee;
if (meta is CustomTag) {
Polymer._registerClassMirror(meta.tagName, c);
}
}
// TODO(sigmund): check also static methods marked with @initMethod.
// This is blocked on two bugs:
// - dartbug.com/12133 (static methods are incorrectly listed as top-level
// in dart2js, so they end up being called twice)
// - dartbug.com/12134 (sometimes "method.metadata" throws an exception,
// we could wrap and hide those exceptions, but it's not ideal).
}
}
void _maybeInvoke(ObjectMirror obj, MethodMirror method) {
var annotationFound = false;
for (var meta in method.metadata) {
if (identical(meta.reflectee, initMethod)) {
annotationFound = true;
break;
}
}
if (!annotationFound) return;
if (!method.isStatic) {
print("warning: methods marked with @initMethod should be static,"
" ${method.simpleName} is not.");
return;
}
if (!method.parameters.where((p) => !p.isOptional).isEmpty) {
print("warning: methods marked with @initMethod should take no "
"arguments, ${method.simpleName} expects some.");
return;
}
obj.invoke(method.simpleName, const []);
}
class _InitMethodAnnotation {
const _InitMethodAnnotation();
}