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

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
« no previous file with comments | « lib/src/compiler.dart ('k') | lib/testing/render_test.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) 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
263 ? readCurrentStackTrace() : null;
260 264
261 void insert() { 265 void insert() {
262 if (isFinal) { 266 if (isFinal) {
263 action(new ChangeNotification(null, exp())); 267 invokeCallback();
264 } else if (stopper != null) { 268 } else if (stopper != null) {
265 throw new StateError('binding already attached'); 269 throw new StateError('binding already attached');
266 } else { 270 } else {
267 stopper = watchAndInvoke(exp, action, 'generic-binding'); 271 stopper = registerAndInvoke();
268 } 272 }
269 } 273 }
270 274
271 void remove() { 275 void remove() {
272 if (!isFinal) { 276 if (!isFinal) {
273 stopper(); 277 stopper();
274 stopper = null; 278 stopper = null;
275 } 279 }
276 } 280 }
281
282 /** Invokes the action associated with this binding. */
283 void invokeCallback();
284
285 /**
286 * Registers the watcher and invokes the action associated with this binding.
287 */
288 ChangeUnobserver registerAndInvoke();
289 }
290
291 /** Represents a generic data binding and a corresponding action. */
292 class GenericBinding extends Binding {
293 final ChangeObserver action;
294
295 GenericBinding(exp, this.action, bool isFinal) : super(exp, isFinal);
296
297 void invokeCallback() => action(new ChangeNotification(null, exp()));
298
299 ChangeUnobserver registerAndInvoke() =>
300 watchAndInvoke(exp, action, 'generic-binding', debugLocation);
277 } 301 }
278 302
279 /** Represents a binding to a style attribute. */ 303 /** Represents a binding to a style attribute. */
280 class StyleAttrBinding extends TemplateItem { 304 class StyleAttrBinding extends Binding {
281 final exp;
282 final Element elem; 305 final Element elem;
283 final bool isFinal;
284 ChangeUnobserver stopper;
285 306
286 StyleAttrBinding(this.elem, this.exp, this.isFinal); 307 StyleAttrBinding(this.elem, exp, bool isFinal) : super(exp, isFinal);
287 308
288 void insert() { 309 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 310
298 void remove() { 311 ChangeUnobserver registerAndInvoke() => bindStyle(elem, exp, debugLocation);
299 if (!isFinal) {
300 stopper();
301 stopper = null;
302 }
303 }
304 } 312 }
305 313
306 /** Represents a binding to a class attribute. */ 314 /** Represents a binding to a class attribute. */
307 class ClassAttrBinding extends TemplateItem { 315 class ClassAttrBinding extends Binding {
308 final Element elem; 316 final Element elem;
309 final exp;
310 final bool isFinal;
311 ChangeUnobserver stopper;
312 317
313 ClassAttrBinding(this.elem, this.exp, this.isFinal); 318 ClassAttrBinding(this.elem, exp, bool isFinal) : super(exp, isFinal);
314 319
315 void insert() { 320 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 321
325 void remove() { 322 ChangeUnobserver registerAndInvoke() =>
326 if (!isFinal) { 323 bindCssClasses(elem, exp, debugLocation);
327 stopper();
328 stopper = null;
329 }
330 }
331 } 324 }
332 325
333 /** 326 /**
334 * Represents a one-way binding between a dart getter expression and a DOM 327 * 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. 328 * property, or conversely between a DOM property value and a dart property.
336 */ 329 */
337 class DomPropertyBinding extends TemplateItem { 330 class DomPropertyBinding extends Binding {
338 /** Value updated by this binding. */ 331 /** Value updated by this binding. */
339 final Setter setter; 332 final Setter setter;
340 333
341 /** 334 /**
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 335 * 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. 336 * values. If so, the value assigned to the attribute needs to be sanitized.
350 */ 337 */
351 final bool isUrl; 338 final bool isUrl;
352 339
353 final bool isFinal; 340 /**
354 341 * Creates a DOM property binding, where [getter] reads the value of the
355 ChangeUnobserver stopper; 342 * binding, either from a Dart expression or from a DOM property (which is
356 343 * internally also a Dart expression).
357 DomPropertyBinding(this.getter, this.setter, this.isUrl, this.isFinal); 344 */
345 DomPropertyBinding(Getter getter, this.setter, this.isUrl, bool isFinal)
346 : super(getter, isFinal);
358 347
359 void _safeSetter(value) { 348 void _safeSetter(value) {
360 setter(isUrl ? sanitizeUri(value) : value); 349 setter(isUrl ? sanitizeUri(value) : value);
361 } 350 }
351 void invokeCallback() => _safeSetter(exp());
362 352
363 void insert() { 353 ChangeUnobserver registerAndInvoke() =>
364 if (isFinal) { 354 watchAndInvoke(exp, (e) => _safeSetter(e.newValue),
365 _safeSetter(getter()); 355 '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 } 356 }
381 357
382 /** Represents a component added within a template. */ 358 /** Represents a component added within a template. */
383 class ComponentItem extends TemplateItem { 359 class ComponentItem extends TemplateItem {
384 /** An autogenerated component. */ 360 /** An autogenerated component. */
385 final component; 361 final component;
386 362
387 ComponentItem(this.component); 363 ComponentItem(this.component);
388 364
389 void create() { 365 void create() {
(...skipping 23 matching lines...) Expand all
413 389
414 Template(this.node); 390 Template(this.node);
415 391
416 /** Associate the event listener while this template is visible. */ 392 /** Associate the event listener while this template is visible. */
417 void listen(Stream<Event> stream, EventListener listener) { 393 void listen(Stream<Event> stream, EventListener listener) {
418 children.add(new Listener(stream, (e) { listener(e); dispatch(); })); 394 children.add(new Listener(stream, (e) { listener(e); dispatch(); }));
419 } 395 }
420 396
421 /** Run [action] when [exp] changes (while this template is visible). */ 397 /** Run [action] when [exp] changes (while this template is visible). */
422 void bind(exp, ChangeObserver action, bool isFinal) { 398 void bind(exp, ChangeObserver action, bool isFinal) {
423 children.add(new Binding(exp, action, isFinal)); 399 children.add(new GenericBinding(exp, action, isFinal));
424 } 400 }
425 401
426 /** Create and bind a [Node] to [exp] while this template is visible. */ 402 /** Create and bind a [Node] to [exp] while this template is visible. */
427 Node contentBind(Function exp, isFinal) { 403 Node contentBind(Function exp, isFinal) {
428 var bindNode = new Text(''); 404 var bindNode = new Text('');
429 children.add(new Binding(() => '${exp()}', (e) { 405 children.add(new GenericBinding(() => '${exp()}', (e) {
430 bindNode = updateBinding(exp(), bindNode, e.newValue); 406 bindNode = updateBinding(exp(), bindNode, e.newValue);
431 }, isFinal)); 407 }, isFinal));
432 return bindNode; 408 return bindNode;
433 } 409 }
434 410
435 /** Bind [exp] to `elem.class` while this template is visible. */ 411 /** Bind [exp] to `elem.class` while this template is visible. */
436 void bindClass(elem, exp, isFinal) { 412 void bindClass(elem, exp, isFinal) {
437 children.add(new ClassAttrBinding(elem, exp, isFinal)); 413 children.add(new ClassAttrBinding(elem, exp, isFinal));
438 } 414 }
439 415
(...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after
669 node.nodes.clear(); 645 node.nodes.clear();
670 nodes.clear(); 646 nodes.clear();
671 } 647 }
672 648
673 void remove() { 649 void remove() {
674 _removeInternal(); 650 _removeInternal();
675 stopper(); 651 stopper();
676 stopper = null; 652 stopper = null;
677 } 653 }
678 } 654 }
OLDNEW
« no previous file with comments | « lib/src/compiler.dart ('k') | lib/testing/render_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698