diff --git a/packages/compiler/src/shadow_css.ts b/packages/compiler/src/shadow_css.ts index e8d08e7adbc87..15e805c00f3c9 100644 --- a/packages/compiler/src/shadow_css.ts +++ b/packages/compiler/src/shadow_css.ts @@ -374,11 +374,40 @@ export class ShadowCss { rule.selector.startsWith('@media') || rule.selector.startsWith('@supports') || rule.selector.startsWith('@page') || rule.selector.startsWith('@document')) { content = this._scopeSelectors(rule.content, scopeSelector, hostSelector); + } else if (rule.selector.startsWith('@font-face')) { + content = this._stripScopingSelectors(rule.content, scopeSelector, hostSelector); } return new CssRule(selector, content); }); } + /** + * Handle a css text that is within a rule that should not contain scope selectors by simply + * removing them! An example of such a rule is `@font-face`. + * + * `@font-face` rules cannot contain nested selectors. Nor can they be nested under a selector. + * Normally this would be a syntax error by the author of the styles. But in some rare cases, such + * as importing styles from a library, and applying `:host ::ng-deep` to the imported styles, we + * can end up with broken css if the imported styles happen to contain @font-face rules. + * + * For example: + * + * ``` + * :host ::ng-deep { + * import 'some/lib/containing/font-face'; + * } + * ``` + */ + private _stripScopingSelectors(cssText: string, scopeSelector: string, hostSelector: string): + string { + return processRules(cssText, rule => { + const selector = rule.selector.replace(_shadowDeepSelectors, ' ') + .replace(_polyfillHostNoCombinatorRe, ' '); + const content = this._scopeSelectors(rule.content, scopeSelector, hostSelector); + return new CssRule(selector, content); + }); + } + private _scopeSelector( selector: string, scopeSelector: string, hostSelector: string, strict: boolean): string { return selector.split(',') diff --git a/packages/compiler/test/shadow_css_spec.ts b/packages/compiler/test/shadow_css_spec.ts index 32cd76dbab9eb..16ddc93d0f61a 100644 --- a/packages/compiler/test/shadow_css_spec.ts +++ b/packages/compiler/test/shadow_css_spec.ts @@ -369,6 +369,18 @@ import {normalizeCSS} from '@angular/platform-browser/testing/src/browser_util'; expect(s(css, 'contenta', 'h')).toEqual('[h] > > .x {}'); }); + it('should strip ::ng-deep and :host from within @font-face', () => { + expect(s('@font-face { font-family {} }', 'contenta', 'h')) + .toEqual('@font-face { font-family {}}'); + expect(s('@font-face { ::ng-deep font-family{} }', 'contenta', 'h')) + .toEqual('@font-face { font-family{}}'); + expect(s('@font-face { :host ::ng-deep font-family{} }', 'contenta', 'h')) + .toEqual('@font-face { font-family{}}'); + expect(s('@supports (display: flex) { @font-face { :host ::ng-deep font-family{} } }', + 'contenta', 'h')) + .toEqual('@supports (display:flex) { @font-face { font-family{}}}'); + }); + it('should pass through @import directives', () => { const styleStr = '@import url("https://fonts.googleapis.com/css?family=Roboto");'; const css = s(styleStr, 'contenta');