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

Commit cf2c496

Browse files
committed
feat(components): change shadow boundary to ignore duplicate styles
1 parent 3b174c0 commit cf2c496

File tree

5 files changed

+68
-14
lines changed

5 files changed

+68
-14
lines changed

lib/core_dom/directive_injector.dart

+6-10
Original file line numberDiff line numberDiff line change
@@ -169,18 +169,11 @@ class DirectiveInjector implements DirectiveBinder {
169169
static Binding _tempBinding = new Binding();
170170

171171
DirectiveInjector(DirectiveInjector parent, appInjector, this._node, this._nodeAttrs,
172-
this._eventHandler, this.scope, this._animate, [View view, ShadowBoundary boundary])
172+
this._eventHandler, this.scope, this._animate, [View view, this._shadowBoundary])
173173
: _parent = parent,
174174
_appInjector = appInjector,
175175
_view = view == null && parent != null ? parent._view : view,
176-
_constructionDepth = _NO_CONSTRUCTION,
177-
_shadowBoundary = _getShadowBoundary(boundary, parent);
178-
179-
static _getShadowBoundary(ShadowBoundary boundary, DirectiveInjector parent) {
180-
if (boundary != null) return boundary;
181-
if (parent != null) return parent._shadowBoundary;
182-
return new DefaultShadowBoundary();
183-
}
176+
_constructionDepth = _NO_CONSTRUCTION;
184177

