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

Commit a3bc7d4

Browse files
vsavkinVictor Savkin
authored and
Victor Savkin
committed
feat(components): change shadow boundary to ignore duplicate styles
Closes #1423
1 parent a943370 commit a3bc7d4

File tree

5 files changed

+71
-14
lines changed

5 files changed

+71
-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
@@ -111,6 +111,7 @@ class CoreDomModule extends Module {
111111
bind(ElementBinderFactory);
112112
bind(NgElement);
113113
bind(EventHandler);
114+
bind(ShadowBoundary, toImplementation: DefaultShadowBoundary);
114115
// TODO(rkirov): remove this once clients have stopped relying on it.
115116
bind(DirectiveInjector, toValue: null);
116117
}

lib/core_dom/shadow_boundary.dart

+22-4
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,43 @@ part of angular.core.dom_internal;
66
* [ShadowBoundary] is responsible for inserting style elements.
77
*/
88
abstract class ShadowBoundary {
9+
Set<dom.StyleElement> _insertedStyles;
10+
911
void insertStyleElements(List<dom.StyleElement> elements);
12+
13+
Iterable<dom.StyleElement> _newStyles(Iterable<dom.StyleElement> elements) {
14+
if (_insertedStyles == null) return elements;
15+
return elements.where((el) => !_insertedStyles.contains(el));
16+
}
17+
18+
void _addInsertedStyles(Iterable<dom.StyleElement> elements) {
19+
if (_insertedStyles == null) _insertedStyles = new Set();
20+
_insertedStyles.addAll(elements);
21+
}
1022
}
1123

1224
@Injectable()
13-
class DefaultShadowBoundary implements ShadowBoundary {
25+
class DefaultShadowBoundary extends ShadowBoundary {
1426
void insertStyleElements(List<dom.StyleElement> elements) {
15-
final cloned = elements.map((el) => el.clone(true));
27+
final newStyles = _newStyles(elements);
28+
final cloned = newStyles.map((el) => el.clone(true));
1629
dom.document.head.nodes.addAll(cloned);
30+
_addInsertedStyles(newStyles);
1731
}
1832
}
1933

2034
@Injectable()
21-
class ShadowRootBoundary implements ShadowBoundary {
35+
class ShadowRootBoundary extends ShadowBoundary {
2236
final dom.ShadowRoot shadowRoot;
2337
dom.StyleElement _lastStyleElement;
2438

2539
ShadowRootBoundary(this.shadowRoot);
2640

2741
void insertStyleElements(List<dom.StyleElement> elements) {
2842
if (elements.isEmpty) return;
29-
final cloned = elements.map((el) => el.clone(true));
43+
44+
final newStyles = _newStyles(elements);
45+
final cloned = newStyles.map((el) => el.clone(true));
3046

3147
cloned.forEach((style) {
3248
if (_lastStyleElement != null) {
@@ -37,5 +53,7 @@ class ShadowRootBoundary implements ShadowBoundary {
3753
_lastStyleElement = shadowRoot.append(style);
3854
}
3955
});
56+
57+
_addInsertedStyles(newStyles);
4058
}
4159
}

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)