Skip to content
This repository has been archived by the owner on Feb 22, 2018. It is now read-only.

Commit

Permalink
fix(element binder): fix memory leak with expando value holding onto …
Browse files Browse the repository at this point in the history
…node

Expando keys are weak references but the values are not.  The
ElementProbe value holds strong references to the node and the injector
keep it from being garbage collected.
  • Loading branch information
chirayuk committed May 30, 2014
1 parent 317c23c commit b7f175b
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 3 deletions.
1 change: 1 addition & 0 deletions lib/core_dom/element_binder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ class ElementBinder {
nodeInjector = parentInjector.createChild([nodeModule]);
probe = _expando[node] = new ElementProbe(
parentInjector.getByKey(_ELEMENT_PROBE_KEY), node, nodeInjector, scope);
scope.on(ScopeEvent.DESTROY).listen((_) {_expando[node] = null;});

_link(nodeInjector, probe, scope, nodeAttrs, formatters);

Expand Down
5 changes: 3 additions & 2 deletions lib/core_dom/shadow_dom_component_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -179,17 +179,18 @@ class _ComponentFactory implements Function {

Injector createShadowInjector(injector, TemplateLoader templateLoader) {
var probe;
var shadowModule = new Module()
var shadowModule = new Module()
..bindByKey(typeKey)
..bindByKey(_NG_ELEMENT_KEY)
..bindByKey(_EVENT_HANDLER_KEY, toImplementation: ShadowRootEventHandler)
..bindByKey(_SCOPE_KEY, toValue: shadowScope)
..bindByKey(_TEMPLATE_LOADER_KEY, toValue: templateLoader)
..bindByKey(_SHADOW_ROOT_KEY, toValue: shadowDom)
..bindByKey(_ELEMENT_PROBE, toFactory: (_) => probe);
shadowInjector = injector.createChild([shadowModule], name: SHADOW_DOM_INJECTOR_NAME);
shadowInjector = injector.createChild([shadowModule], name: SHADOW_DOM_INJECTOR_NAME);
probe = _expando[shadowDom] = new ElementProbe(
injector.getByKey(_ELEMENT_PROBE), shadowDom, shadowInjector, shadowScope);
shadowScope.on(ScopeEvent.DESTROY).listen((ScopeEvent) {_expando[shadowDom] = null;});
return shadowInjector;
}
}
Expand Down
33 changes: 32 additions & 1 deletion test/core_dom/compiler_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,28 @@ void main() {
expect(_.rootElement.shadowRoot).toBeNotNull();
}
}));

describe('expando memory', () {
Expando expando;

beforeEach(inject((Expando _expando) => expando = _expando));

['shadowy', 'shadowless'].forEach((selector) {
it('should release expando when a node is freed ($selector)', async(() {
_.rootScope.context['flag'] = true;
_.compile('<div><div ng-if=flag><$selector>x</$selector></div></div>');
microLeap(); _.rootScope.apply();
var element = _.rootElement.querySelector('$selector');
if (element.shadowRoot != null) {
element = element.shadowRoot;
}
expect(expando[element]).not.toEqual(null);
_.rootScope.context['flag'] = false;
microLeap(); _.rootScope.apply();
expect(expando[element]).toEqual(null);
}));
});
});
});

describe('bindings', () {
Expand Down Expand Up @@ -849,7 +871,6 @@ void main() {
expect(_.rootElement.text).toEqual('MyController');
});
});

}));
}

Expand Down Expand Up @@ -975,6 +996,16 @@ class SimpleComponent {
}
}

@Component(
selector: 'simple2',
template: r'{{name}}(<content>SHADOW-CONTENT</content>)')
class Simple2Component {
Scope scope;
Simple2Component(Scope this.scope) {
scope.context['name'] = 'INNER';
}
}

@Component(
selector: 'shadowy',
template: r'With shadow DOM',
Expand Down

0 comments on commit b7f175b

Please sign in to comment.