Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(268)

Side by Side Diff: pkg/polymer/lib/src/loader.dart

Issue 189213003: Refactoring two pieces of polymer: (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « pkg/polymer/lib/src/instance.dart ('k') | pkg/polymer/lib/src/mirror_loader.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 /// Metadata used to label static or top-level methods that are called 13 /// Metadata used to label static or top-level methods that are called
14 /// automatically when loading the library of a custom element. 14 /// automatically when loading the library of a custom element.
15 const initMethod = const _InitMethodAnnotation(); 15 const initMethod = const InitMethodAnnotation();
16
17 /// Implementation behind [initMethod]. Only exposed for internal implementation
18 /// details
19 class InitMethodAnnotation {
20 const InitMethodAnnotation();
21 }
16 22
17 /// Initializes a polymer application as follows: 23 /// Initializes a polymer application as follows:
18 /// * set up up polling for observable changes 24 /// * set up up polling for observable changes
19 /// * initialize Model-Driven Views 25 /// * initialize Model-Driven Views
20 /// * Include some style to prevent flash of unstyled content (FOUC) 26 /// * Include some style to prevent flash of unstyled content (FOUC)
21 /// * for each library included transitively from HTML and HTML imports, 27 /// * for each library included transitively from HTML and HTML imports,
22 /// register custom elements declared there (labeled with [CustomTag]) and 28 /// register custom elements declared there (labeled with [CustomTag]) and
23 /// invoke the initialization method on it (top-level functions annotated with 29 /// invoke the initialization method on it (top-level functions annotated with
24 /// [initMethod]). 30 /// [initMethod]).
25 Zone initPolymer() { 31 Zone initPolymer() => loader.deployMode
26 // We use this pattern, and not the inline lazy initialization pattern, so we
27 // can help dart2js detect that _discoverInitializers can be tree-shaken for
28 // deployment (and hence all uses of dart:mirrors from this loading logic).
29 // TODO(sigmund): fix polymer's transformers so they can replace initPolymer
30 // by initPolymerOptimized.
31 if (_initializers == null) _initializers = _discoverInitializers();
32
33 // In deployment mode, we rely on change notifiers instead of dirty checking. 32 // In deployment mode, we rely on change notifiers instead of dirty checking.
34 if (!_deployMode) { 33 ? _initPolymerOptimized() : (dirtyCheckZone()..run(_initPolymerOptimized));
35 return dirtyCheckZone()..run(initPolymerOptimized);
36 }
37
38 return initPolymerOptimized();
39 }
40 34
41 /// Same as [initPolymer], but runs the version that is optimized for deployment 35 /// Same as [initPolymer], but runs the version that is optimized for deployment
42 /// to the internet. The biggest difference is it omits the [Zone] that 36 /// to the internet. The biggest difference is it omits the [Zone] that
43 /// automatically invokes [Observable.dirtyCheck], and the list of initializers 37 /// automatically invokes [Observable.dirtyCheck], and the list of initializers
44 /// must be supplied instead of being dynamically searched for at runtime using 38 /// must be supplied instead of being dynamically searched for at runtime using
45 /// mirrors. 39 /// mirrors.
46 Zone initPolymerOptimized() { 40 Zone _initPolymerOptimized() {
47 // TODO(sigmund): refactor this so we can replace it by codegen.
48 smoke.useMirrors();
49 _hookJsPolymer(); 41 _hookJsPolymer();
50 42
51 for (var initializer in _initializers) { 43 for (var initializer in loader.initializers) {
52 initializer(); 44 initializer();
53 } 45 }
54 46
55 return Zone.current; 47 return Zone.current;
56 } 48 }
57 49
58 /// Configures [initPolymer] making it optimized for deployment to the internet. 50 /// Configures [initPolymer] making it optimized for deployment to the internet.
59 /// With this setup the initializer list is supplied instead of searched for 51 /// With this setup the initializer list is supplied instead of searched for
60 /// at runtime. Additionally, after this method is called [initPolymer] omits 52 /// at runtime. Additionally, after this method is called [initPolymer] omits
61 /// the [Zone] that automatically invokes [Observable.dirtyCheck]. 53 /// the [Zone] that automatically invokes [Observable.dirtyCheck].
62 void configureForDeployment(List<Function> initializers) { 54 void configureForDeployment(List<Function> initializers) {
63 _initializers = initializers; 55 loader.initializers = initializers;
64 _deployMode = true; 56 loader.deployMode = true;
65 }
66
67 /// List of initializers that by default will be executed when calling
68 /// initPolymer. If null, initPolymer will compute the list of initializers by
69 /// crawling HTML imports, searchfing for script tags, and including an
70 /// initializer for each type tagged with a [CustomTag] annotation and for each
71 /// top-level method annotated with [initMethod]. The value of this field is
72 /// assigned programatically by the code generated from the polymer deploy
73 /// scripts.
74 List<Function> _initializers;
75
76 /// True if we're in deployment mode.
77 bool _deployMode = false;
78
79 List<Function> _discoverInitializers() {
80 var initializers = [];
81 var librariesToLoad = _discoverScripts(document, window.location.href);
82 for (var lib in librariesToLoad) {
83 try {
84 _loadLibrary(lib, initializers);
85 } catch (e, s) {
86 // Deliver errors async, so if a single library fails it doesn't prevent
87 // other things from loading.
88 new Completer().completeError(e, s);
89 }
90 }
91 return initializers;
92 }
93
94 /// Walks the HTML import structure to discover all script tags that are
95 /// implicitly loaded. This code is only used in Dartium and should only be
96 /// called after all HTML imports are resolved. Polymer ensures this by asking
97 /// users to put their Dart script tags after all HTML imports (this is checked
98 /// by the linter, and Dartium will otherwise show an error message).
99 List<String> _discoverScripts(Document doc, String baseUri,
100 [Set<Document> seen, List<String> scripts]) {
101 if (seen == null) seen = new Set<Document>();
102 if (scripts == null) scripts = <String>[];
103 if (doc == null) {
104 print('warning: $baseUri not found.');
105 return scripts;
106 }
107 if (seen.contains(doc)) return scripts;
108 seen.add(doc);
109
110 bool scriptSeen = false;
111 for (var node in doc.querySelectorAll('script,link[rel="import"]')) {
112 if (node is LinkElement) {
113 _discoverScripts(node.import, node.href, seen, scripts);
114 } else if (node is ScriptElement && node.type == 'application/dart') {
115 if (!scriptSeen) {
116 var url = node.src;
117 scripts.add(url == '' ? baseUri : url);
118 scriptSeen = true;
119 } else {
120 print('warning: more than one Dart script tag in $baseUri. Dartium '
121 'currently only allows a single Dart script tag per document.');
122 }
123 }
124 }
125 return scripts;
126 }
127
128 /// All libraries in the current isolate.
129 final _libs = currentMirrorSystem().libraries;
130
131 // TODO(sigmund): explore other (cheaper) ways to resolve URIs relative to the
132 // root library (see dartbug.com/12612)
133 final _rootUri = currentMirrorSystem().isolate.rootLibrary.uri;
134
135 final Logger _loaderLog = new Logger('polymer.loader');
136
137 bool _isHttpStylePackageUrl(Uri uri) {
138 var uriPath = uri.path;
139 return uri.scheme == _rootUri.scheme &&
140 // Don't process cross-domain uris.
141 uri.authority == _rootUri.authority &&
142 uriPath.endsWith('.dart') &&
143 (uriPath.contains('/packages/') || uriPath.startsWith('packages/'));
144 }
145
146 /// Reads the library at [uriString] (which can be an absolute URI or a relative
147 /// URI from the root library), and:
148 ///
149 /// * If present, invokes any top-level and static functions marked
150 /// with the [initMethod] annotation (in the order they appear).
151 ///
152 /// * Registers any [PolymerElement] that is marked with the [CustomTag]
153 /// annotation.
154 void _loadLibrary(String uriString, List<Function> initializers) {
155 var uri = _rootUri.resolve(uriString);
156 var lib = _libs[uri];
157 if (_isHttpStylePackageUrl(uri)) {
158 // Use package: urls if available. This rule here is more permissive than
159 // how we translate urls in polymer-build, but we expect Dartium to limit
160 // the cases where there are differences. The polymer-build issues an error
161 // when using packages/ inside lib without properly stepping out all the way
162 // to the packages folder. If users don't create symlinks in the source
163 // tree, then Dartium will also complain because it won't find the file seen
164 // in an HTML import.
165 var packagePath = uri.path.substring(
166 uri.path.lastIndexOf('packages/') + 'packages/'.length);
167 var canonicalLib = _libs[Uri.parse('package:$packagePath')];
168 if (canonicalLib != null) {
169 lib = canonicalLib;
170 }
171 }
172
173 if (lib == null) {
174 _loaderLog.info('$uri library not found');
175 return;
176 }
177
178 // Search top-level functions marked with @initMethod
179 for (var f in lib.declarations.values.where((d) => d is MethodMirror)) {
180 _addInitMethod(lib, f, initializers);
181 }
182
183
184 // Dart note: we don't get back @CustomTags in a reliable order from mirrors,
185 // at least on Dart VM. So we need to sort them so base classes are registered
186 // first, which ensures that document.register will work correctly for a
187 // set of types within in the same library.
188 var customTags = new LinkedHashMap<Type, Function>();
189 for (var c in lib.declarations.values.where((d) => d is ClassMirror)) {
190 _loadCustomTags(lib, c, customTags);
191 // TODO(sigmund): check also static methods marked with @initMethod.
192 // This is blocked on two bugs:
193 // - dartbug.com/12133 (static methods are incorrectly listed as top-level
194 // in dart2js, so they end up being called twice)
195 // - dartbug.com/12134 (sometimes "method.metadata" throws an exception,
196 // we could wrap and hide those exceptions, but it's not ideal).
197 }
198
199 initializers.addAll(customTags.values);
200 }
201
202 void _loadCustomTags(LibraryMirror lib, ClassMirror cls,
203 LinkedHashMap registerFns) {
204 if (cls == null || cls.reflectedType == HtmlElement) return;
205
206 // Register superclass first.
207 _loadCustomTags(lib, cls.superclass, registerFns);
208
209 if (cls.owner != lib) {
210 // Don't register classes from different libraries.
211 // TODO(jmesserly): @CustomTag does not currently respect re-export, because
212 // LibraryMirror.declarations doesn't include these.
213 return;
214 }
215
216 var meta = _getCustomTagMetadata(cls);
217 if (meta == null) return;
218
219 registerFns.putIfAbsent(cls.reflectedType, () =>
220 () => Polymer.register(meta.tagName, cls.reflectedType));
221 }
222
223 /// Search for @CustomTag on a classemirror
224 CustomTag _getCustomTagMetadata(ClassMirror c) {
225 for (var m in c.metadata) {
226 var meta = m.reflectee;
227 if (meta is CustomTag) return meta;
228 }
229 return null;
230 }
231
232 void _addInitMethod(ObjectMirror obj, MethodMirror method,
233 List<Function> initializers) {
234 var annotationFound = false;
235 for (var meta in method.metadata) {
236 if (identical(meta.reflectee, initMethod)) {
237 annotationFound = true;
238 break;
239 }
240 }
241 if (!annotationFound) return;
242 if (!method.isStatic) {
243 print("warning: methods marked with @initMethod should be static,"
244 " ${method.simpleName} is not.");
245 return;
246 }
247 if (!method.parameters.where((p) => !p.isOptional).isEmpty) {
248 print("warning: methods marked with @initMethod should take no "
249 "arguments, ${method.simpleName} expects some.");
250 return;
251 }
252 initializers.add(() => obj.invoke(method.simpleName, const []));
253 }
254
255 class _InitMethodAnnotation {
256 const _InitMethodAnnotation();
257 } 57 }
258 58
259 /// To ensure Dart can interoperate with polymer-element registered by 59 /// To ensure Dart can interoperate with polymer-element registered by
260 /// polymer.js, we need to be able to execute Dart code if we are registering 60 /// polymer.js, we need to be able to execute Dart code if we are registering
261 /// a Dart class for that element. We trigger Dart logic by patching 61 /// a Dart class for that element. We trigger Dart logic by patching
262 /// polymer-element's register function and: 62 /// polymer-element's register function and:
263 /// 63 ///
264 /// * if it has a Dart class, run PolymerDeclaration's register. 64 /// * if it has a Dart class, run PolymerDeclaration's register.
265 /// * otherwise it is a JS prototype, run polymer-element's normal register. 65 /// * otherwise it is a JS prototype, run polymer-element's normal register.
266 void _hookJsPolymer() { 66 void _hookJsPolymer() {
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
304 final extendsDecl = _getDeclaration(extendee); 104 final extendsDecl = _getDeclaration(extendee);
305 return zone.run(() => 105 return zone.run(() =>
306 new PolymerDeclaration(jsElem, name, type, extendsDecl).register()); 106 new PolymerDeclaration(jsElem, name, type, extendsDecl).register());
307 } 107 }
308 // It's a JavaScript polymer element, fall back to the original register. 108 // It's a JavaScript polymer element, fall back to the original register.
309 return originalRegister.apply([name, extendee], thisArg: jsElem); 109 return originalRegister.apply([name, extendee], thisArg: jsElem);
310 } 110 }
311 111
312 proto['register'] = new JsFunction.withThis(registerDart); 112 proto['register'] = new JsFunction.withThis(registerDart);
313 } 113 }
OLDNEW
« no previous file with comments | « pkg/polymer/lib/src/instance.dart ('k') | pkg/polymer/lib/src/mirror_loader.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698