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