diff --git a/benchmark/launch_chrome.sh b/benchmark/launch_chrome.sh
index c9e7ea65b..cf3924587 100755
--- a/benchmark/launch_chrome.sh
+++ b/benchmark/launch_chrome.sh
@@ -4,5 +4,10 @@ platform=`uname`
if [[ "$platform" == 'Linux' ]]; then
`google-chrome --js-flags="--expose-gc"`
elif [[ "$platform" == 'Darwin' ]]; then
- `/Applications/Google\ Chrome\ Canary.app/Contents/MacOS/Google\ Chrome\ Canary --enable-memory-info --enable-precise-memory-info --enable-memory-benchmarking --js-flags="--expose-gc"`
+ `/Applications/Google\ Chrome\ Canary.app/Contents/MacOS/Google\ Chrome\ Canary \
+ --enable-memory-info \
+ --enable-precise-memory-info \
+ --enable-memory-benchmarking \
+ --js-flags="--expose-gc" \
+ --remote-debugging-port=9222`
fi
diff --git a/benchmark/web/wtf.dart b/benchmark/web/wtf.dart
new file mode 100644
index 000000000..eb398a27f
--- /dev/null
+++ b/benchmark/web/wtf.dart
@@ -0,0 +1,31 @@
+library wtf_test_app;
+
+import 'package:angular/wtf.dart';
+import 'dart:html';
+import 'dart:js' show context;
+
+main() {
+ traceInit(context);
+ var _main = traceCreateScope('main()');
+ var _querySelector = traceCreateScope('Node#querySelector()');
+ var _DivElement = traceCreateScope('DivElement()');
+ var _ElementText = traceCreateScope('Element#text');
+ var _NodeAppend = traceCreateScope('Node#append()');
+ var scope = traceEnter(_main);
+ var s = traceEnter(_querySelector);
+ BodyElement body = window.document.querySelector('body');
+ traceLeave(s);
+
+ s = traceEnter(_DivElement);
+ var div = new DivElement();
+ traceLeave(s);
+
+ s = traceEnter(_ElementText);
+ div.text = 'Hello WTF! (enabled: ${wtfEnabled})';
+ traceLeave(s);
+
+ s = traceEnter(_NodeAppend);
+ body.append(div);
+ traceLeave(s);
+ traceLeave(scope);
+}
diff --git a/benchmark/web/wtf.html b/benchmark/web/wtf.html
new file mode 100644
index 000000000..44f01bb30
--- /dev/null
+++ b/benchmark/web/wtf.html
@@ -0,0 +1,12 @@
+
+
+
+
+ WTF Test page
+
+
+
+
+
+
+
diff --git a/lib/application.dart b/lib/application.dart
index e6d696276..f4068cd71 100644
--- a/lib/application.dart
+++ b/lib/application.dart
@@ -68,6 +68,7 @@
library angular.app;
import 'dart:html' as dom;
+import 'dart:js' show context;
import 'package:intl/date_symbol_data_local.dart';
import 'package:di/di.dart';
@@ -76,12 +77,12 @@ import 'package:angular/perf/module.dart';
import 'package:angular/cache/module.dart';
import 'package:angular/cache/js_cache_register.dart';
import 'package:angular/core/module_internal.dart';
-import 'package:angular/core/registry.dart';
import 'package:angular/core_dom/module_internal.dart';
import 'package:angular/directive/module.dart';
import 'package:angular/formatter/module_internal.dart';
import 'package:angular/routing/module.dart';
import 'package:angular/introspection.dart';
+import 'package:angular/wtf.dart';
import 'package:angular/core_dom/static_keys.dart';
import 'package:angular/core_dom/directive_injector.dart';
@@ -129,6 +130,7 @@ class AngularModule extends Module {
* applicationFactory to bootstrap your Angular application.
*
*/
+var _Application_run = traceCreateScope('Application#run()');
abstract class Application {
static _find(String selector, [dom.Element defaultElement]) {
var element = dom.document.querySelector(selector);
@@ -150,6 +152,7 @@ abstract class Application {
dom.Element selector(String selector) => element = _find(selector);
Application(): element = _find('[ng-app]', dom.window.document.documentElement) {
+ traceInit(context);
modules.add(ngModule);
ngModule..bind(VmTurnZone, toValue: zone)
..bind(Application, toValue: this)
@@ -172,26 +175,31 @@ abstract class Application {
}
Injector run() {
- publishToJavaScript();
- return zone.run(() {
- var rootElements = [element];
- Injector injector = createInjector();
- ExceptionHandler exceptionHandler = injector.getByKey(EXCEPTION_HANDLER_KEY);
- // Publish cache register interface
- injector.getByKey(JS_CACHE_REGISTER_KEY);
- initializeDateFormatting(null, null).then((_) {
- try {
- Compiler compiler = injector.getByKey(COMPILER_KEY);
- DirectiveMap directiveMap = injector.getByKey(DIRECTIVE_MAP_KEY);
- RootScope rootScope = injector.getByKey(ROOT_SCOPE_KEY);
- ViewFactory viewFactory = compiler(rootElements, directiveMap);
- viewFactory(rootScope, injector.get(DirectiveInjector), rootElements);
- } catch (e, s) {
- exceptionHandler(e, s);
- }
+ var scope = traceEnter(_Application_run);
+ try {
+ publishToJavaScript();
+ return zone.run(() {
+ var rootElements = [element];
+ Injector injector = createInjector();
+ ExceptionHandler exceptionHandler = injector.getByKey(EXCEPTION_HANDLER_KEY);
+ // Publish cache register interface
+ injector.getByKey(JS_CACHE_REGISTER_KEY);
+ initializeDateFormatting(null, null).then((_) {
+ try {
+ Compiler compiler = injector.getByKey(COMPILER_KEY);
+ DirectiveMap directiveMap = injector.getByKey(DIRECTIVE_MAP_KEY);
+ RootScope rootScope = injector.getByKey(ROOT_SCOPE_KEY);
+ ViewFactory viewFactory = compiler(rootElements, directiveMap);
+ viewFactory(rootScope, injector.get(DirectiveInjector), rootElements);
+ } catch (e, s) {
+ exceptionHandler(e, s);
+ }
+ });
+ return injector;
});
- return injector;
- });
+ } finally {
+ traceLeave(scope);
+ }
}
/**
diff --git a/lib/change_detection/watch_group.dart b/lib/change_detection/watch_group.dart
index f7bc83a3b..82e4052b9 100644
--- a/lib/change_detection/watch_group.dart
+++ b/lib/change_detection/watch_group.dart
@@ -2,11 +2,18 @@ library angular.watch_group;
import 'package:angular/change_detection/change_detection.dart';
import 'dart:collection';
+import 'package:angular/wtf.dart';
part 'linked_list.dart';
part 'ast.dart';
part 'prototype_map.dart';
+var _WatchGroup_detect = traceCreateScope('WatchGroup#detect()');
+var _WatchGroup_fields = traceCreateScope('WatchGroup#field()');
+var _WatchGroup_field_handler = traceCreateScope('WatchGroup#field_handler()');
+var _WatchGroup_eval = traceCreateScope('WatchGroup#eval()');
+var _WatchGroup_reaction = traceCreateScope('WatchGroup#reaction()');
+
/**
* A function that is notified of changes to the model.
*
@@ -392,11 +399,15 @@ class RootWatchGroup extends WatchGroup {
AvgStopwatch evalStopwatch,
AvgStopwatch processStopwatch}) {
// Process the Records from the change detector
+ var sDetect = traceEnter(_WatchGroup_detect);
+ var s = traceEnter(_WatchGroup_fields);
Iterator> changedRecordIterator =
(_changeDetector as ChangeDetector<_Handler>).collectChanges(
exceptionHandler:exceptionHandler,
stopwatch: fieldStopwatch);
+ traceLeave(s);
if (processStopwatch != null) processStopwatch.start();
+ s = traceEnter(_WatchGroup_field_handler);
while (changedRecordIterator.moveNext()) {
var record = changedRecordIterator.current;
if (changeLog != null) changeLog(record.handler.expression,
@@ -404,11 +415,13 @@ class RootWatchGroup extends WatchGroup {
record.previousValue);
record.handler.onChange(record);
}
+ traceLeave(s);
if (processStopwatch != null) processStopwatch.stop();
if (evalStopwatch != null) evalStopwatch.start();
// Process our own function evaluations
_EvalWatchRecord evalRecord = _evalWatchHead;
+ s = traceEnter(_WatchGroup_eval);
int evalCount = 0;
while (evalRecord != null) {
try {
@@ -423,11 +436,15 @@ class RootWatchGroup extends WatchGroup {
}
evalRecord = evalRecord._nextEvalWatch;
}
+
+ traceLeave(s);
+ traceLeave(sDetect);
if (evalStopwatch != null) evalStopwatch..stop()..increment(evalCount);
// Because the handler can forward changes between each other synchronously
// We need to call reaction functions asynchronously. This processes the
// asynchronous reaction function queue.
+ s = traceEnter(_WatchGroup_reaction);
int count = 0;
if (processStopwatch != null) processStopwatch.start();
Watch dirtyWatch = _dirtyWatchHead;
@@ -451,6 +468,7 @@ class RootWatchGroup extends WatchGroup {
_dirtyWatchTail = null;
root._removeCount = 0;
}
+ traceLeave(s);
if (processStopwatch != null) processStopwatch..stop()..increment(count);
return count;
}
diff --git a/lib/core/module_internal.dart b/lib/core/module_internal.dart
index 5b98206b0..2ad07a969 100644
--- a/lib/core/module_internal.dart
+++ b/lib/core/module_internal.dart
@@ -11,6 +11,7 @@ import 'package:di/annotations.dart';
import 'package:angular/core/parser/parser.dart';
import 'package:angular/core/parser/lexer.dart';
import 'package:angular/utils.dart';
+import 'package:angular/wtf.dart';
import 'package:angular/core/annotation_src.dart';
diff --git a/lib/core/parser/dynamic_parser.dart b/lib/core/parser/dynamic_parser.dart
index d1caf80f2..a46254ee9 100644
--- a/lib/core/parser/dynamic_parser.dart
+++ b/lib/core/parser/dynamic_parser.dart
@@ -3,7 +3,7 @@ library angular.core.parser.dynamic_parser;
import 'package:di/annotations.dart';
import 'package:angular/cache/module.dart';
import 'package:angular/core/annotation_src.dart' hide Formatter;
-import 'package:angular/core/module_internal.dart' show FormatterMap;
+import 'package:angular/core/formatter.dart' show FormatterMap;
import 'package:angular/core/parser/parser.dart';
import 'package:angular/core/parser/lexer.dart';
diff --git a/lib/core/parser/eval.dart b/lib/core/parser/eval.dart
index c8d3ce3c9..0beab7f33 100644
--- a/lib/core/parser/eval.dart
+++ b/lib/core/parser/eval.dart
@@ -2,7 +2,7 @@ library angular.core.parser.eval;
import 'package:angular/core/parser/syntax.dart' as syntax;
import 'package:angular/core/parser/utils.dart';
-import 'package:angular/core/module_internal.dart';
+import 'package:angular/core/formatter.dart' show FormatterMap;
export 'package:angular/core/parser/eval_access.dart';
export 'package:angular/core/parser/eval_calls.dart';
diff --git a/lib/core/parser/eval_access.dart b/lib/core/parser/eval_access.dart
index 0fdfa61c0..566a69834 100644
--- a/lib/core/parser/eval_access.dart
+++ b/lib/core/parser/eval_access.dart
@@ -3,7 +3,7 @@ library angular.core.parser.eval_access;
import 'package:angular/core/parser/parser.dart';
import 'package:angular/core/parser/syntax.dart' as syntax;
import 'package:angular/core/parser/utils.dart';
-import 'package:angular/core/module_internal.dart';
+import 'package:angular/core/formatter.dart' show FormatterMap;
class AccessScopeFast extends syntax.AccessScope with AccessFast {
final Getter getter;
diff --git a/lib/core/parser/eval_calls.dart b/lib/core/parser/eval_calls.dart
index 7ed2d143b..e1a2445bc 100644
--- a/lib/core/parser/eval_calls.dart
+++ b/lib/core/parser/eval_calls.dart
@@ -3,7 +3,7 @@ library angular.core.parser.eval_calls;
import 'package:angular/core/parser/parser.dart';
import 'package:angular/core/parser/syntax.dart' as syntax;
import 'package:angular/core/parser/utils.dart';
-import 'package:angular/core/module_internal.dart';
+import 'package:angular/core/formatter.dart' show FormatterMap;
class CallScope extends syntax.CallScope {
diff --git a/lib/core/parser/syntax.dart b/lib/core/parser/syntax.dart
index de2ebc70e..2213c4f35 100644
--- a/lib/core/parser/syntax.dart
+++ b/lib/core/parser/syntax.dart
@@ -3,7 +3,7 @@ library angular.core.parser.syntax;
import 'package:angular/core/parser/parser.dart' show LocalsWrapper;
import 'package:angular/core/parser/unparser.dart' show Unparser;
import 'package:angular/core/parser/utils.dart' show EvalError;
-import 'package:angular/core/module_internal.dart';
+import 'package:angular/core/formatter.dart' show FormatterMap;
abstract class Visitor {
visit(Expression expression) => expression.accept(this);
diff --git a/lib/core/parser/utils.dart b/lib/core/parser/utils.dart
index 82a898d7b..0da2df0e1 100644
--- a/lib/core/parser/utils.dart
+++ b/lib/core/parser/utils.dart
@@ -1,7 +1,7 @@
library angular.core.parser.utils;
import 'package:angular/core/parser/syntax.dart' show Expression;
-import 'package:angular/core/module_internal.dart';
+import 'package:angular/core/formatter.dart' show FormatterMap;
export 'package:angular/utils.dart' show relaxFnApply, relaxFnArgs, toBool;
/// Marker for an uninitialized value.
diff --git a/lib/core/scope.dart b/lib/core/scope.dart
index 914c02638..a8a286f41 100644
--- a/lib/core/scope.dart
+++ b/lib/core/scope.dart
@@ -3,6 +3,14 @@ part of angular.core_internal;
typedef EvalFunction0();
typedef EvalFunction1(context);
+var _Scope_apply = traceCreateScope('Scope#apply()');
+var _Scope_digest = traceCreateScope('Scope#digest()');
+var _Scope_flush = traceCreateScope('Scope#flush()');
+var _Scope_domWrite = traceCreateScope('Scope#domWrite()');
+var _Scope_domRead = traceCreateScope('Scope#domRead()');
+var _Scope_assert = traceCreateScope('Scope#assert()');
+var _Scope_runAsync = traceCreateScope('Scope#runAsync()');
+var _Scope_createChild = traceCreateScope('Scope#createChild()');
/**
* Injected into the listener function within [Scope.on] to provide event-specific details to the
* scope listener.
@@ -330,6 +338,7 @@ class Scope {
/// Creates a child [Scope] with the given [childContext]
Scope createChild(Object childContext) {
+ var s = traceEnter(_Scope_createChild);
assert(isAttached);
var child = new Scope(childContext, rootScope, this,
_readWriteGroup.newGroup(childContext),
@@ -341,6 +350,7 @@ class Scope {
child._prev = prev;
if (prev == null) _childHead = child; else prev._next = child;
_childTail = child;
+ traceLeave(s);
return child;
}
@@ -593,6 +603,7 @@ class RootScope extends Scope {
final ScopeStats _scopeStats;
String _state;
+ var _state_wtf_scope;
/**
* While processing data bindings, Angular passes through multiple states. When testing or
@@ -735,6 +746,7 @@ class RootScope extends Scope {
try {
do {
if (_domWriteHead != null) _stats.domWriteStart();
+ var s = traceEnter(_Scope_domWrite);
while (_domWriteHead != null) {
try {
_domWriteHead.fn();
@@ -744,6 +756,7 @@ class RootScope extends Scope {
_domWriteHead = _domWriteHead._next;
if (_domWriteHead == null) _stats.domWriteEnd();
}
+ traceLeave(s);
_domWriteTail = null;
if (runObservers) {
runObservers = false;
@@ -753,6 +766,7 @@ class RootScope extends Scope {
processStopwatch: _scopeStats.processStopwatch);
}
if (_domReadHead != null) _stats.domReadStart();
+ s = traceEnter(_Scope_domRead);
while (_domReadHead != null) {
try {
_domReadHead.fn();
@@ -763,6 +777,7 @@ class RootScope extends Scope {
if (_domReadHead == null) _stats.domReadEnd();
}
_domReadTail = null;
+ traceLeave(s);
_runAsyncFns();
} while (_domWriteHead != null || _domReadHead != null || _runAsyncHead != null);
_stats.flushEnd();
@@ -808,6 +823,7 @@ class RootScope extends Scope {
}
_runAsyncFns() {
+ var s = traceEnter(_Scope_runAsync);
var count = 0;
while (_runAsyncHead != null) {
try {
@@ -819,6 +835,7 @@ class RootScope extends Scope {
_runAsyncHead = _runAsyncHead._next;
}
_runAsyncTail = null;
+ traceLeave(s);
return count;
}
@@ -846,6 +863,13 @@ class RootScope extends Scope {
assert(isAttached);
if (_state != from) throw "$_state already in progress can not enter $to.";
_state = to;
+ if (_state_wtf_scope != null) traceLeave(_state_wtf_scope);
+ var wtfScope = null;
+ if (to == STATE_APPLY) wtfScope = _Scope_apply;
+ else if (to == STATE_DIGEST) wtfScope = _Scope_digest;
+ else if (to == STATE_FLUSH) wtfScope = _Scope_flush;
+ else if (to == STATE_FLUSH_ASSERT) wtfScope = _Scope_assert;
+ _state_wtf_scope = wtfScope == null ? null : traceEnter(wtfScope);
}
}
diff --git a/lib/core/zone.dart b/lib/core/zone.dart
index ba9cbdaf5..f0ea4aa03 100644
--- a/lib/core/zone.dart
+++ b/lib/core/zone.dart
@@ -39,6 +39,9 @@ class LongStackTrace {
}
}
+var _VmTurnZone_onRunBase = traceCreateScope('VmTurnZone#onRun()');
+var _VmTurnZone_onScheduleMicrotask = traceCreateScope('VmTurnZone#onScheduleMicrotask()');
+
/**
* A [Zone] wrapper that lets you schedule tasks after its private microtask
* queue is exhausted but before the next "turn", i.e. event loop iteration.
@@ -89,6 +92,7 @@ class VmTurnZone {
var _currentlyInTurn = false;
dynamic _onRunBase(async.Zone self, async.ZoneDelegate delegate, async.Zone zone, fn()) {
+ var scope = traceEnter(_VmTurnZone_onRunBase);
_runningInTurn++;
try {
if (!_currentlyInTurn) {
@@ -103,6 +107,7 @@ class VmTurnZone {
} finally {
_runningInTurn--;
if (_runningInTurn == 0) _finishTurn(zone, delegate);
+ traceLeave(scope);
}
}
@@ -115,8 +120,13 @@ class VmTurnZone {
_onRunBase(self, delegate, zone, () => delegate.runUnary(zone, fn, args));
void _onScheduleMicrotask(async.Zone self, async.ZoneDelegate delegate, async.Zone zone, fn()) {
- onScheduleMicrotask(() => delegate.run(zone, fn));
- if (_runningInTurn == 0 && !_inFinishTurn) _finishTurn(zone, delegate);
+ var s = traceEnter(_VmTurnZone_onScheduleMicrotask);
+ try {
+ onScheduleMicrotask(() => delegate.run(zone, fn));
+ if (_runningInTurn == 0 && !_inFinishTurn) _finishTurn(zone, delegate);
+ } finally {
+ traceLeave(s);
+ }
}
void _uncaughtError(async.Zone self, async.ZoneDelegate delegate, async.Zone zone,
diff --git a/lib/core_dom/compiler.dart b/lib/core_dom/compiler.dart
index e465dc72d..8f9557121 100644
--- a/lib/core_dom/compiler.dart
+++ b/lib/core_dom/compiler.dart
@@ -1,5 +1,8 @@
part of angular.core.dom_internal;
+var _Compiler_call = traceCreateScope('Compiler#call()');
+var _Compiler_subTemplate = traceCreateScope('Compiler#subTemplate()');
+
@Injectable()
class Compiler implements Function {
final Profiler _perf;
@@ -8,6 +11,7 @@ class Compiler implements Function {
Compiler(this._perf, this._expando);
ViewFactory call(List elements, DirectiveMap directives) {
+ var s = traceEnter(_Compiler_call);
var timerId;
assert((timerId = _perf.startTimer('ng.compile', _html(elements))) != false);
final elementBinders = [];
@@ -19,6 +23,7 @@ class Compiler implements Function {
elements, _removeUnusedBinders(elementBinders), _perf);
assert(_perf.stopTimer(timerId) != false);
+ traceLeave(s);
return viewFactory;
}
@@ -128,7 +133,7 @@ class Compiler implements Function {
isTopLevel, directParentElementBinder);
} while (domCursor.moveNext());
- return elementBinders;
+ return elementBinders;
}
ViewFactory _compileTransclusion(
@@ -136,6 +141,7 @@ class Compiler implements Function {
DirectiveRef directiveRef,
ElementBinder transcludedElementBinder,
DirectiveMap directives) {
+ var s = traceEnter(_Compiler_subTemplate);
var anchorName = directiveRef.annotation.selector +
(directiveRef.value != null ? '=' + directiveRef.value : '');
@@ -146,7 +152,7 @@ class Compiler implements Function {
var viewFactory = new ViewFactory(transcludeCursor.elements,
_removeUnusedBinders(elementBinders), _perf);
-
+ traceLeave(s);
return viewFactory;
}
diff --git a/lib/core_dom/directive.dart b/lib/core_dom/directive.dart
index 8738fe273..aa32724ce 100644
--- a/lib/core_dom/directive.dart
+++ b/lib/core_dom/directive.dart
@@ -22,7 +22,7 @@ class NodeAttrs {
NodeAttrs(this.element);
- operator [](String attrName) => element.attributes[attrName];
+ operator [](String attrName) => element.getAttribute(attrName);
void operator []=(String attrName, String value) {
if (_mustacheAttrs.containsKey(attrName)) {
@@ -31,7 +31,7 @@ class NodeAttrs {
if (value == null) {
element.attributes.remove(attrName);
} else {
- element.attributes[attrName] = value;
+ element.setAttribute(attrName, value);
}
if (_observers != null && _observers.containsKey(attrName)) {
@@ -86,9 +86,18 @@ class NodeAttrs {
* ShadowRoot is ready.
*/
class TemplateLoader {
- final async.Future template;
+ async.Future _template;
+ List _futures;
+ final dom.Node _shadowRoot;
- TemplateLoader(this.template);
+ TemplateLoader(this._shadowRoot, this._futures);
+
+ async.Future get template {
+ if (_template == null) {
+ _template = async.Future.wait(_futures).then((_) => _shadowRoot);
+ }
+ return _template;
+ }
}
class _MustacheAttr {
diff --git a/lib/core_dom/element_binder.dart b/lib/core_dom/element_binder.dart
index 814bd9148..5c90afe77 100644
--- a/lib/core_dom/element_binder.dart
+++ b/lib/core_dom/element_binder.dart
@@ -1,5 +1,8 @@
part of angular.core.dom_internal;
+var _ElementBinder_directive = traceCreateScope('ElementBinder#createDirective(ascii name)');
+var _ElementBinder_setupBindings = traceCreateScope('ElementBinder#setupBindings(ascii name)');
+
class TemplateElementBinder extends ElementBinder {
final DirectiveRef template;
ViewFactory templateViewFactory;
@@ -186,39 +189,53 @@ class ElementBinder {
}
void _link(DirectiveInjector directiveInjector, Scope scope, nodeAttrs) {
+ var s;
for(var i = 0; i < _usableDirectiveRefs.length; i++) {
DirectiveRef ref = _usableDirectiveRefs[i];
var key = ref.typeKey;
+ var wtfArgs = wtfEnabled ? [ref.typeKey.toString()] : null;
if (identical(key, TEXT_MUSTACHE_KEY) || identical(key, ATTR_MUSTACHE_KEY)) continue;
- var directive = directiveInjector.getByKey(ref.typeKey);
- if (ref.annotation is Controller) {
- scope.parentScope.context[(ref.annotation as Controller).publishAs] = directive;
+ s = traceEnter(_ElementBinder_directive, wtfArgs);
+ var directive;
+ try {
+ directive = directiveInjector.getByKey(ref.typeKey);
+ } finally {
+ traceLeave(s);
}
- var tasks = directive is AttachAware ? new _TaskList(() {
- if (scope.isAttached) directive.attach();
- }) : null;
+ s = traceEnter(_ElementBinder_setupBindings, wtfArgs);
+ try {
+ if (ref.annotation is Controller) {
+ scope.parentScope.context[(ref.annotation as Controller).publishAs] = directive;
+ }
- if (ref.mappings.isNotEmpty) {
- if (nodeAttrs == null) nodeAttrs = new _AnchorAttrs(ref);
- _createAttrMappings(directive, scope, ref.mappings, nodeAttrs, tasks);
- }
+ var tasks = directive is AttachAware ? new _TaskList(() {
+ if (scope.isAttached) directive.attach();
+ }) : null;
- if (directive is AttachAware) {
- var taskId = (tasks != null) ? tasks.registerTask() : 0;
- Watch watch;
- watch = scope.watch('1', // Cheat a bit.
- (_, __) {
- watch.remove();
- if (tasks != null) tasks.completeTask(taskId);
- });
- }
+ if (ref.mappings.isNotEmpty) {
+ if (nodeAttrs == null) nodeAttrs = new _AnchorAttrs(ref);
+ _createAttrMappings(directive, scope, ref.mappings, nodeAttrs, tasks);
+ }
- if (tasks != null) tasks.doneRegistering();
+ if (directive is AttachAware) {
+ var taskId = (tasks != null) ? tasks.registerTask() : 0;
+ Watch watch;
+ watch = scope.watch('1', // Cheat a bit.
+ (_, __) {
+ watch.remove();
+ if (tasks != null) tasks.completeTask(taskId);
+ });
+ }
+
+ if (tasks != null) tasks.doneRegistering();
- if (directive is DetachAware) {
- scope.on(ScopeEvent.DESTROY).listen((_) => directive.detach());
+ if (directive is DetachAware) {
+ scope.on(ScopeEvent.DESTROY).listen((_) => directive.detach());
+ }
+ } finally {
+ traceLeave(s);
}
}
}
diff --git a/lib/core_dom/http.dart b/lib/core_dom/http.dart
index 0cc6ae918..5123188b7 100644
--- a/lib/core_dom/http.dart
+++ b/lib/core_dom/http.dart
@@ -445,6 +445,7 @@ class Http {
cache,
timeout
}) {
+ var range = wtfEnabled ? traceAsyncStart('http:$method', url) : null;
if (timeout != null) {
throw ['timeout not implemented'];
}
@@ -541,9 +542,17 @@ class Http {
// Depending on the implementation of HttpBackend (e.g. with a local cache) the entire
// chain could finish synchronously with a non-Future result.
- return chainResult is async.Future
+ var result = chainResult is async.Future
? chainResult
: new async.Future.value(chainResult);
+ if (wtfEnabled) {
+ return new async.Future(() {
+ traceAsyncEnd(range);
+ return result;
+ });
+ } else {
+ return result;
+ }
}
/**
diff --git a/lib/core_dom/module_internal.dart b/lib/core_dom/module_internal.dart
index 61d132066..b7cd7bbbd 100644
--- a/lib/core_dom/module_internal.dart
+++ b/lib/core_dom/module_internal.dart
@@ -22,6 +22,7 @@ export 'package:angular/core_dom/directive_injector.dart' show DirectiveInjector
import 'package:angular/change_detection/watch_group.dart' show Watch, PrototypeMap;
import 'package:angular/change_detection/ast_parser.dart';
import 'package:angular/core/registry.dart';
+import 'package:angular/wtf.dart';
import 'package:angular/directive/module.dart' show NgBaseCss;
import 'dart:collection';
diff --git a/lib/core_dom/shadow_dom_component_factory.dart b/lib/core_dom/shadow_dom_component_factory.dart
index 7c8a48c9a..b6c5ae242 100644
--- a/lib/core_dom/shadow_dom_component_factory.dart
+++ b/lib/core_dom/shadow_dom_component_factory.dart
@@ -1,5 +1,8 @@
part of angular.core.dom_internal;
+var _ComponentFactory_call = traceCreateScope('ComponentFactory#call()');
+var _ComponentFactory_styles = traceCreateScope('ComponentFactory#styles()');
+
abstract class ComponentFactory {
BoundComponentFactory bind(DirectiveRef ref, directives);
}
@@ -11,7 +14,7 @@ abstract class BoundComponentFactory {
List get callArgs;
Function call(dom.Element element);
- static async.Future _viewFuture(
+ static async.Future _viewFactoryFuture(
Component component, ViewCache viewCache, DirectiveMap directives) {
if (component.template != null) {
return new async.Future.value(viewCache.fromHtml(component.template, directives));
@@ -58,34 +61,41 @@ class ShadowDomComponentFactory implements ComponentFactory {
class BoundShadowDomComponentFactory implements BoundComponentFactory {
- final ShadowDomComponentFactory _f;
+ final ShadowDomComponentFactory _componentFactory;
final DirectiveRef _ref;
final DirectiveMap _directives;
Component get _component => _ref.annotation as Component;
String _tag;
- async.Future> _styleElementsFuture;
- async.Future _viewFuture;
+ async.Future> _styleElementsFuture;
+ List _styleElements;
+ async.Future _shadowViewFactoryFuture;
+ ViewFactory _shadowViewFactory;
- BoundShadowDomComponentFactory(this._f, this._ref, this._directives) {
+ BoundShadowDomComponentFactory(this._componentFactory, this._ref, this._directives) {
_tag = _component.selector.toLowerCase();
- _styleElementsFuture = async.Future.wait(_component.cssUrls.map(_styleFuture));
+ _styleElementsFuture = async.Future.wait(_component.cssUrls.map(_urlToStyle))
+ ..then((stylesElements) => _styleElements = stylesElements);
- _viewFuture = BoundComponentFactory._viewFuture(
+ _shadowViewFactoryFuture = BoundComponentFactory._viewFactoryFuture(
_component,
- new PlatformViewCache(_f.viewCache, _tag, _f.platform),
+ // TODO(misko): Why do we create a new one per Component. This kind of defeats the caching.
+ new PlatformViewCache(_componentFactory.viewCache, _tag, _componentFactory.platform),
_directives);
+ if (_shadowViewFactoryFuture != null) {
+ _shadowViewFactoryFuture.then((viewFactory) => _shadowViewFactory = viewFactory);
+ }
}
- async.Future _styleFuture(cssUrl) {
- Http http = _f.http;
- TemplateCache templateCache = _f.templateCache;
- WebPlatform platform = _f.platform;
- ComponentCssRewriter componentCssRewriter = _f.componentCssRewriter;
- dom.NodeTreeSanitizer treeSanitizer = _f.treeSanitizer;
+ async.Future _urlToStyle(cssUrl) {
+ Http http = _componentFactory.http;
+ TemplateCache templateCache = _componentFactory.templateCache;
+ WebPlatform platform = _componentFactory.platform;
+ ComponentCssRewriter componentCssRewriter = _componentFactory.componentCssRewriter;
+ dom.NodeTreeSanitizer treeSanitizer = _componentFactory.treeSanitizer;
- return _f.styleElementCache.putIfAbsent(
+ return _componentFactory.styleElementCache.putIfAbsent(
new _ComponentAssetKey(_tag, cssUrl), () =>
http.get(cssUrl, cache: templateCache)
.then((resp) => resp.responseText,
@@ -107,7 +117,7 @@ class BoundShadowDomComponentFactory implements BoundComponentFactory {
// If the css shim is required, it means that scoping does not
// work, and adding the style to the head of the document is
- // preferrable.
+ // preferable.
if (platform.cssShimRequired) {
dom.document.head.append(styleElement);
return null;
@@ -124,62 +134,101 @@ class BoundShadowDomComponentFactory implements BoundComponentFactory {
Function call(dom.Element element) {
return (DirectiveInjector injector, Scope scope, NgBaseCss baseCss,
EventHandler eventHandler) {
- var shadowDom = element.createShadowRoot()
- ..applyAuthorStyles = _component.applyAuthorStyles
- ..resetStyleInheritance = _component.resetStyleInheritance;
-
- var shadowScope = scope.createChild(new HashMap()); // Isolate
-
- async.Future> cssFuture;
- if (_component.useNgBaseCss == true) {
- cssFuture = async.Future.wait(
- [async.Future.wait(baseCss.urls.map(_styleFuture)), _styleElementsFuture])
- .then((twoLists) {
- assert(twoLists.length == 2);
- return []..addAll(twoLists[0])..addAll(twoLists[1]);
- });
- } else {
- cssFuture = _styleElementsFuture;
- }
+ var s = traceEnter(_ComponentFactory_call);
+ try {
+ var shadowScope = scope.createChild(new HashMap()); // Isolate
+ ComponentDirectiveInjector shadowInjector;
+ dom.ShadowRoot shadowRoot = element.createShadowRoot();
+ shadowRoot
+ ..applyAuthorStyles = _component.applyAuthorStyles
+ ..resetStyleInheritance = _component.resetStyleInheritance;
+
+ List futures = [];
+ TemplateLoader templateLoader = new TemplateLoader(shadowRoot, futures);
+ shadowInjector = new ShadowDomComponentDirectiveInjector(
+ injector, injector.appInjector, shadowScope, templateLoader, shadowRoot);
+ shadowInjector.bindByKey(_ref.typeKey, _ref.factory, _ref.paramKeys,
+ _ref.annotation.visibility);
+ dom.Node firstViewNode = null;
+
+ // Load ngBase CSS
+ if (_component.useNgBaseCss == true && baseCss.urls.isNotEmpty) {
+ if (baseCss.styles == null) {
+ futures.add(async.Future
+ .wait(baseCss.urls.map(_urlToStyle))
+ .then((List cssList) {
+ baseCss.styles = cssList;
+ _insertCss(cssList, shadowRoot, shadowRoot.firstChild);
+ }));
+ } else {
+ _insertCss(baseCss.styles, shadowRoot, shadowRoot.firstChild);
+ }
+ }
+
+ if (_styleElementsFuture != null) {
+ if (_styleElements == null) {
+ futures.add(_styleElementsFuture .then((List styles) =>
+ _insertCss(styles, shadowRoot, firstViewNode)));
+ } else {
+ _insertCss(_styleElements, shadowRoot);
+ }
+ }
- ComponentDirectiveInjector shadowInjector;
-
- TemplateLoader templateLoader = new TemplateLoader(
- cssFuture.then((Iterable cssList) {
- cssList
- .where((styleElement) => styleElement != null)
- .forEach((styleElement) {
- shadowDom.append(styleElement.clone(true));
- });
- if (_viewFuture != null) {
- return _viewFuture.then((ViewFactory viewFactory) {
- if (shadowScope.isAttached) {
- shadowDom.nodes.addAll(
- viewFactory.call(shadowInjector.scope, shadowInjector).nodes);
- }
- return shadowDom;
- });
- }
- return shadowDom;
- }));
-
- var probe;
- shadowInjector = new ShadowDomComponentDirectiveInjector(injector, injector.appInjector,
- shadowScope, templateLoader, shadowDom);
- shadowInjector.bindByKey(_ref.typeKey, _ref.factory, _ref.paramKeys, _ref.annotation.visibility);
-
- if (_f.config.elementProbeEnabled) {
- probe = _f.expando[shadowDom] = shadowInjector.elementProbe;
- shadowScope.on(ScopeEvent.DESTROY).listen((ScopeEvent) => _f.expando[shadowDom] = null);
- }
- var controller = shadowInjector.getByKey(_ref.typeKey);
- BoundComponentFactory._setupOnShadowDomAttach(controller, templateLoader, shadowScope);
- shadowScope.context[_component.publishAs] = controller;
+ if (_shadowViewFactoryFuture != null) {
+ if (_shadowViewFactory == null) {
+ futures.add(_shadowViewFactoryFuture.then((ViewFactory viewFactory) =>
+ firstViewNode = _insertView(viewFactory, shadowRoot, shadowScope, shadowInjector)));
+ } else {
+ _insertView(_shadowViewFactory, shadowRoot, shadowScope, shadowInjector);
+ }
+ }
+
+ if (_componentFactory.config.elementProbeEnabled) {
+ ElementProbe probe = _componentFactory.expando[shadowRoot] = shadowInjector.elementProbe;
+ shadowScope.on(ScopeEvent.DESTROY).listen((ScopeEvent) => _componentFactory.expando[shadowRoot] = null);
+ }
+
+ var controller = shadowInjector.getByKey(_ref.typeKey);
+ BoundComponentFactory._setupOnShadowDomAttach(controller, templateLoader, shadowScope);
+ shadowScope.context[_component.publishAs] = controller;
- return controller;
+ return controller;
+ } finally {
+ traceLeave(s);
+ }
};
}
+
+ _insertCss(List cssList,
+ dom.ShadowRoot shadowRoot,
+ [dom.Node insertBefore = null]) {
+ var s = traceEnter(_ComponentFactory_styles);
+ for(int i = 0; i < cssList.length; i++) {
+ var styleElement = cssList[i];
+ if (styleElement != null) {
+ shadowRoot.insertBefore(styleElement.clone(true), insertBefore);
+ }
+ }
+ traceLeave(s);
+ }
+
+ dom.Node _insertView(ViewFactory viewFactory,
+ dom.ShadowRoot shadowRoot,
+ Scope shadowScope,
+ ShadowDomComponentDirectiveInjector shadowInjector) {
+ dom.Node first = null;
+ if (shadowScope.isAttached) {
+ View shadowView = viewFactory.call(shadowScope, shadowInjector);
+ List shadowViewNodes = shadowView.nodes;
+ for (var j = 0; j < shadowViewNodes.length; j++) {
+ var node = shadowViewNodes[j];
+ if (j == 0) first = node;
+ shadowRoot.append(node);
+ }
+ }
+ return first;
+ }
}
class _ComponentAssetKey {
diff --git a/lib/core_dom/transcluding_component_factory.dart b/lib/core_dom/transcluding_component_factory.dart
index 29f8f2663..cc0e40c73 100644
--- a/lib/core_dom/transcluding_component_factory.dart
+++ b/lib/core_dom/transcluding_component_factory.dart
@@ -87,13 +87,14 @@ class BoundTranscludingComponentFactory implements BoundComponentFactory {
final DirectiveMap _directives;
Component get _component => _ref.annotation as Component;
- async.Future _viewFuture;
+ async.Future _viewFactoryFuture;
+ ViewFactory _viewFactory;
BoundTranscludingComponentFactory(this._f, this._ref, this._directives) {
- _viewFuture = BoundComponentFactory._viewFuture(
- _component,
- _f.viewCache,
- _directives);
+ _viewFactoryFuture = BoundComponentFactory._viewFactoryFuture(_component, _f.viewCache, _directives);
+ if (_viewFactoryFuture != null) {
+ _viewFactoryFuture.then((viewFactory) => _viewFactory = viewFactory);
+ }
}
List get callArgs => _CALL_ARGS;
@@ -110,51 +111,39 @@ class BoundTranscludingComponentFactory implements BoundComponentFactory {
ViewCache viewCache, Http http, TemplateCache templateCache,
DirectiveMap directives, NgBaseCss baseCss, EventHandler eventHandler) {
- DirectiveInjector childInjector;
- var childInjectorCompleter; // Used if the ViewFuture is available before the childInjector.
-
- var component = _component;
+ List futures = [];
var contentPort = new ContentPort(element);
-
- // Append the component's template as children
- var elementFuture;
-
- if (_viewFuture != null) {
- elementFuture = _viewFuture.then((ViewFactory viewFactory) {
- contentPort.pullNodes();
- if (childInjector != null) {
- element.nodes.addAll(
- viewFactory.call(childInjector.scope, childInjector).nodes);
- return element;
- } else {
- childInjectorCompleter = new async.Completer();
- return childInjectorCompleter.future.then((childInjector) {
- element.nodes.addAll(
- viewFactory.call(childInjector.scope, childInjector).nodes);
- return element;
- });
- }
- });
- } else {
- elementFuture = new async.Future.microtask(() => contentPort.pullNodes());
- }
- TemplateLoader templateLoader = new TemplateLoader(elementFuture);
-
+ TemplateLoader templateLoader = new TemplateLoader(element, futures);
Scope shadowScope = scope.createChild(new HashMap());
-
- childInjector = new ShadowlessComponentDirectiveInjector(injector, injector.appInjector,
- eventHandler, shadowScope, templateLoader, new ShadowlessShadowRoot(element),
- contentPort);
+ DirectiveInjector childInjector = new ShadowlessComponentDirectiveInjector(
+ injector, injector.appInjector, eventHandler, shadowScope, templateLoader,
+ new ShadowlessShadowRoot(element), contentPort);
childInjector.bindByKey(_ref.typeKey, _ref.factory, _ref.paramKeys, _ref.annotation.visibility);
- if (childInjectorCompleter != null) {
- childInjectorCompleter.complete(childInjector);
- }
-
var controller = childInjector.getByKey(_ref.typeKey);
- shadowScope.context[component.publishAs] = controller;
+ shadowScope.context[_component.publishAs] = controller;
BoundComponentFactory._setupOnShadowDomAttach(controller, templateLoader, shadowScope);
+
+ if (_viewFactoryFuture != null && _viewFactory == null) {
+ futures.add(_viewFactoryFuture.then((ViewFactory viewFactory) =>
+ _insert(viewFactory, element, childInjector, contentPort)));
+ } else {
+ scope.rootScope.runAsync(() {
+ _insert(_viewFactory, element, childInjector, contentPort);
+ });
+ }
return controller;
};
}
+
+ _insert(ViewFactory viewFactory, dom.Element element, DirectiveInjector childInjector,
+ ContentPort contentPort) {
+ contentPort.pullNodes();
+ if (viewFactory != null) {
+ var viewNodes = viewFactory.call(childInjector.scope, childInjector).nodes;
+ for(var i = 0; i < viewNodes.length; i++) {
+ element.append(viewNodes[i]);
+ }
+ }
+ }
}
diff --git a/lib/core_dom/view_factory.dart b/lib/core_dom/view_factory.dart
index 9ef9cc1b4..a3e2f07d5 100644
--- a/lib/core_dom/view_factory.dart
+++ b/lib/core_dom/view_factory.dart
@@ -1,5 +1,8 @@
part of angular.core.dom_internal;
+var _ViewFactory_call = traceCreateScope('ViewFactory#call(ascii html)');
+var _ViewFactory_bind = traceCreateScope('ViewFactory#bind()');
+var _ViewFactory_querySelectorAll = traceCreateScope('ViewFactory#querySelectorAll()');
/**
* BoundViewFactory is a [ViewFactory] which does not need Injector because
@@ -24,10 +27,24 @@ class ViewFactory implements Function {
final List templateNodes;
final List nodeLinkingInfos;
final Profiler _perf;
+ List _wtfArgs;
ViewFactory(templateNodes, this.elementBinders, this._perf) :
nodeLinkingInfos = computeNodeLinkingInfos(templateNodes),
- templateNodes = templateNodes;
+ templateNodes = templateNodes
+ {
+ if (wtfEnabled) {
+ _wtfArgs = [templateNodes.map((dom.Node e) {
+ if (e is dom.Element) {
+ return (e as dom.Element).outerHtml;
+ } else if (e is dom.Comment) {
+ return '';
+ } else {
+ return e.text;
+ }
+ }).toList().join('')];
+ }
+ }
@deprecated
BoundViewFactory bind(DirectiveInjector directiveInjector) =>
@@ -37,21 +54,20 @@ class ViewFactory implements Function {
View call(Scope scope, DirectiveInjector directiveInjector,
[List nodes /* TODO: document fragment */]) {
+ var s = traceEnter(_ViewFactory_call, _wtfArgs);
assert(scope != null);
if (nodes == null) {
nodes = cloneElements(templateNodes);
}
- var timerId;
- try {
- assert((timerId = _perf.startTimer('ng.view')) != false);
- Animate animate = directiveInjector.getByKey(ANIMATE_KEY);
- EventHandler eventHandler = directiveInjector.getByKey(EVENT_HANDLER_KEY);
- var view = new View(nodes, scope, eventHandler);
- _link(view, scope, nodes, eventHandler, animate, directiveInjector);
- return view;
- } finally {
- assert(_perf.stopTimer(timerId) != false);
- }
+ Animate animate = directiveInjector.getByKey(ANIMATE_KEY);
+ EventHandler eventHandler = directiveInjector.getByKey(EVENT_HANDLER_KEY);
+ var view = new View(nodes, scope, eventHandler);
+ var sBind = traceEnter(_ViewFactory_bind);
+ _link(view, scope, nodes, eventHandler, animate, directiveInjector);
+ traceLeave(sBind);
+ traceLeave(s);
+
+ return view;
}
void _bindTagged(TaggedElementBinder tagged, int elementBinderIndex,
@@ -78,10 +94,12 @@ class ViewFactory implements Function {
}
elementInjectors[elementBinderIndex] = elementInjector;
- if (tagged.textBinders != null) {
- for (var k = 0; k < tagged.textBinders.length; k++) {
- TaggedTextBinder taggedText = tagged.textBinders[k];
- var childNode = boundNode.childNodes[taggedText.offsetIndex];
+ var textBinders = tagged.textBinders;
+ if (textBinders != null && textBinders.length > 0) {
+ var childNodes = boundNode.childNodes;
+ for (var k = 0; k < textBinders.length; k++) {
+ TaggedTextBinder taggedText = textBinders[k];
+ var childNode = childNodes[taggedText.offsetIndex];
taggedText.binder.bind(view, scope, elementInjector, childNode, eventHandler, animate);
}
}
@@ -97,15 +115,6 @@ class ViewFactory implements Function {
dom.Node node = nodeList[i];
NodeLinkingInfo linkingInfo = nodeLinkingInfos[i];
- // if node isn't attached to the DOM, create a parent for it.
- var parentNode = node.parentNode;
- var fakeParent = false;
- if (parentNode == null) {
- fakeParent = true;
- parentNode = new dom.DivElement();
- parentNode.append(node);
- }
-
if (linkingInfo.isElement) {
if (linkingInfo.containsNgBinding) {
var tagged = elementBinders[elementBinderIndex];
@@ -115,7 +124,9 @@ class ViewFactory implements Function {
}
if (linkingInfo.ngBindingChildren) {
+ var s = traceEnter(_ViewFactory_querySelectorAll);
var elts = (node as dom.Element).querySelectorAll('.ng-binding');
+ traceLeave(s);
for (int j = 0; j < elts.length; j++, elementBinderIndex++) {
TaggedElementBinder tagged = elementBinders[elementBinderIndex];
_bindTagged(tagged, elementBinderIndex, rootInjector, elementInjectors,
@@ -131,11 +142,6 @@ class ViewFactory implements Function {
}
elementBinderIndex++;
}
-
- if (fakeParent) {
- // extract the node from the parentNode.
- nodeList[i] = parentNode.nodes[0];
- }
}
return view;
}
diff --git a/lib/core_dom/web_platform.dart b/lib/core_dom/web_platform.dart
index f40845622..f30aa8c3d 100644
--- a/lib/core_dom/web_platform.dart
+++ b/lib/core_dom/web_platform.dart
@@ -42,7 +42,7 @@ class WebPlatform {
//
// TODO Remove the try-catch once https://github.com/angular/angular.dart/issues/1189 is fixed.
try {
- root.querySelectorAll("*").forEach((n) => n.attributes[selector] = "");
+ root.querySelectorAll("*").forEach((dom.Element n) => n.setAttribute(selector, ""));
} catch (e, s) {
print("WARNING: Failed to set up Shadow DOM shim for $selector.\n$e\n$s");
}
@@ -69,6 +69,7 @@ class PlatformViewCache implements ViewCache {
if (selector != null && selector != "" && platform.shadowDomShimRequired) {
// By adding a comment with the tag name we ensure the template html is unique per selector
// name when used as a key in the view factory cache.
+ //TODO(misko): This will always be miss, since we never put it in cache under such key.
viewFactory = viewFactoryCache.get("$html");
} else {
viewFactory = viewFactoryCache.get(html);
diff --git a/lib/directive/ng_base_css.dart b/lib/directive/ng_base_css.dart
index 74e41406d..34b9eade6 100644
--- a/lib/directive/ng_base_css.dart
+++ b/lib/directive/ng_base_css.dart
@@ -12,10 +12,14 @@ part of angular.directive;
selector: '[ng-base-css]',
visibility: Visibility.CHILDREN)
class NgBaseCss {
+ List styles;
List _urls = const [];
@NgAttr('ng-base-css')
- set urls(v) => _urls = v is List ? v : [v];
+ set urls(v) {
+ _urls = v is List ? v : [v];
+ styles = null;
+ }
List get urls => _urls;
}
diff --git a/lib/introspection.dart b/lib/introspection.dart
index 066d50612..c3b3712d0 100644
--- a/lib/introspection.dart
+++ b/lib/introspection.dart
@@ -277,7 +277,7 @@ class _Testability implements _JsObjectProxyable {
_Testability(this.node, this.probe);
whenStable(callback) {
- probe.injector.get(VmTurnZone).run(
+ (probe.injector.get(VmTurnZone) as VmTurnZone).run(
() => new async.Timer(Duration.ZERO, callback));
}
diff --git a/lib/wtf.dart b/lib/wtf.dart
new file mode 100644
index 000000000..b21b1152c
--- /dev/null
+++ b/lib/wtf.dart
@@ -0,0 +1,85 @@
+library angular.wtf;
+
+import "dart:profiler";
+
+bool wtfEnabled = false;
+dynamic /* JsObject */ _trace;
+dynamic /* JsObject */ _events;
+dynamic /* JsFunction */ _createScope;
+dynamic /* JsFunction */ _enterScope;
+dynamic /* JsFunction */ _leaveScope;
+dynamic /* JsFunction */ _beginTimeRange;
+dynamic /* JsFunction */ _endTimeRange;
+final List _arg1 = [null];
+final List _arg2 = [null, null];
+
+/**
+ * Use this method to initialize the WTF. It would be
+ * nice if this file could depend on dart:js, but that would
+ * make it not possible to refer to it in Dart VM. For this
+ * reason we expect the init caller to pass in the context
+ * JsObject.
+ */
+traceInit(dynamic /* JsObject */ context) {
+ if (context.hasProperty('wtf')) {
+ dynamic /* JsObject */ wtf = context['wtf'];
+ if (wtf.hasProperty('trace')) {
+ wtfEnabled = true;
+ _trace = wtf['trace'];
+ _events = _trace['events'];
+ _createScope = _events['createScope'];
+ _enterScope = _trace['enterScope'];
+ _leaveScope = _trace['leaveScope'];
+ _beginTimeRange = _trace['beginTimeRange'];
+ _endTimeRange = _trace['endTimeRange'];
+ }
+
+ }
+}
+
+// WTF.trace.events.createScope(string signature, opt_flags)
+dynamic /* JsFunction */ traceCreateScope(signature, [flags]) {
+ if (wtfEnabled) {
+ _arg2[0] = signature;
+ _arg2[1] = flags;
+ return _createScope.apply(_arg2, thisArg: _events);
+ } else {
+ return new UserTag(signature);
+ }
+}
+
+dynamic /* JsObject */ traceEnter(dynamic /* JsFunction */ scope, [args = const []]) {
+ if (wtfEnabled) {
+ return scope.apply(args);
+ } else {
+ return scope.makeCurrent();
+ }
+}
+
+dynamic /* JsObject */ traceLeave(dynamic /* JsObject */ scope) {
+ if (wtfEnabled) {
+ _arg1[0] = scope;
+ _leaveScope.apply(_arg1, thisArg: _trace);
+ } else {
+ scope.makeCurrent();
+ }
+}
+
+// WTF.trace.beginTimeRange('my.Type:job', actionName);
+dynamic /* JsObject */ traceAsyncStart(String rangeType, String action) {
+ if (wtfEnabled) {
+ _arg2[0] = rangeType;
+ _arg2[1] = action;
+ return _beginTimeRange.apply(_arg2, thisArg: _trace);
+ }
+ return null;
+}
+
+dynamic /* JsObject */ traceAsyncEnd(dynamic /* JsObject */ range) {
+ if (wtfEnabled) {
+ _arg1[0] = range;
+ return _endTimeRange.apply(_arg1, thisArg: _trace);
+ }
+ return null;
+}
+
diff --git a/test/core_dom/compiler_spec.dart b/test/core_dom/compiler_spec.dart
index 6e637fa29..916225cdc 100644
--- a/test/core_dom/compiler_spec.dart
+++ b/test/core_dom/compiler_spec.dart
@@ -872,7 +872,7 @@ void main() {
}));
/*
- This test is dissabled becouse I (misko) thinks it has no real use case. It is easier
+ This test is disabled because I (misko) thinks it has no real use case. It is easier
to understand in terms of ng-repeat
@@ -886,7 +886,7 @@ void main() {
the DirectChild between tabs and pane.
It is not clear to me (misko) that there is a use case for getting hold of the
- tranrscluding directive such a ng-repeat.
+ transcluding directive such a ng-repeat.
*/
xit('should reuse controllers for transclusions', async((Logger log) {
_.compile('view
');
diff --git a/test/directive/ng_base_css_spec.dart b/test/directive/ng_base_css_spec.dart
index bc56214be..73225445a 100644
--- a/test/directive/ng_base_css_spec.dart
+++ b/test/directive/ng_base_css_spec.dart
@@ -23,22 +23,35 @@ main() => describe('NgBaseCss', () {
..bind(_NoBaseCssComponent);
});
- it('should load css urls from ng-base-css', async((TestBed _, MockHttpBackend backend) {
+ it('should load css urls from ng-base-css', async((TestBed _, MockHttpBackend backend,
+ DirectiveMap directiveMap) {
backend
..expectGET('simple.css').respond(200, '.simple{}')
..expectGET('simple.html').respond(200, 'Simple!
')
..expectGET('base.css').respond(200, '.base{}');
- var element = e('ignore
');
- _.compile(element);
+ NgBaseCss ngBaseCss = new NgBaseCss();
+ ngBaseCss.urls = 'base.css';
+ DirectiveInjector directiveInjector = new DirectiveInjector(
+ _.directiveInjector, _.injector, null, null, null, null, null);
+ directiveInjector.bind(NgBaseCss, toValue: ngBaseCss, visibility: Visibility.CHILDREN);
+ var elements = es('ignore
');
+ ViewFactory viewFactory = _.compiler(elements, directiveMap);
+ View view = viewFactory.call(_.rootScope, directiveInjector);
microLeap();
backend.flush();
microLeap();
- expect(element.children[0].shadowRoot).toHaveHtml(
- 'Simple!
'
- );
+ expect((view.nodes[0].firstChild as Element).shadowRoot).toHaveHtml(
+ 'Simple!
');
+ expect(ngBaseCss.styles.first.innerHtml).toEqual('.base{}');
+
+ // Now it should be sync
+ view = viewFactory.call(_.rootScope, directiveInjector);
+ expect((view.nodes[0].firstChild as Element).shadowRoot).toHaveHtml(
+ 'Simple!
');
+
}));
it('ng-base-css should overwrite parent ng-base-csses', async((TestBed _, MockHttpBackend backend) {