diff --git a/src/scriptlets/inject-css-in-shadow-dom.js b/src/scriptlets/inject-css-in-shadow-dom.js index 72a9f558..bed72ccc 100644 --- a/src/scriptlets/inject-css-in-shadow-dom.js +++ b/src/scriptlets/inject-css-in-shadow-dom.js @@ -39,6 +39,12 @@ export function injectCssInShadowDom(source, cssRule, hostSelector = '') { return; } + // url() css function is not allowed + if (cssRule.match(/url\(.*\)/i)) { + logMessage(source, '"url()" function is not allowed for css rules'); + return; + } + const callback = (shadowRoot) => { const stylesheet = new CSSStyleSheet(); try { diff --git a/tests/scriptlets/inject-css-in-shadow-dom.test.js b/tests/scriptlets/inject-css-in-shadow-dom.test.js index 7b93f78f..6cd6d345 100644 --- a/tests/scriptlets/inject-css-in-shadow-dom.test.js +++ b/tests/scriptlets/inject-css-in-shadow-dom.test.js @@ -1,18 +1,21 @@ -/* eslint-disable no-underscore-dangle */ +/* eslint-disable no-underscore-dangle, no-console */ import { runScriptlet, clearGlobalProps } from '../helpers'; const { test, module } = QUnit; const name = 'inject-css-in-shadow-dom'; const nativeAttachShadow = window.Element.prototype.attachShadow; +const nativeConsole = console.log; const TARGET_ID1 = 'target1'; const CSS_TEXT1 = `#${TARGET_ID1} { color: rgb(255, 0, 0) !important }`; +const CSS_TEXT2 = `#${TARGET_ID1} { background: lightblue url("https://www.w3schools.com/cssref/img_tree.gif"); } !important }`; +const INVALID_CSS_TEXT = `#${TARGET_ID1} { color: rgb(255, 0, 0) } !important`; const HOST_ID1 = 'host1'; const HOST_ID2 = 'host2'; const appendTarget = (parent, id) => { - const target = document.createElement('h1'); + const target = document.createElement('div'); target.id = id; target.innerText = id; return parent.appendChild(target); @@ -44,6 +47,7 @@ const afterEach = () => { clearGlobalProps('hit', '__debug'); removeHosts(); window.Element.prototype.attachShadow = nativeAttachShadow; + console.log = nativeConsole; }; module(name, { beforeEach, afterEach }); @@ -99,4 +103,58 @@ if (!isSupported) { assert.strictEqual(window.hit, 'FIRED', 'hit function was executed'); }); + + test('do not apply style with url function, logged correctly', (assert) => { + assert.expect(3); + console.log = function log(input) { + if (input.indexOf('trace') > -1) { + return; + } + assert.strictEqual( + input, + `${name}: "url()" function is not allowed for css rules`, + 'message logged correctly', + ); + }; + + runScriptlet(name, [CSS_TEXT2]); + + const host1 = appendHost(HOST_ID1); + const shadowRoot1 = host1.attachShadow({ mode: 'closed' }); + appendTarget(shadowRoot1, TARGET_ID1); + + // style with url() function should not be applied + const target1 = shadowRoot1.getElementById(TARGET_ID1); + const target1Background = getComputedStyle(target1).background; + + assert.ok(!target1Background.match(/url\(.*\)/i), 'style was not applied to shadowRoot #1'); + assert.strictEqual(window.hit, undefined, 'hit should NOT fire'); + }); + + test('do not apply invalid style, logged correctly', (assert) => { + assert.expect(3); + console.log = function log(input) { + if (input.indexOf('trace') > -1) { + return; + } + assert.strictEqual( + input, + `${name}: Failed to parse the rule: ${INVALID_CSS_TEXT}`, + 'message logged correctly', + ); + }; + + runScriptlet(name, [INVALID_CSS_TEXT]); + + const host1 = appendHost(HOST_ID1); + const shadowRoot1 = host1.attachShadow({ mode: 'closed' }); + appendTarget(shadowRoot1, TARGET_ID1); + + // style with url() function should not be applied + const target1 = shadowRoot1.getElementById(TARGET_ID1); + const target1Color = getComputedStyle(target1).color; + + assert.strictEqual(target1Color, 'rgb(0, 0, 0)', 'style was not applied to shadowRoot #1'); + assert.strictEqual(window.hit, undefined, 'hit should NOT fire'); + }); }