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 /** Annotation used to automatically register polymer elements. */ | 7 /** Annotation used to automatically register polymer elements. */ |
8 class CustomTag { | 8 class CustomTag { |
9 final String tagName; | 9 final String tagName; |
10 const CustomTag(this.tagName); | 10 const CustomTag(this.tagName); |
11 } | 11 } |
12 | 12 |
13 /** | 13 /** |
14 * Metadata used to label static or top-level methods that are called | 14 * Metadata used to label static or top-level methods that are called |
15 * automatically when loading the library of a custom element. | 15 * automatically when loading the library of a custom element. |
16 */ | 16 */ |
17 const initMethod = const _InitMethodAnnotation(); | 17 const initMethod = const _InitMethodAnnotation(); |
18 | 18 |
19 /** | 19 /** |
20 * Initializes a polymer application as follows: | 20 * Initializes a polymer application as follows: |
21 * * set up up polling for observable changes | 21 * * set up up polling for observable changes |
22 * * initialize Model-Driven Views | 22 * * initialize Model-Driven Views |
23 * * Include some style to prevent flash of unstyled content (FOUC) | 23 * * Include some style to prevent flash of unstyled content (FOUC) |
24 * * for each library in [libraries], register custom elements labeled with | 24 * * for each library included transitively from HTML and HTML imports, |
25 * [CustomTag] and invoke the initialization method on it. If [libraries] | 25 * register custom elements declared there (labeled with [CustomTag]) and |
26 * is null, first find all libraries that need to be loaded by scanning for | 26 * invoke the initialization method on it (top-level functions annotated with |
27 * HTML imports in the main document. | 27 * [initMethod]). |
28 * | |
29 * The initialization on each library is a top-level function and annotated with | |
30 * [initMethod]. | |
31 * | |
32 * The urls in [libraries] can be absolute or relative to | |
33 * `currentMirrorSystem().isolate.rootLibrary.uri`. | |
34 */ | 28 */ |
35 Zone initPolymer() { | 29 Zone initPolymer() { |
| 30 // We use this pattern, and not the inline lazy initialization pattern, so we |
| 31 // can help dart2js detect that _discoverInitializers can be tree-shaken for |
| 32 // deployment (and hence all uses of dart:mirrors from this loading logic). |
| 33 // TODO(sigmund): fix polymer's transformers so they can replace initPolymer |
| 34 // by initPolymerOptimized. |
| 35 if (_initializers == null) _initializers = _discoverInitializers(); |
36 if (_useDirtyChecking) { | 36 if (_useDirtyChecking) { |
37 return dirtyCheckZone()..run(_initPolymerOptimized); | 37 return dirtyCheckZone()..run(initPolymerOptimized); |
38 } | 38 } |
39 | 39 |
40 _initPolymerOptimized(); | 40 return initPolymerOptimized(); |
41 return Zone.current; | |
42 } | 41 } |
43 | 42 |
44 /** | 43 /** |
45 * Same as [initPolymer], but runs the version that is optimized for deployment | 44 * Same as [initPolymer], but runs the version that is optimized for deployment |
46 * to the internet. The biggest difference is it omits the [Zone] that | 45 * to the internet. The biggest difference is it omits the [Zone] that |
47 * automatically invokes [Observable.dirtyCheck], and the list of libraries must | 46 * automatically invokes [Observable.dirtyCheck], and the list of initializers |
48 * be supplied instead of being dynamically searched for at runtime. | 47 * must be supplied instead of being dynamically searched for at runtime using |
| 48 * mirrors. |
49 */ | 49 */ |
50 // TODO(jmesserly): change the Polymer build step to call this directly. | 50 Zone initPolymerOptimized() { |
51 void _initPolymerOptimized() { | |
52 document.register(PolymerDeclaration._TAG, PolymerDeclaration); | 51 document.register(PolymerDeclaration._TAG, PolymerDeclaration); |
53 | 52 |
54 _loadLibraries(); | 53 for (var initializer in _initializers) { |
| 54 initializer(); |
| 55 } |
55 | 56 |
56 // Run this after user code so they can add to Polymer.veiledElements | 57 // Run this after user code so they can add to Polymer.veiledElements |
57 _preventFlashOfUnstyledContent(); | 58 _preventFlashOfUnstyledContent(); |
58 | 59 |
59 customElementsReady.then((_) => Polymer._ready.complete()); | 60 customElementsReady.then((_) => Polymer._ready.complete()); |
| 61 return Zone.current; |
60 } | 62 } |
61 | 63 |
62 /** | 64 /** |
63 * Configures [initPolymer] making it optimized for deployment to the internet. | 65 * Configures [initPolymer] making it optimized for deployment to the internet. |
64 * With this setup the list of libraries to initialize is supplied instead of | 66 * With this setup the initializer list is supplied instead of being dynamically |
65 * being dynamically searched for at runtime. Additionally, after this method is | 67 * searched for at runtime. Additionally, after this method is called, |
66 * called, [initPolymer] omits the [Zone] that automatically invokes | 68 * [initPolymer] omits the [Zone] that automatically invokes |
67 * [Observable.dirtyCheck]. | 69 * [Observable.dirtyCheck]. |
68 */ | 70 */ |
69 void configureForDeployment(List<String> libraries) { | 71 void configureForDeployment(List<Function> initializers) { |
70 _librariesToLoad = libraries; | 72 _initializers = initializers; |
71 _useDirtyChecking = false; | 73 _useDirtyChecking = false; |
72 } | 74 } |
73 | 75 |
74 /** | 76 /** |
75 * Libraries that will be initialized. For each library, the intialization | 77 * List of initializers that by default will be executed when calling |
76 * registers any type tagged with a [CustomTag] annotation and calls any | 78 * initPolymer. If null, initPolymer will compute the list of initializers by |
| 79 * crawling HTML imports, searchfing for script tags, and including an |
| 80 * initializer for each type tagged with a [CustomTag] annotation and for each |
77 * top-level method annotated with [initMethod]. The value of this field is | 81 * top-level method annotated with [initMethod]. The value of this field is |
78 * assigned programatically by the code generated from the polymer deploy | 82 * assigned programatically by the code generated from the polymer deploy |
79 * scripts. During development, the libraries are inferred by crawling HTML | 83 * scripts. |
80 * imports and searching for script tags. | |
81 */ | 84 */ |
82 List<String> _librariesToLoad = | 85 List<Function> _initializers; |
83 _discoverScripts(document, window.location.href); | 86 |
84 bool _useDirtyChecking = true; | 87 bool _useDirtyChecking = true; |
85 | 88 |
86 void _loadLibraries() { | 89 List<Function> _discoverInitializers() { |
87 for (var lib in _librariesToLoad) { | 90 var initializers = []; |
| 91 var librariesToLoad = _discoverScripts(document, window.location.href); |
| 92 for (var lib in librariesToLoad) { |
88 try { | 93 try { |
89 _loadLibrary(lib); | 94 _loadLibrary(lib, initializers); |
90 } catch (e, s) { | 95 } catch (e, s) { |
91 // Deliver errors async, so if a single library fails it doesn't prevent | 96 // Deliver errors async, so if a single library fails it doesn't prevent |
92 // other things from loading. | 97 // other things from loading. |
93 new Completer().completeError(e, s); | 98 new Completer().completeError(e, s); |
94 } | 99 } |
95 } | 100 } |
| 101 return initializers; |
96 } | 102 } |
97 | 103 |
98 /** | 104 /** |
99 * Walks the HTML import structure to discover all script tags that are | 105 * Walks the HTML import structure to discover all script tags that are |
100 * implicitly loaded. This code is only used in Dartium and should only be | 106 * implicitly loaded. This code is only used in Dartium and should only be |
101 * called after all HTML imports are resolved. Polymer ensures this by asking | 107 * called after all HTML imports are resolved. Polymer ensures this by asking |
102 * users to put their Dart script tags after all HTML imports (this is checked | 108 * users to put their Dart script tags after all HTML imports (this is checked |
103 * by the linter, and Dartium will otherwise show an error message). | 109 * by the linter, and Dartium will otherwise show an error message). |
104 */ | 110 */ |
105 List<String> _discoverScripts(Document doc, String baseUri, | 111 List<String> _discoverScripts(Document doc, String baseUri, |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
152 /** | 158 /** |
153 * Reads the library at [uriString] (which can be an absolute URI or a relative | 159 * Reads the library at [uriString] (which can be an absolute URI or a relative |
154 * URI from the root library), and: | 160 * URI from the root library), and: |
155 * | 161 * |
156 * * If present, invokes any top-level and static functions marked | 162 * * If present, invokes any top-level and static functions marked |
157 * with the [initMethod] annotation (in the order they appear). | 163 * with the [initMethod] annotation (in the order they appear). |
158 * | 164 * |
159 * * Registers any [PolymerElement] that is marked with the [CustomTag] | 165 * * Registers any [PolymerElement] that is marked with the [CustomTag] |
160 * annotation. | 166 * annotation. |
161 */ | 167 */ |
162 void _loadLibrary(String uriString) { | 168 void _loadLibrary(String uriString, List<Function> initializers) { |
163 var uri = _rootUri.resolve(uriString); | 169 var uri = _rootUri.resolve(uriString); |
164 var lib = _libs[uri]; | 170 var lib = _libs[uri]; |
165 if (_isHttpStylePackageUrl(uri)) { | 171 if (_isHttpStylePackageUrl(uri)) { |
166 // Use package: urls if available. This rule here is more permissive than | 172 // Use package: urls if available. This rule here is more permissive than |
167 // how we translate urls in polymer-build, but we expect Dartium to limit | 173 // how we translate urls in polymer-build, but we expect Dartium to limit |
168 // the cases where there are differences. The polymer-build issues an error | 174 // the cases where there are differences. The polymer-build issues an error |
169 // when using packages/ inside lib without properly stepping out all the way | 175 // when using packages/ inside lib without properly stepping out all the way |
170 // to the packages folder. If users don't create symlinks in the source | 176 // to the packages folder. If users don't create symlinks in the source |
171 // tree, then Dartium will also complain because it won't find the file seen | 177 // tree, then Dartium will also complain because it won't find the file seen |
172 // in an HTML import. | 178 // in an HTML import. |
173 var packagePath = uri.path.substring( | 179 var packagePath = uri.path.substring( |
174 uri.path.lastIndexOf('packages/') + 'packages/'.length); | 180 uri.path.lastIndexOf('packages/') + 'packages/'.length); |
175 var canonicalLib = _libs[Uri.parse('package:$packagePath')]; | 181 var canonicalLib = _libs[Uri.parse('package:$packagePath')]; |
176 if (canonicalLib != null) { | 182 if (canonicalLib != null) { |
177 lib = canonicalLib; | 183 lib = canonicalLib; |
178 } | 184 } |
179 } | 185 } |
180 | 186 |
181 if (lib == null) { | 187 if (lib == null) { |
182 _loaderLog.info('$uri library not found'); | 188 _loaderLog.info('$uri library not found'); |
183 return; | 189 return; |
184 } | 190 } |
185 | 191 |
186 // Search top-level functions marked with @initMethod | 192 // Search top-level functions marked with @initMethod |
187 for (var f in lib.declarations.values.where((d) => d is MethodMirror)) { | 193 for (var f in lib.declarations.values.where((d) => d is MethodMirror)) { |
188 _maybeInvoke(lib, f); | 194 _addInitMethod(lib, f, initializers); |
189 } | 195 } |
190 | 196 |
191 for (var c in lib.declarations.values.where((d) => d is ClassMirror)) { | 197 for (var c in lib.declarations.values.where((d) => d is ClassMirror)) { |
192 // Search for @CustomTag on classes | 198 // Search for @CustomTag on classes |
193 for (var m in c.metadata) { | 199 for (var m in c.metadata) { |
194 var meta = m.reflectee; | 200 var meta = m.reflectee; |
195 if (meta is CustomTag) { | 201 if (meta is CustomTag) { |
196 Polymer.register(meta.tagName, c.reflectedType); | 202 initializers.add(() => Polymer.register(meta.tagName, c.reflectedType)); |
197 } | 203 } |
198 } | 204 } |
199 | 205 |
200 // TODO(sigmund): check also static methods marked with @initMethod. | 206 // TODO(sigmund): check also static methods marked with @initMethod. |
201 // This is blocked on two bugs: | 207 // This is blocked on two bugs: |
202 // - dartbug.com/12133 (static methods are incorrectly listed as top-level | 208 // - dartbug.com/12133 (static methods are incorrectly listed as top-level |
203 // in dart2js, so they end up being called twice) | 209 // in dart2js, so they end up being called twice) |
204 // - dartbug.com/12134 (sometimes "method.metadata" throws an exception, | 210 // - dartbug.com/12134 (sometimes "method.metadata" throws an exception, |
205 // we could wrap and hide those exceptions, but it's not ideal). | 211 // we could wrap and hide those exceptions, but it's not ideal). |
206 } | 212 } |
207 } | 213 } |
208 | 214 |
209 void _maybeInvoke(ObjectMirror obj, MethodMirror method) { | 215 void _addInitMethod(ObjectMirror obj, MethodMirror method, |
| 216 List<Function> initializers) { |
210 var annotationFound = false; | 217 var annotationFound = false; |
211 for (var meta in method.metadata) { | 218 for (var meta in method.metadata) { |
212 if (identical(meta.reflectee, initMethod)) { | 219 if (identical(meta.reflectee, initMethod)) { |
213 annotationFound = true; | 220 annotationFound = true; |
214 break; | 221 break; |
215 } | 222 } |
216 } | 223 } |
217 if (!annotationFound) return; | 224 if (!annotationFound) return; |
218 if (!method.isStatic) { | 225 if (!method.isStatic) { |
219 print("warning: methods marked with @initMethod should be static," | 226 print("warning: methods marked with @initMethod should be static," |
220 " ${method.simpleName} is not."); | 227 " ${method.simpleName} is not."); |
221 return; | 228 return; |
222 } | 229 } |
223 if (!method.parameters.where((p) => !p.isOptional).isEmpty) { | 230 if (!method.parameters.where((p) => !p.isOptional).isEmpty) { |
224 print("warning: methods marked with @initMethod should take no " | 231 print("warning: methods marked with @initMethod should take no " |
225 "arguments, ${method.simpleName} expects some."); | 232 "arguments, ${method.simpleName} expects some."); |
226 return; | 233 return; |
227 } | 234 } |
228 obj.invoke(method.simpleName, const []); | 235 initializers.add(() => obj.invoke(method.simpleName, const [])); |
229 } | 236 } |
230 | 237 |
231 class _InitMethodAnnotation { | 238 class _InitMethodAnnotation { |
232 const _InitMethodAnnotation(); | 239 const _InitMethodAnnotation(); |
233 } | 240 } |
OLD | NEW |