185178
void bind(key, {dynamic toValue: DEFAULT_VALUE,
186179
Function toFactory: DEFAULT_VALUE,
@@ -317,7 +310,7 @@ class DirectiveInjector implements DirectiveBinder {
317310
case ELEMENT_PROBE_KEY_ID: return elementProbe;
318311
case NG_ELEMENT_KEY_ID: return ngElement;
319312
case EVENT_HANDLER_KEY_ID: return _eventHandler;
320-
case SHADOW_BOUNDARY_KEY_ID: return _shadowBoundary;
313+
case SHADOW_BOUNDARY_KEY_ID: return _findShadowBoundary();
321314
case DESTINATION_LIGHT_DOM_KEY_ID: return _destLightDom;
322315
case SOURCE_LIGHT_DOM_KEY_ID: return _sourceLightDom;
323316
case VIEW_KEY_ID: return _view;
@@ -408,6 +401,9 @@ class DirectiveInjector implements DirectiveBinder {
408401
}
409402

410403
DestinationLightDom get _destLightDom => _parent == null ? null : _parent.lightDom;
404+
405+
ShadowBoundary _findShadowBoundary() =>
406+
_shadowBoundary != null ? _shadowBoundary : getFromParentByKey(SHADOW_BOUNDARY_KEY);
411407
}
412408

413409
class TemplateDirectiveInjector extends DirectiveInjector {

lib/core_dom/module_internal.dart

+1
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ class CoreDomModule extends Module {
104104
bind(ElementBinderFactory);
105105
bind(NgElement);
106106
bind(EventHandler);
107+
bind(ShadowBoundary, toImplementation: DefaultShadowBoundary);
107108
// TODO(rkirov): remove this once clients have stopped relying on it.
108109
bind(DirectiveInjector, toValue: null);
109110
}

lib/core_dom/shadow_boundary.dart

+19-4
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,40 @@ part of angular.core.dom_internal;
77
*/
88
@Injectable()
99
abstract class ShadowBoundary {
10+
final Set<dom.StyleElement> _insertedStyles = new Set();
11+
1012
void insertStyleElements(List<dom.StyleElement> elements);
13+
14+
Iterable<dom.StyleElement> _newStyles(Iterable<dom.StyleElement> elements) =>
15+
elements.where((el) => !_insertedStyles.contains(el));
16+
17+
void _addInsertedStyles(Iterable<dom.StyleElement> elements) {
18+
_insertedStyles.addAll(elements);
19+
}
1120
}
1221

1322
@Injectable()
14-
class DefaultShadowBoundary implements ShadowBoundary {
23+
class DefaultShadowBoundary extends ShadowBoundary {
1524
void insertStyleElements(List<dom.StyleElement> elements) {
16-
final cloned = elements.map((el) => el.clone(true));
25+
final newStyles = _newStyles(elements);
26+
final cloned = newStyles.map((el) => el.clone(true));
1727
dom.document.head.nodes.addAll(cloned);
28+
_addInsertedStyles(newStyles);
1829
}
1930
}
2031

2132
@Injectable()
22-
class ShadowRootBoundary implements ShadowBoundary {
33+
class ShadowRootBoundary extends ShadowBoundary {
2334
final dom.ShadowRoot shadowRoot;
2435
dom.StyleElement _lastStyleElement;
2536

2637
ShadowRootBoundary(this.shadowRoot);
2738

2839
void insertStyleElements(List<dom.StyleElement> elements) {
2940
if (elements.isEmpty) return;
30-
final cloned = elements.map((el) => el.clone(true));
41+
42+
final newStyles = _newStyles(elements);
43+
final cloned = newStyles.map((el) => el.clone(true));
3144

3245
cloned.forEach((style) {
3346
if (_lastStyleElement != null) {
@@ -38,5 +51,7 @@ class ShadowRootBoundary implements ShadowBoundary {
3851
_lastStyleElement = shadowRoot.append(style);
3952
}
4053
});
54+
55+
_addInsertedStyles(newStyles);
4156
}
4257
}

test/core_dom/directive_injector_spec.dart

+30
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ library directive_injector_spec;
33
import '../_specs.dart';
44
import 'package:angular/core_dom/directive_injector.dart';
55
import 'package:angular/core_dom/static_keys.dart';
6+
import 'dart:html' as dom;
67

78
void main() {
89
describe('DirectiveInjector', () {
@@ -108,6 +109,35 @@ void main() {
108109
});
109110
});
110111

112+
describe("returning ShadowBoundary", () {
113+
it('should return the shadow bounary of the injector', () {
114+
final root = new dom.DivElement().createShadowRoot();
115+
final boundary = new ShadowRootBoundary(root);
116+
final childInjector = new DirectiveInjector(injector, null, null, null, null, null,
117+
null, null, boundary);
118+
119+
expect(childInjector.getByKey(SHADOW_BOUNDARY_KEY)).toBe(boundary);
120+
});
121+
122+
it('should return the shadow bounary of the parent injector', () {
123+
final root = new dom.DivElement().createShadowRoot();
124+
final boundary = new ShadowRootBoundary(root);
125+
final parentInjector = new DirectiveInjector(injector, null, null, null, null, null,
126+
null, null, boundary);
127+
final childInjector = new DirectiveInjector(parentInjector, null, null, null, null, null,
128+
null, null, null);
129+
130+
expect(childInjector.getByKey(SHADOW_BOUNDARY_KEY)).toBe(boundary);
131+
});
132+
133+
it('should throw we cannot find a shadow boundary', () {
134+
final childInjector = new DirectiveInjector(injector, null, null, null, null, null,
135+
null, null, null);
136+
137+
expect(() => childInjector.getByKey(SHADOW_BOUNDARY_KEY)).toThrow("No provider found");
138+
});
139+
});
140+
111141
describe('error handling', () {
112142
it('should throw circular dependency error', () {
113143
addDirective(_TypeC0);

test/core_dom/shadow_boundary_spec.dart

+12
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,18 @@ main() {
3131

3232
expect(root).toHaveText(".style1{}adiv");
3333
});
34+
35+
it("should not insert the same style element twice", () {
36+
final root = new dom.DivElement().createShadowRoot();
37+
final boundary = new ShadowRootBoundary(root);
38+
39+
final s = new dom.StyleElement()..text = ".style1{}";
40+
41+
boundary.insertStyleElements([s]);
42+
boundary.insertStyleElements([s]);
43+
44+
expect(root).toHaveText(".style1{}");
45+
});
3446
});
3547
});
3648
}

0 commit comments

Comments
 (0)