Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

<!DOCTYPE html>
<meta charset="utf-8">
<title>Shadow Host Style Sharing Tests</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id="container"></div>
<script>
const container = document.getElementById('container');
function cleanup() {
container.innerHTML = '';
}
// Test 1: Different :host rules in different stylesheets
test(() => {
cleanup();
const redSheet = new CSSStyleSheet();
redSheet.replaceSync(':host { color: rgb(255, 0, 0); }');
const blueSheet = new CSSStyleSheet();
blueSheet.replaceSync(':host { color: rgb(0, 0, 255); }');
let useRed = true;
class DynamicHost extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = '<div>content</div>';
shadow.adoptedStyleSheets = [useRed ? redSheet : blueSheet];
}
}
customElements.define('dynamic-host-1', DynamicHost);
const parent = document.createElement('div');
// Create siblings: first 10 with red sheet
useRed = true;
const redHosts = [];
for (let i = 0; i < 10; i++) {
const host = document.createElement('dynamic-host-1');
redHosts.push(host);
parent.appendChild(host);
}
// Then 1 with blue sheet as a sibling
useRed = false;
const blueHost = document.createElement('dynamic-host-1');
parent.appendChild(blueHost);
// Add to document and force style computation
container.appendChild(parent);
container.offsetHeight;
assert_equals(getComputedStyle(blueHost).color, 'rgb(0, 0, 255)', 'Must be blue, not red');
for (const host of redHosts) {
assert_equals(getComputedStyle(host).color, 'rgb(255, 0, 0)', 'Must be red');
}
}, 'Different CascadeData must block sharing despite being same element type');
// Test 2: Same :host rules, different inherited styles
test(() => {
cleanup();
const sharedSheet = new CSSStyleSheet();
sharedSheet.replaceSync(':host { font-size: 20px; }');
class SharedHost extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
shadow.adoptedStyleSheets = [sharedSheet];
shadow.innerHTML = '<span>content</span>';
}
}
customElements.define('shared-host-2', SharedHost);
const parent1 = document.createElement('div');
parent1.style.color = 'rgb(255, 0, 0)';
const host1 = document.createElement('shared-host-2');
parent1.appendChild(host1);
container.appendChild(parent1);
const parent2 = document.createElement('div');
parent2.style.color = 'rgb(0, 255, 0)';
const host2 = document.createElement('shared-host-2');
parent2.appendChild(host2);
container.appendChild(parent2);
assert_equals(getComputedStyle(host1).color, 'rgb(255, 0, 0)');
assert_equals(getComputedStyle(host2).color, 'rgb(0, 255, 0)');
}, 'Same :host rules but different inherited styles');
// Test 3: :host with class selector
test(() => {
cleanup();
const classSheet = new CSSStyleSheet();
classSheet.replaceSync(`
:host(.active) { background-color: rgb(255, 0, 0); }
:host(.inactive) { background-color: rgb(0, 0, 255); }
`);
class ClassHost extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
shadow.adoptedStyleSheets = [classSheet];
shadow.innerHTML = '<span>content</span>';
}
}
customElements.define('class-host-3', ClassHost);
const active = document.createElement('class-host-3');
active.className = 'active';
const inactive = document.createElement('class-host-3');
inactive.className = 'inactive';
container.appendChild(active);
container.appendChild(inactive);
assert_equals(getComputedStyle(active).backgroundColor, 'rgb(255, 0, 0)');
assert_equals(getComputedStyle(inactive).backgroundColor, 'rgb(0, 0, 255)');
}, ':host with class selector should not share across different classes');
// Test 4: :host with attribute selector
test(() => {
cleanup();
const attrSheet = new CSSStyleSheet();
attrSheet.replaceSync(`
:host([data-theme="dark"]) { color: rgb(0, 0, 0); }
:host([data-theme="light"]) { color: rgb(255, 255, 255); }
`);
class AttrHost extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
shadow.adoptedStyleSheets = [attrSheet];
shadow.innerHTML = '<span>content</span>';
}
}
customElements.define('attr-host-4', AttrHost);
const dark = document.createElement('attr-host-4');
dark.setAttribute('data-theme', 'dark');
const light = document.createElement('attr-host-4');
light.setAttribute('data-theme', 'light');
container.appendChild(dark);
container.appendChild(light);
assert_equals(getComputedStyle(dark).color, 'rgb(0, 0, 0)');
assert_equals(getComputedStyle(light).color, 'rgb(255, 255, 255)');
}, ':host with attribute selector should not share across different attributes');
// Test 5: Same stylesheet, different inline styles
test(() => {
cleanup();
const sharedSheet = new CSSStyleSheet();
sharedSheet.replaceSync(':host { display: block; }');
class InlineHost extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
shadow.adoptedStyleSheets = [sharedSheet];
shadow.innerHTML = '<span>content</span>';
}
}
customElements.define('inline-host-5', InlineHost);
const host1 = document.createElement('inline-host-5');
host1.style.color = 'rgb(255, 0, 0)';
const host2 = document.createElement('inline-host-5');
host2.style.color = 'rgb(0, 255, 0)';
container.appendChild(host1);
container.appendChild(host2);
assert_equals(getComputedStyle(host1).color, 'rgb(255, 0, 0)');
assert_equals(getComputedStyle(host2).color, 'rgb(0, 255, 0)');
}, 'Same CascadeData but different inline styles should not share');
// Test 6: Same :host rules but different shadow stylesheets
test(() => {
cleanup();
const sheetA = new CSSStyleSheet();
sheetA.replaceSync(':host { color: rgb(255, 0, 0); }');
const sheetB = new CSSStyleSheet();
sheetB.replaceSync(':host { color: rgb(255, 0, 0); } span { font-weight: bold; }');
let whichSheet = 'a';
class SubtleHost extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
shadow.adoptedStyleSheets = [whichSheet === 'a' ? sheetA : sheetB];
shadow.innerHTML = '<span>content</span>';
}
}
customElements.define('subtle-host-6', SubtleHost);
whichSheet = 'a';
const hostA = document.createElement('subtle-host-6');
whichSheet = 'b';
const hostB = document.createElement('subtle-host-6');
container.appendChild(hostA);
container.appendChild(hostB);
assert_equals(getComputedStyle(hostA).color, 'rgb(255, 0, 0)');
assert_equals(getComputedStyle(hostB).color, 'rgb(255, 0, 0)');
}, 'Same :host rules but different CascadeData should prevent sharing');
</script>