diff --git a/lib/core/annotation_src.dart b/lib/core/annotation_src.dart index 1c1796d12..ee5270362 100644 --- a/lib/core/annotation_src.dart +++ b/lib/core/annotation_src.dart @@ -308,6 +308,13 @@ class Component extends Directive { @deprecated final String publishAs; + /** + * If set to true, this component will always use shadow DOM. + * If set to false, this component will never use shadow DOM. + * If unset, the compiler's default construction strategy will be used + */ + final bool useShadowDom; + const Component({ this.template, this.templateUrl, @@ -320,7 +327,8 @@ class Component extends Directive { selector, visibility, exportExpressions, - exportExpressionAttrs}) + exportExpressionAttrs, + this.useShadowDom}) : _cssUrls = cssUrl, _applyAuthorStyles = applyAuthorStyles, _resetStyleInheritance = resetStyleInheritance, @@ -349,7 +357,8 @@ class Component extends Directive { selector: selector, visibility: visibility, exportExpressions: exportExpressions, - exportExpressionAttrs: exportExpressionAttrs); + exportExpressionAttrs: exportExpressionAttrs, + useShadowDom: useShadowDom); } /** diff --git a/lib/core_dom/element_binder.dart b/lib/core_dom/element_binder.dart index 13035f6fc..dd01377ac 100644 --- a/lib/core_dom/element_binder.dart +++ b/lib/core_dom/element_binder.dart @@ -14,9 +14,13 @@ class TemplateElementBinder extends ElementBinder { return _directiveCache = [template]; } - TemplateElementBinder(_perf, _expando, _parser, _componentFactory, this.template, this.templateBinder, + TemplateElementBinder(perf, expando, parser, componentFactory, + transcludingComponentFactory, shadowDomComponentFactory, + this.template, this.templateBinder, onEvents, bindAttrs, childMode) - : super(_perf, _expando, _parser, _componentFactory, null, null, onEvents, bindAttrs, childMode); + : super(perf, expando, parser, componentFactory, + transcludingComponentFactory, shadowDomComponentFactory, + null, null, onEvents, bindAttrs, childMode); String toString() => "[TemplateElementBinder template:$template]"; @@ -41,7 +45,11 @@ class ElementBinder { final Profiler _perf; final Expando _expando; final Parser _parser; + + // The default component factory final ComponentFactory _componentFactory; + final TranscludingComponentFactory _transcludingComponentFactory; + final ShadowDomComponentFactory _shadowDomComponentFactory; final Map onEvents; final Map bindAttrs; @@ -53,7 +61,11 @@ class ElementBinder { // Can be either COMPILE_CHILDREN or IGNORE_CHILDREN final String childMode; - ElementBinder(this._perf, this._expando, this._parser, this._componentFactory, this.component, this.decorators, + ElementBinder(this._perf, this._expando, this._parser, + this._componentFactory, + this._transcludingComponentFactory, + this._shadowDomComponentFactory, + this.component, this.decorators, this.onEvents, this.bindAttrs, this.childMode); final bool hasTemplate = false; @@ -215,7 +227,16 @@ class ElementBinder { } nodesAttrsDirectives.add(ref); } else if (ref.annotation is Component) { - nodeModule.factory(ref.type, _componentFactory.call(node, ref), visibility: visibility); + var factory; + var annotation = ref.annotation as Component; + if (annotation.useShadowDom == true) { + factory = _shadowDomComponentFactory; + } else if (annotation.useShadowDom == false) { + factory = _transcludingComponentFactory; + } else { + factory = _componentFactory; + } + nodeModule.factory(ref.type, factory.call(node, ref), visibility: visibility); } else { nodeModule.type(ref.type, visibility: visibility); } diff --git a/lib/core_dom/element_binder_builder.dart b/lib/core_dom/element_binder_builder.dart index 12c4fbcb5..050c53c18 100644 --- a/lib/core_dom/element_binder_builder.dart +++ b/lib/core_dom/element_binder_builder.dart @@ -6,17 +6,22 @@ class ElementBinderFactory { final Profiler _perf; final Expando _expando; final ComponentFactory _componentFactory; + final TranscludingComponentFactory _transcludingComponentFactory; + final ShadowDomComponentFactory _shadowDomComponentFactory; - ElementBinderFactory(this._parser, this._perf, this._expando, this._componentFactory); + ElementBinderFactory(this._parser, this._perf, this._expando, this._componentFactory, + this._transcludingComponentFactory, this._shadowDomComponentFactory); // TODO: Optimize this to re-use a builder. ElementBinderBuilder builder() => new ElementBinderBuilder(this); ElementBinder binder(ElementBinderBuilder b) => new ElementBinder(_perf, _expando, _parser, _componentFactory, + _transcludingComponentFactory, _shadowDomComponentFactory, b.component, b.decorators, b.onEvents, b.bindAttrs, b.childMode); TemplateElementBinder templateBinder(ElementBinderBuilder b, ElementBinder transclude) => new TemplateElementBinder(_perf, _expando, _parser, _componentFactory, + _transcludingComponentFactory, _shadowDomComponentFactory, b.template, transclude, b.onEvents, b.bindAttrs, b.childMode); } diff --git a/lib/core_dom/module_internal.dart b/lib/core_dom/module_internal.dart index a94445170..810f2f6a7 100644 --- a/lib/core_dom/module_internal.dart +++ b/lib/core_dom/module_internal.dart @@ -57,6 +57,8 @@ class CoreDomModule extends Module { type(Compiler, implementedBy: TaggingCompiler); type(ComponentFactory, implementedBy: ShadowDomComponentFactory); + type(ShadowDomComponentFactory); + type(TranscludingComponentFactory); type(Content); value(ContentPort, null); diff --git a/test/core/annotation_src_spec.dart b/test/core/annotation_src_spec.dart index 4fabc414d..c3d7f8e44 100644 --- a/test/core/annotation_src_spec.dart +++ b/test/core/annotation_src_spec.dart @@ -45,7 +45,8 @@ void main() => describe('annotations', () { selector: '', visibility: Directive.LOCAL_VISIBILITY, exportExpressions: [], - exportExpressionAttrs: [] + exportExpressionAttrs: [], + useShadowDom: true ); // Check that no fields are null diff --git a/test/core_dom/compiler_spec.dart b/test/core_dom/compiler_spec.dart index 5fb4002ac..f86eea9aa 100644 --- a/test/core_dom/compiler_spec.dart +++ b/test/core_dom/compiler_spec.dart @@ -618,6 +618,35 @@ void main() { }).toThrow('Unknown selector format \'buttonbar button\' for InvalidSelector'); }); }); + + describe('useShadowDom option', () { + beforeEachModule((Module m) { + m.type(ShadowyComponent); + m.type(ShadowlessComponent); + }); + + it('should create shadowy components', async((Logger log) { + _.compile(''); + expect(log).toEqual(['shadowy']); + expect(_.rootElement.shadowRoot).toBeNotNull(); + })); + + it('should create shadowless components', async((Logger log) { + _.compile(''); + expect(log).toEqual(['shadowless']); + expect(_.rootElement.shadowRoot).toBeNull(); + })); + + it('should create other components with the default strategy', async((ComponentFactory factory) { + _.compile(''); + if (factory is TranscludingComponentFactory) { + expect(_.rootElement.shadowRoot).toBeNull(); + } else { + expect(factory is ShadowDomComponentFactory).toBeTruthy(); + expect(_.rootElement.shadowRoot).toBeNotNull(); + } + })); + }); }); @@ -807,6 +836,27 @@ class SimpleComponent { } } +@Component( + selector: 'shadowy', + template: r'With shadow DOM', + useShadowDom: true +) +class ShadowyComponent { + ShadowyComponent(Logger log) { + log('shadowy'); + } +} + +@Component( + selector: 'shadowless', + template: r'Without shadow DOM', + useShadowDom: false +) +class ShadowlessComponent { + ShadowlessComponent(Logger log) { + log('shadowless'); + } +} @Component( selector: 'sometimes',