From fcb8a1fcf52a63eb79db406072bd3ee85d73a5d4 Mon Sep 17 00:00:00 2001 From: Ankit Ahuja Date: Wed, 29 Jul 2020 12:33:35 -0700 Subject: [PATCH] do not inject important for declarations whose ancestor is an atrule. #559 --- src/background/styles.ts | 16 ++--- src/css/__tests__/declaration.test.ts | 88 ++++++++++++++++++++++++++- src/css/declaration.ts | 22 +++++++ src/css/index.ts | 2 +- src/css/inject-style.ts | 6 +- 5 files changed, 116 insertions(+), 18 deletions(-) diff --git a/src/background/styles.ts b/src/background/styles.ts index d0696f54..af827173 100644 --- a/src/background/styles.ts +++ b/src/background/styles.ts @@ -1,4 +1,6 @@ import * as postcss from 'postcss'; +import { appendImportantToDeclarations } from '@stylebot/css'; + import BackgroundPageUtils from './utils'; import { @@ -63,7 +65,7 @@ class BackgroundPageStyles { const { styles, defaultStyle } = this.getStylesForPage(tab.url); this.updateIcon(tab, styles, defaultStyle); - const css = this.addImportantToCss(this.styles[url].css); + const css = appendImportantToDeclarations(this.styles[url].css); const message: EnableStyleForTab = { name: 'EnableStyleForTab', url, @@ -161,7 +163,7 @@ class BackgroundPageStyles { if (matches && this.styles[url]) { const css = important - ? this.addImportantToCss(this.styles[url].css) + ? appendImportantToDeclarations(this.styles[url].css) : this.styles[url].css; const { enabled, readability } = this.styles[url]; @@ -182,16 +184,6 @@ class BackgroundPageStyles { return { styles, defaultStyle }; } - addImportantToCss(css: string): string { - const root = postcss.parse(css); - - root.walkDecls(decl => { - decl.important = true; - }); - - return root.toString(); - } - getImportCss(url: string): Promise { return new Promise(resolve => { const xhr = new XMLHttpRequest(); diff --git a/src/css/__tests__/declaration.test.ts b/src/css/__tests__/declaration.test.ts index eb241ec8..454e0ff6 100644 --- a/src/css/__tests__/declaration.test.ts +++ b/src/css/__tests__/declaration.test.ts @@ -2,7 +2,7 @@ const dedent = require('dedent'); import 'jest-fetch-mock'; -import { addDeclaration } from '../declaration'; +import { addDeclaration, appendImportantToDeclarations } from '../declaration'; describe('declaration', () => { describe('addDeclaration', () => { @@ -141,4 +141,90 @@ describe('declaration', () => { expect(output).toBe(''); }); }); + + describe('appendImportantToDeclarations', () => { + it('correctly appends !important to declarations', () => { + const css = dedent` + * { + font-family: Helvetica; + } + + a { + color: red; + } + `; + + const output = appendImportantToDeclarations(css); + expect(output).toEqual(dedent` + * { + font-family: Helvetica !important; + } + + a { + color: red !important; + } + `); + }); + + it('does not append !important to declarations whose ancestor is an atrule', () => { + const css = dedent` + @font-face { + font-family: 'MS PGothic'; + src: local('Meiryo'); + } + + @page { + margin: 1cm; + } + + @keyframes slidein { + from { + transform: translateX(0%); + } + + to { + transform: translateX(100%); + } + } + + * { + font-family: Helvetica; + } + + a { + color: red; + } + `; + + const output = appendImportantToDeclarations(css); + expect(output).toEqual(dedent` + @font-face { + font-family: 'MS PGothic'; + src: local('Meiryo'); + } + + @page { + margin: 1cm; + } + + @keyframes slidein { + from { + transform: translateX(0%); + } + + to { + transform: translateX(100%); + } + } + + * { + font-family: Helvetica !important; + } + + a { + color: red !important; + } + `); + }); + }); }); diff --git a/src/css/declaration.ts b/src/css/declaration.ts index f74d1f17..0d1fa348 100644 --- a/src/css/declaration.ts +++ b/src/css/declaration.ts @@ -58,3 +58,25 @@ export const addDeclaration = ( return css; }; + +export const appendImportantToDeclarations = (css: string): string => { + const root = postcss.parse(css); + + const isAncestorAnAtRule = (node: postcss.Node): boolean => { + if (node.type === 'atrule') { + return true; + } else if (node.type === 'decl' || node.type === 'rule') { + return isAncestorAnAtRule(node.parent); + } else { + return false; + } + }; + + root.walkDecls(decl => { + if (!isAncestorAnAtRule(decl)) { + decl.important = true; + } + }); + + return root.toString(); +}; diff --git a/src/css/index.ts b/src/css/index.ts index 5328e650..3d17c27c 100644 --- a/src/css/index.ts +++ b/src/css/index.ts @@ -19,4 +19,4 @@ export { export { addGoogleWebFont, cleanGoogleWebFonts } from './webfont'; -export { addDeclaration } from './declaration'; +export { addDeclaration, appendImportantToDeclarations } from './declaration'; diff --git a/src/css/inject-style.ts b/src/css/inject-style.ts index 5ebf47e1..4131fdcd 100644 --- a/src/css/inject-style.ts +++ b/src/css/inject-style.ts @@ -1,4 +1,5 @@ import * as postcss from 'postcss'; +import { appendImportantToDeclarations } from './declaration'; import { getCssWithExpandedImports } from './import'; const getStylesheetId = (id: string) => { @@ -32,10 +33,7 @@ export const injectRootIntoDocument = ( root: postcss.Root, id: string ): void => { - const rootWithImportant = root.clone(); - rootWithImportant.walkDecls(decl => (decl.important = true)); - - const css = rootWithImportant.toString(); + const css = appendImportantToDeclarations(root.toString()); injectCSSIntoDocument(css, id); };