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

Side by Side Diff: lib/templating.dart

Issue 5885170347409408: Add location information to watchers to make them more debuggable. (Closed) Base URL: git@github.com:dart-lang/web-ui.git@master
Patch Set: Created 7 years, 5 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
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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 /** Common utility functions used by code generated by the dwc compiler. */ 5 /** Common utility functions used by code generated by the dwc compiler. */
6 library templating; 6 library templating;
7 7
8 import 'dart:async'; 8 import 'dart:async';
9 import 'dart:collection'; 9 import 'dart:collection';
10 import 'dart:html'; 10 import 'dart:html';
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after
140 * * binding classes with a string: 140 * * binding classes with a string:
141 * 141 *
142 * bindCssClasses(e, () => "${class1 != null ? class1 : ''} " 142 * bindCssClasses(e, () => "${class1 != null ? class1 : ''} "
143 * "${class2 != null ? class2 : ''}"); 143 * "${class2 != null ? class2 : ''}");
144 * 144 *
145 * * binding classes separately: 145 * * binding classes separately:
146 * 146 *
147 * bindCssClasses(e, () => class1); 147 * bindCssClasses(e, () => class1);
148 * bindCssClasses(e, () => class2); 148 * bindCssClasses(e, () => class2);
149 */ 149 */
150 ChangeUnobserver bindCssClasses(Element elem, dynamic exp()) { 150 ChangeUnobserver bindCssClasses(Element elem, dynamic exp(),
151 [String debugLocation]) {
151 return watchAndInvoke(exp, (e) { 152 return watchAndInvoke(exp, (e) {
152 if (e.changes != null) { 153 if (e.changes != null) {
153 for (var change in e.changes) changeCssClasses(elem, change); 154 for (var change in e.changes) changeCssClasses(elem, change);
154 } else { 155 } else {
155 updateCssClass(elem, false, e.oldValue); 156 updateCssClass(elem, false, e.oldValue);
156 updateCssClass(elem, true, e.newValue); 157 updateCssClass(elem, true, e.newValue);
157 } 158 }
158 }, 'css-class-bind'); 159 }, 'css-class-bind', debugLocation);
159 } 160 }
160 161
161 /** Bind the result of [exp] to the style attribute in [elem]. */ 162 /** Bind the result of [exp] to the style attribute in [elem]. */
162 ChangeUnobserver bindStyle(Element elem, Map<String, String> exp()) { 163 ChangeUnobserver bindStyle(Element elem, Map<String, String> exp(),
164 [String debugLocation]) {
163 return watchAndInvoke(exp, (e) => updateStyle(elem, e.oldValue, e.newValue), 165 return watchAndInvoke(exp, (e) => updateStyle(elem, e.oldValue, e.newValue),
164 'css-style-bind'); 166 'css-style-bind', debugLocation);
165 } 167 }
166 168
167 /** 169 /**
168 * Changes the style properties from [oldValue] to [newValue]. A runtime error 170 * Changes the style properties from [oldValue] to [newValue]. A runtime error
169 * is reported if [newValue] is not a `String` or `Map<String, String>`. 171 * is reported if [newValue] is not a `String` or `Map<String, String>`.
170 */ 172 */
171 void updateStyle(Element elem, oldValue, newValue) { 173 void updateStyle(Element elem, oldValue, newValue) {
172 if (newValue is! String && newValue is! Map<String, String>) { 174 if (newValue is! String && newValue is! Map<String, String>) {
173 elem.style.cssText = ''; 175 elem.style.cssText = '';
174 throw new ArgumentError("style must be a String or Map<String, String>."); 176 throw new ArgumentError("style must be a String or Map<String, String>.");
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
242 void insert() { 244 void insert() {
243 _subscription = eventStream.listen(listener); 245 _subscription = eventStream.listen(listener);
244 } 246 }
245 247
246 void remove() { 248 void remove() {
247 _subscription.cancel(); 249 _subscription.cancel();
248 _subscription = null; 250 _subscription = null;
249 } 251 }
250 } 252 }
251 253
252 /** Represents a generic data binding and a corresponding action. */ 254 /** Common logic for all bindings. */
253 class Binding extends TemplateItem { 255 abstract class Binding extends TemplateItem {
254 final exp; 256 final exp;
255 final ChangeObserver action;
256 final bool isFinal; 257 final bool isFinal;
258 final String debugLocation;
257 ChangeUnobserver stopper; 259 ChangeUnobserver stopper;
258 260
259 Binding(this.exp, this.action, this.isFinal); 261 Binding(this.exp, this.isFinal)
262 : debugLocation = verboseDebugMessages ? _readCurrentStackTrace() : null;
260 263
261 void insert() { 264 void insert() {
262 if (isFinal) { 265 if (isFinal) {
263 action(new ChangeNotification(null, exp())); 266 invokeCallback();
264 } else if (stopper != null) { 267 } else if (stopper != null) {
265 throw new StateError('binding already attached'); 268 throw new StateError('binding already attached');
266 } else { 269 } else {
267 stopper = watchAndInvoke(exp, action, 'generic-binding'); 270 stopper = registerAndInvoke();
268 } 271 }
269 } 272 }
270 273
271 void remove() { 274 void remove() {
272 if (!isFinal) { 275 if (!isFinal) {
273 stopper(); 276 stopper();
274 stopper = null; 277 stopper = null;
275 } 278 }
276 } 279 }
280
281 /** Invokes the action associated with this binding. */
282 void invokeCallback();
283
284 /**
285 * Registers the watcher and invokes the action associated with this binding.
286 */
287 ChangeUnobserver registerAndInvoke();
288 }
289
290 /** Represents a generic data binding and a corresponding action. */
291 class GenericBinding extends Binding {
292 final ChangeObserver action;
293
294 GenericBinding(exp, this.action, bool isFinal) : super(exp, isFinal);
295
296 void invokeCallback() => action(new ChangeNotification(null, exp()));
297
298 ChangeUnobserver registerAndInvoke() =>
299 watchAndInvoke(exp, action, 'generic-binding', debugLocation);
277 } 300 }
278 301
279 /** Represents a binding to a style attribute. */ 302 /** Represents a binding to a style attribute. */
280 class StyleAttrBinding extends TemplateItem { 303 class StyleAttrBinding extends Binding {
281 final exp;
282 final Element elem; 304 final Element elem;
283 final bool isFinal;
284 ChangeUnobserver stopper;
285 305
286 StyleAttrBinding(this.elem, this.exp, this.isFinal); 306 StyleAttrBinding(this.elem, exp, bool isFinal) : super(exp, isFinal);
287 307
288 void insert() { 308 void invokeCallback() => updateStyle(elem, null, exp());
289 if (isFinal) {
290 updateStyle(elem, null, exp());
291 } else if (stopper != null) {
292 throw new StateError('style binding already attached');
293 } else {
294 stopper = bindStyle(elem, exp);
295 }
296 }
297 309
298 void remove() { 310 ChangeUnobserver registerAndInvoke() => bindStyle(elem, exp, debugLocation);
299 if (!isFinal) {
300 stopper();
301 stopper = null;
302 }
303 }
304 } 311 }
305 312
306 /** Represents a binding to a class attribute. */ 313 /** Represents a binding to a class attribute. */
307 class ClassAttrBinding extends TemplateItem { 314 class ClassAttrBinding extends Binding {
308 final Element elem; 315 final Element elem;
309 final exp;
310 final bool isFinal;
311 ChangeUnobserver stopper;
312 316
313 ClassAttrBinding(this.elem, this.exp, this.isFinal); 317 ClassAttrBinding(this.elem, exp, bool isFinal) : super(exp, isFinal);
314 318
315 void insert() { 319 void invokeCallback() => updateCssClass(elem, true, exp());
316 if (isFinal) {
317 updateCssClass(elem, true, exp());
318 } else if (stopper != null) {
319 throw new StateError('class binding already attached');
320 } else {
321 stopper = bindCssClasses(elem, exp);
322 }
323 }
324 320
325 void remove() { 321 ChangeUnobserver registerAndInvoke() =>
326 if (!isFinal) { 322 bindCssClasses(elem, exp, debugLocation);
327 stopper();
328 stopper = null;
329 }
330 }
331 } 323 }
332 324
333 /** 325 /**
334 * Represents a one-way binding between a dart getter expression and a DOM 326 * Represents a one-way binding between a dart getter expression and a DOM
335 * property, or conversely between a DOM property value and a dart property. 327 * property, or conversely between a DOM property value and a dart property.
336 */ 328 */
337 class DomPropertyBinding extends TemplateItem { 329 class DomPropertyBinding extends Binding {
338 /** Value updated by this binding. */ 330 /** Value updated by this binding. */
339 final Setter setter; 331 final Setter setter;
340 332
341 /** 333 /**
342 * Getter that reads the value of the binding, either from a Dart expression
343 * or from a DOM property (which is internally also a Dart expression).
344 */
345 final Getter getter;
346
347 /**
348 * Whether this is a binding that assigns a DOM attribute accepting URL 334 * Whether this is a binding that assigns a DOM attribute accepting URL
349 * values. If so, the value assigned to the attribute needs to be sanitized. 335 * values. If so, the value assigned to the attribute needs to be sanitized.
350 */ 336 */
351 final bool isUrl; 337 final bool isUrl;
352 338
353 final bool isFinal; 339 /**
354 340 * Creates a DOM property binding, where [getter] reads the value of the
355 ChangeUnobserver stopper; 341 * binding, either from a Dart expression or from a DOM property (which is
356 342 * internally also a Dart expression).
357 DomPropertyBinding(this.getter, this.setter, this.isUrl, this.isFinal); 343 */
344 DomPropertyBinding(Getter getter, this.setter, this.isUrl, bool isFinal)
345 : super(getter, isFinal);
358 346
359 void _safeSetter(value) { 347 void _safeSetter(value) {
360 setter(isUrl ? sanitizeUri(value) : value); 348 setter(isUrl ? sanitizeUri(value) : value);
361 } 349 }
350 void invokeCallback() => _safeSetter(exp());
362 351
363 void insert() { 352 ChangeUnobserver registerAndInvoke() =>
364 if (isFinal) { 353 watchAndInvoke(exp, (e) => _safeSetter(e.newValue),
365 _safeSetter(getter()); 354 'dom-property-binding', debugLocation);
366 } else if (stopper != null) {
367 throw new StateError('data binding already attached.');
368 } else {
369 stopper = watchAndInvoke(getter, (e) => _safeSetter(e.newValue),
370 'dom-property-binding');
371 }
372 }
373
374 void remove() {
375 if (!isFinal) {
376 stopper();
377 stopper = null;
378 }
379 }
380 } 355 }
381 356
382 /** Represents a component added within a template. */ 357 /** Represents a component added within a template. */
383 class ComponentItem extends TemplateItem { 358 class ComponentItem extends TemplateItem {
384 /** An autogenerated component. */ 359 /** An autogenerated component. */
385 final component; 360 final component;
386 361
387 ComponentItem(this.component); 362 ComponentItem(this.component);
388 363
389 void create() { 364 void create() {
(...skipping 23 matching lines...) Expand all
413 388
414 Template(this.node); 389 Template(this.node);
415 390
416 /** Associate the event listener while this template is visible. */ 391 /** Associate the event listener while this template is visible. */
417 void listen(Stream<Event> stream, EventListener listener) { 392 void listen(Stream<Event> stream, EventListener listener) {
418 children.add(new Listener(stream, (e) { listener(e); dispatch(); })); 393 children.add(new Listener(stream, (e) { listener(e); dispatch(); }));
419 } 394 }
420 395
421 /** Run [action] when [exp] changes (while this template is visible). */ 396 /** Run [action] when [exp] changes (while this template is visible). */
422 void bind(exp, ChangeObserver action, bool isFinal) { 397 void bind(exp, ChangeObserver action, bool isFinal) {
423 children.add(new Binding(exp, action, isFinal)); 398 children.add(new GenericBinding(exp, action, isFinal));
424 } 399 }
425 400
426 /** Create and bind a [Node] to [exp] while this template is visible. */ 401 /** Create and bind a [Node] to [exp] while this template is visible. */
427 Node contentBind(Function exp, isFinal) { 402 Node contentBind(Function exp, isFinal) {
428 var bindNode = new Text(''); 403 var bindNode = new Text('');
429 children.add(new Binding(() => '${exp()}', (e) { 404 children.add(new GenericBinding(() => '${exp()}', (e) {
430 bindNode = updateBinding(exp(), bindNode, e.newValue); 405 bindNode = updateBinding(exp(), bindNode, e.newValue);
431 }, isFinal)); 406 }, isFinal));
432 return bindNode; 407 return bindNode;
433 } 408 }
434 409
435 /** Bind [exp] to `elem.class` while this template is visible. */ 410 /** Bind [exp] to `elem.class` while this template is visible. */
436 void bindClass(elem, exp, isFinal) { 411 void bindClass(elem, exp, isFinal) {
437 children.add(new ClassAttrBinding(elem, exp, isFinal)); 412 children.add(new ClassAttrBinding(elem, exp, isFinal));
438 } 413 }
439 414
(...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after
669 node.nodes.clear(); 644 node.nodes.clear();
670 nodes.clear(); 645 nodes.clear();
671 } 646 }
672 647
673 void remove() { 648 void remove() {
674 _removeInternal(); 649 _removeInternal();
675 stopper(); 650 stopper();
676 stopper = null; 651 stopper = null;
677 } 652 }
678 } 653 }
654
655 String _readCurrentStackTrace() {
656 try {
657 throw "";
658 } catch (e, trace) {
659 return trace.toString();
Jennifer Messerly 2013/07/11 21:27:23 use http://pub.dartlang.org/packages/stack_trace ?
Siggi Cherem (dart-lang) 2013/07/11 22:07:01 Oh, it used to depend on dart:io, but seems to hav
660 }
661 }
662
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698