1<!DOCTYPE html>
2<!--
3@license
4Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
5This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
6The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
7The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
8Code distributed by Google as part of the polymer project is also
9subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
10-->
11<html>
12<head>
13  <meta charset="utf-8">
14  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
15  <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
16   <script>
17    WCT = {waitFor: function (cb) {HTMLImports.whenReady(cb)}}
18  </script>
19  <script src="./test-flags.js"></script>
20  <script src="../node_modules/wct-browser-legacy/browser.js"></script>
21  <script src="../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
22  <script src="../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
23  <script src="../node_modules/@webcomponents/template/template.js"></script>
24  <script src="../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
25  <script src="../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
26  <script src="../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
27  <script src="../scoping-shim.min.js"></script>
28  <script src="../apply-shim.min.js"></script>
29  <script src="../custom-style-interface.min.js"></script>
30  <script src="module/generated/make-element.js"></script>
31  <script src="module/generated/custom-style-element.js"></script>
32</head>
33<body>
34
35  <custom-style>
36    <style>
37      x-container, x-sample, x-sample-dynamic, x-container-dynamic {
38        display: block;
39        padding: 10px;
40        margin: 10px;
41        border: 1px solid black;
42      }
43
44      .target {
45        background-color: rgb(0, 255, 0);
46      }
47    </style>
48  </custom-style>
49
50  <template id="x-sample">
51    <style>
52      .target {
53        background-color: rgb(255, 0, 0);
54      }
55    </style>
56    <h2></h2>
57    <p>here .target elements are red</p>
58    <div class="target">I'm red</div>
59    <template id="renderer">
60      <div class="target"></div>
61    </template>
62  </template>
63
64  <template id="x-container">
65    <style>
66      .target {
67        background-color: rgb(123, 123, 123);
68      }
69    </style>
70    <h1>x-container</h1>
71    <p>here .target elements are gray</p>
72    <div class="target">I'm gray</div>
73    <slot></slot>
74  </template>
75
76  <h2>body</h2>
77  <p>here .target elements are green</p>
78
79  <div class="target">I'm green</div>
80
81  <x-sample id="inBody"></x-sample>
82
83  <x-sample id="inContainer"></x-sample>
84
85  <x-container></x-container>
86
87  <template id="x-dynamic">
88    <style>
89      span {
90        background-color: rgb(123, 123, 123);
91      }
92    </style>
93    <div id="container">
94    </div>
95  </template>
96
97  <x-dynamic></x-dynamic>
98
99  <template id="out-of-band">
100    <style>
101      div {
102        color: var(--foo);
103      }
104    </style>
105    <div>oob shadowed</div>
106  </template>
107
108  <template id="oob-parent">
109    <style>
110      out-of-band {
111        --foo: rgb(0, 0, 255);
112      }
113    </style>
114    <out-of-band></out-of-band>
115  </template>
116
117  <template id="oob-other-parent">
118    <style>
119      out-of-band {
120        --foo: rgb(255, 0, 0);
121      }
122    </style>
123  </template>
124
125  <oob-parent></oob-parent>
126  <oob-other-parent></oob-other-parent>
127
128  <template id="x-sample-dynamic">
129    <style>
130      .target {
131        background-color: rgb(255, 0, 0);
132      }
133    </style>
134    <h2></h2>
135    <p>here .target elements are red</p>
136    <div class="target">I'm red</div>
137    <template id="renderer">
138      <div class="target"></div>
139    </template>
140  </template>
141
142  <template id="x-container-dynamic">
143    <style>
144      .target {
145        background-color: rgb(123, 123, 123);
146      }
147    </style>
148    <h1>x-container</h1>
149    <p>here .target elements are gray</p>
150    <div class="target">I'm gray</div>
151    <slot></slot>
152  </template>
153
154  <x-container-dynamic></x-container-dynamic>
155
156  <template id="css-build" css-build="shady">
157    <style>:host{@apply --fake;}</style>
158    <div class="style-scope css-build"></div>
159  </template>
160
161  <template id="css-build-comment"><!--css-build:shady-->
162    <style>:host{@apply --fake;}</style>
163    <div class="style-scope css-build-comment"></div>
164  </template>
165
166  <script>
167    suite('Dynamic Scoping', () => {
168      function stamp(parent, host) {
169        let template = host.shadowRoot.querySelector('template#renderer')
170        let el = template.content.cloneNode(true).querySelector('div.target');
171        el.textContent = `stamped by ${host.id}`;
172        parent.appendChild(el);
173        return el;
174      }
175      test('DOM is scoped correctly when stamped from an element into document', (done) => {
176        let inBody = document.querySelector('x-sample#inBody');
177        let inContainer = document.querySelector('x-sample#inContainer');
178        makeElement('x-sample', function() {
179          this.shadowRoot.querySelector('h2').textContent = `${this.id}`;
180        });
181        makeElement('x-container');
182        setTimeout(() => {
183          let body = stamp(document.body, inBody);
184          let container = stamp(document.querySelector('x-container').shadowRoot, inContainer);
185          requestAnimationFrame(() => {
186            assert.equal(getComputedStyle(body).backgroundColor, 'rgb(0, 255, 0)');
187            assert.equal(getComputedStyle(container).backgroundColor, 'rgb(123, 123, 123)')
188            done();
189          });
190        }, 300);
191      });
192      test('DOM is scoped correctly when created dynamically inside a scoped container', (done) => {
193        makeElement('x-dynamic', function() {
194          let div = this.shadowRoot.querySelector('#container');
195          let newDiv = div.cloneNode(true);
196          let span = document.createElement('span');
197          span.textContent = 'created dynamically';
198          newDiv.appendChild(span);
199          this.shadowRoot.appendChild(newDiv);
200          requestAnimationFrame(() => {
201            assert.equal(getComputedStyle(span).backgroundColor, 'rgb(123, 123, 123)');
202            done();
203          })
204        });
205      });
206      test('moving a custom element between scopes recalculates correctly', function(done) {
207        makeElement('out-of-band');
208        makeElement('oob-parent');
209        makeElement('oob-other-parent');
210        let parent = document.querySelector('oob-parent');
211        let newParent = document.querySelector('oob-other-parent');
212        let oob = parent.shadowRoot.querySelector('out-of-band');
213        let shadowDiv = oob.shadowRoot.querySelector('div');
214        newParent.shadowRoot.appendChild(oob);
215        requestAnimationFrame(() => {
216          assert.equal(getComputedStyle(shadowDiv).getPropertyValue('color').trim(), 'rgb(255, 0, 0)');
217          done();
218        });
219      })
220      function makeDynamicElement(name, connectedCallback) {
221        let template = document.querySelector(`template#${name}`);
222        if (template && window.ShadyCSS) {
223          window.ShadyCSS.prepareTemplate(template, name);
224        }
225        window.customElements.define(name, class extends window.HTMLElement {
226          constructor() {
227            super();
228            if (template && !this.shadowRoot) {
229              this.attachShadow({mode: 'open'});
230              this.shadowRoot.appendChild(document.importNode(template.content, true));
231            }
232          }
233          connectedCallback() {
234            window.ShadyCSS && window.ShadyCSS.styleElement(this);
235            if (connectedCallback) {
236              connectedCallback.call(this);
237            }
238          }
239        });
240      }
241      test('Nested DOM is scoped correctly when created dynamically inside a dynamic container', function(done) {
242        if (!window.customElements.polyfillWrapFlushCallback && window.ShadyDOM && window.ShadyDOM.inUse) {
243          /*
244           * This test is flaky if running with native custom elements and polyfill shadowdom,
245           * as the shadowdom polyfill may render inside of the constructor and create children,
246           * which is not allowed in the CE spec.
247           */
248          this.skip();
249        }
250        makeDynamicElement('x-container-dynamic');
251        makeDynamicElement('x-sample-dynamic');
252        const dynamicDiv = document.createElement('div');
253        dynamicDiv.classList.add('target');
254        dynamicDiv.innerText = 'I was created dynamically';
255        const dynamicSample = document.createElement('x-sample-dynamic');
256        const dynamicContainer = document.createElement('x-container-dynamic');
257        dynamicSample.shadowRoot.appendChild(dynamicDiv);
258        dynamicContainer.shadowRoot.appendChild(dynamicSample);
259        document.querySelector('x-container-dynamic').shadowRoot.appendChild(dynamicContainer);
260        requestAnimationFrame(() => {
261          dynamicSample.shadowRoot.querySelectorAll('div.target').forEach((target) =>
262            assert.equal(getComputedStyle(target).backgroundColor,'rgb(255, 0, 0)'));
263          done();
264        });
265      });
266
267      test('templates marked with "css-build" will be left alone', function() {
268        makeElement('css-build');
269        const template = document.querySelector('template#css-build');
270        const div = template.content.querySelector('div');
271        const divClasses = Array.from(div.classList);
272        assert.includeMembers(divClasses, ['style-scope', 'css-build']);
273        const style = template.content.querySelector('style');
274        if (style) {
275          assert.match(style.textContent.trim(), /:host\s*{\s*@apply --fake;\s*}/);
276        }
277      });
278
279      test('templates with css-build comments will be left alone', function() {
280        const template = document.querySelector('template#css-build-comment');
281        const buildComment = template.content.firstChild;
282        assert.instanceOf(buildComment, Comment, 'first child of template content should be a Comment');
283        makeElement('css-build-comment');
284        const div = template.content.querySelector('div');
285        const divClasses = Array.from(div.classList);
286        assert.includeMembers(divClasses, ['style-scope', 'css-build-comment']);
287        const style = template.content.querySelector('style');
288        if (style) {
289          assert.match(style.textContent.trim(), /:host\s*{\s*@apply --fake;\s*}/);
290        }
291        assert.equal(buildComment.parentNode, null, 'build commment should have been removed');
292      });
293    });
294  </script>
295</body>
296</html>
297