1<!doctype html>
2<!--
3@license
4Copyright (c) 2014 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  <script>
15  WCT = {waitFor: function (cb) {HTMLImports.whenReady(cb)}}
16  </script>
17  <script src="./test-flags.js"></script>
18  <script src="../node_modules/wct-browser-legacy/browser.js"></script>
19  <script src="../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
20  <script src="../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
21  <script src="../node_modules/@webcomponents/template/template.js"></script>
22  <script src="../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
23  <script src="../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
24  <script src="../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
25  <script src="../scoping-shim.min.js"></script>
26  <script src="../apply-shim.min.js"></script>
27  <script src="../custom-style-interface.min.js"></script>
28  <script src="module/generated/make-element.js"></script>
29
30  <custom-style>
31    <style>
32      div#priority {
33        border: 1px solid black;
34      }
35    </style>
36  </custom-style>
37</head>
38<body>
39
40<template id="x-gchild">
41  <style>
42  </style>
43  <div id="target">x-gchild</div>
44</template>
45
46<template id="x-child">
47  <div id="simple">simple</div>
48  <div id="complex1" class="scoped">complex1</div>
49  <div id="complex2" selected>complex2</div>
50  <div id="media">media</div>
51  <div id="shadow" class="shadowTarget">shadowTarget</div>
52  <div id="deep" class="deepTarget">deepTarget</div>
53  <x-gchild id="gchild1"></x-gchild>
54  <x-gchild id="gchild2" class="wide"></x-gchild>
55</template>
56
57<template id="x-child2">
58  <style>
59    :host(.wide) #target{
60      border: none;
61    }
62  </style>
63  <div id="target">x-child2</div>
64</template>
65
66<template id="x-scope-class">
67  <div id="scope">Trivial</div>
68</template>
69
70<template id="x-scoped">
71  <style>
72    :host {
73      display: block;
74      border: 1px solid orange;
75      --keyframes100: 100px;
76    }
77
78    :host(.wide) {
79      border-width: 2px;
80    }
81
82    :host(.wide)::after {
83      content: '-content-';
84    };
85
86    #keyframes2.special {
87      --keyframes100: 200px;
88    }
89
90    #simple {
91      border: 3px solid orange;
92    }
93
94    .scoped, [selected] {
95      border: 4px solid pink;
96    }
97
98    @media(max-width: 10000px) {
99      .media {
100        border: 5px solid brown;
101      }
102    }
103
104    .container ::slotted(*) {
105      border: 6px solid navy;
106    }
107
108    #priority {
109      border: 9px solid orange;
110    }
111
112    .container1 > ::slotted([slot=content1]) {
113      border: 13px solid navy;
114    }
115
116    .container2 > ::slotted([slot=content2]) {
117      border: 14px solid navy;
118    }
119
120    .computed {
121      border: 15px solid orange;
122    }
123
124    .computeda {
125      border: 20px solid orange;
126    }
127
128    #child {
129      border: 16px solid tomato;
130      display: block;
131    }
132
133    svg {
134      margin-top: 20px;
135    }
136
137    #circle {
138      fill: seagreen;
139      stroke-width: 1px;
140      stroke: tomato;
141    }
142  </style>
143  <slot name="blank"></slot>
144  <div id="simple">simple</div>
145  <div id="complex1" class="scoped">complex1</div>
146  <div id="complex2" selected>complex2</div>
147  <div id="media" class="media">media</div>
148  <div class="container1">
149    <slot name="content1"></slot>
150  </div>
151  <div class="container2">
152    <slot name="content2"></slot>
153  </div>
154  <div class="container">
155    <slot></slot>
156  </div>
157  <x-child id="child"></x-child>
158  <div id="priority">priority</div>
159  <x-child2 class="wide" id="child2"></x-child2>
160  <div id="computed">Computed</div>
161  <svg height="25" width="25">
162    <circle id="circle" cx="12" cy="12" r="10"></circle>
163  </svg>
164  <x-scope-class id="scopeClass"></x-scope-class>
165  <x-keyframes id="keyframes"></x-keyframes>
166  <x-keyframes id="keyframes2"></x-keyframes>
167</template>
168
169<template id="x-slotted">
170  <style>
171  ::slotted(.auto-content) {
172    border: 2px solid orange;
173  }
174  .bar, ::slotted(.complex-child) {
175    border: 6px solid navy;
176  }
177  #container ::slotted(*) {
178    border: 8px solid green;
179  }
180  </style>
181  <slot></slot>
182  <div id="container">
183    <slot name="container"></slot>
184  </div>
185</template>
186
187<template id="dynamic">
188  <div class="added">
189    Added
190    <div class="sub-added">
191      Sub-added
192    </div>
193    </div>
194  </div>
195</template>
196
197<template id="x-dynamic-scope">
198  <style>
199    .added {
200      border: 17px solid beige;
201    }
202    .sub-added {
203      border: 18px solid #fafafa;
204    }
205  </style>
206  <div id="container"></div>
207</template>
208
209<template id="x-keyframes">
210  <style>
211    :host {
212      display: block;
213      position: relative;
214      border: 10px solid blue;
215      left: 0px;
216      /* Prefix required by Safari <= 8 */
217      -webkit-animation-duration: 0.3s;
218      animation-duration: 0.3s;
219      -webkit-animation-fill-mode: forwards;
220      animation-fill-mode: forwards;
221    }
222
223    :host([animated]) {
224      /* Prefix required by Safari <= 8 */
225      -webkit-animation-name: x-keyframes-animation;
226      animation-name: x-keyframes-animation;
227    }
228
229    /* Prefix required by Safari <= 8 */
230    @-webkit-keyframes x-keyframes-animation {
231      0% {
232        left: var(--keyframes0, 0px);
233      }
234
235      100% {
236        left: var(--keyframes100, 10px);
237      }
238    }
239    @keyframes x-keyframes-animation {
240      0% {
241        left: var(--keyframes0, 0px);
242      }
243
244      100% {
245        left: var(--keyframes100, 10px);
246      }
247    }
248  </style>
249  x-keyframes
250</template>
251
252<template id="x-attr-selector">
253  <style>
254    #foo1 ~ #bar1 {
255      border: 2px solid red;
256    }
257
258    #foo1 ~ #bar1 ~ #foo2[attr~=foo2] ~ #bar2[attr~=bar2]  {
259      border: 4px solid red;
260    }
261
262    #foo1 ~ #bar1 ~ #foo2[attr~=foo2] ~ #bar2[attr~=bar2] ~ #foo3[attr~=foo3][a~=a] ~ #bar3[attr~=bar3][a~=a] {
263      border: 6px solid red;
264    }
265  </style>
266  <div id="foo1"></div>
267  <div id="bar1">bar1</div>
268  <div id="foo2" attr="foo2"></div>
269  <div id="bar2" attr="bar2">bar2</div>
270  <div id="foo3" attr="foo3" a="a"></div>
271  <div id="bar3" attr="bar3" a="a">bar3</div>
272</template>
273
274<template id="x-adjacent-sibling">
275  <style>
276    div {
277      border: 20px solid black;
278    }
279    #foo2 + #foo1 {
280      border: 2px solid black;
281    }
282    #foo1 + #foo2 {
283      border: 4px solid black;
284    }
285    #foo2 + #foo3 {
286      border: 6px solid black;
287    }
288  </style>
289  <div id="foo1"></div>
290  <div id="foo2"></div>
291  <div id="foo3"></div>
292</template>
293
294<template id="svg">
295  <svg class="svg" viewBox="0 0 24 24">
296    <circle id="circle" r="12" cx="12" cy="12" />
297  </svg>
298</template>
299
300<template id="x-dynamic-svg">
301  <style>
302    .svg {
303      height: 24px;
304      width: 24px;
305    }
306    #circle {
307      fill: red;
308      fill-opacity: 0.5;
309    }
310  </style>
311  <div id="container"></div>
312</template>
313
314<template id="x-specificity">
315  <style>
316    :host {
317      border-top: 1px solid red;
318    }
319    :host(.bar) {
320      border-top: 2px solid red;
321    }
322  </style>
323  <slot></slot>
324</template>
325
326<template id="self-test">
327  <style>
328    :host {
329      --border: 10px solid rgb(123, 123, 123);
330    }
331
332    a {
333      border: var(--border);
334    }
335  </style>
336  <a>I should be red.</a>
337</template>
338
339<template id="nth-plus-one">
340  <style>
341    .foo.bar {
342      color: rgb(255, 0, 0);
343    }
344    div:nth-child(n+1) {
345      color: rgb(0, 255, 0);
346    }
347  </style>
348  <div>1</div>
349  <div class="foo bar">2</div>
350</template>
351
352<template id="shady-unscoped">
353  <style shady-unscoped>
354    .unscoped {
355      color: rgb(255, 0, 0);
356    }
357  </style>
358  <div class="unscoped"></div>
359</template>
360
361<template id="shady-unscoped-2">
362  <style shady-unscoped>
363    .unscoped {
364      color: rgb(255, 0, 0);
365    }
366  </style>
367  <span class="unscoped"></span>
368</template>
369
370<template id="unscoped-apply-user">
371  <style>
372    div {
373      @apply --unscoped-foo;
374    }
375  </style>
376  <div></div>
377</template>
378
379<template id="unscoped-apply">
380  <style shady-unscoped>
381    html, :host > * {
382      --unscoped-foo: {border: 10px solid black};
383    }
384  </style>
385  <unscoped-apply-user></unscoped-apply-user>
386</template>
387
388<template id="any-selector">
389  <style>
390    :-webkit-any(div, span) {
391      color: rgb(123, 123, 123);
392    }
393    :-moz-any(div, span) {
394      color: rgb(123, 123, 123);
395    }
396  </style>
397  <div>a</div>
398  <span>b</span>
399</template>
400
401<template id="scoped-keyframes">
402  <style>
403    :host {
404      --time: 0.1s;
405    }
406
407    div {
408      /* prefix for older chrome and safari */
409      -webkit-animation-duration: var(--time);
410      animation-duration: var(--time);
411      -webkit-animation-fill-mode: forwards;
412      animation-fill-mode: forwards;
413      border: 0px solid black;
414    }
415
416    :host([animate]) div {
417      /* prefix for older chrome and safari */
418      -webkit-animation-name: border-grow;
419      animation-name: border-grow;
420    }
421
422    /* prefix for older chrome and safari */
423    @-webkit-keyframes border {}
424    @-webkit-keyframes border-grow {
425      to {
426        border-top-width: 10px;
427      }
428    }
429    @keyframes border {}
430    @keyframes border-grow {
431      to {
432        border-top-width: 10px;
433      }
434    }
435  </style>
436
437  <div id="target">Hello world</div>
438</template>
439
440<template id="nested-templates">
441  <style>
442    * {
443      opacity: 0.5;
444    }
445  </style>
446  <div id="a"></div>
447  <template id="t1">
448    <div id="b">
449      <div id="c">
450        <template id="t2">
451          <div id="d"></div>
452        </template>
453      </div>
454    </div>
455  </template>
456  <svg>
457    <template id="t3">
458      <g id="g">
459        <circle id="circle"></circle>
460      </g>
461    </template>
462  </svg>
463</template>
464
465<template id="bad-mixin">
466  <style>
467    :host(.nomatch) {
468      --div-border: {
469        border: 2px solid black;
470      }
471    }
472    div {
473      @apply --div-border;
474    }
475  </style>
476  <div></div>
477</template>
478
479<template id="x-parent-skip">
480  <style>
481    :host {
482      --foo: 10px solid black;
483    }
484  </style>
485  <x-skip></x-skip>
486</template>
487
488<template id="x-skip">
489  <x-child-skip></x-child-skip>
490</template>
491
492<template id="x-child-skip">
493  <style>
494    div {
495      border: var(--foo);
496    }
497  </style>
498  <div></div>
499</template>
500
501<script>
502(function() {
503  function assertComputed(element, value, property, pseudo) {
504    var computed = getComputedStyle(element, pseudo);
505    property = property || 'border-top-width';
506    if (Array.isArray(value)) {
507      assert.oneOf(computed[property], value, 'computed style incorrect for ' + property);
508    } else {
509      assert.equal(computed[property], value, 'computed style incorrect for ' + property);
510    }
511  }
512
513  function findNode(desc) {
514    var parts = desc.split('.');
515    var root = document;
516    var node;
517    for (var i=0, p; i < parts.length; i++) {
518      p = parts[i];
519      if (p == '$') {
520        root = node.shadowRoot;
521      } else {
522        node = root.querySelector('#' + p);
523      }
524    }
525    return node;
526  }
527
528  function flush() {
529    if (window.ShadyDOM) {
530      window.ShadyDOM.flush();
531    }
532    window.ShadyCSS.ScopingShim.flush();
533  }
534
535  suite('scoped-styling', function() {
536
537    suiteSetup(function() {
538      makeElement('x-gchild');
539      makeElement('x-child', function() {
540        this.classList.add('nug');
541      });
542      makeElement('x-child2');
543      makeElement('x-scope-class');
544      makeElement('x-scoped');
545      makeElement('x-slotted');
546      (function() {
547        var dynamic = document.querySelector('template#dynamic');
548
549        makeElement('x-dynamic-scope',
550          function() {
551          // simulate 3rd party action by using normal dom to add to element.
552          var dom = document.importNode(dynamic.content, true);
553          this.shadowRoot.querySelector('#container').appendChild(dom);
554        });
555      })();
556      makeElement('x-keyframes');
557      makeElement('x-attr-selector');
558      (function() {
559        var template = document.querySelector('template#svg');
560
561        makeElement('x-dynamic-svg', function() {
562          var dom = document.importNode(template.content, true);
563          this.shadowRoot.querySelector('#container').appendChild(dom);
564        });
565      })();
566      makeElement('x-specificity');
567      makeElement('nested-templates');
568    });
569
570    var el;
571    setup(function() {
572      el = document.createElement('x-scoped');
573      el.id = 'el';
574      document.body.appendChild(el);
575      flush();
576    });
577
578    teardown(function() {
579      document.body.removeChild(el);
580    });
581
582    test(':host', function() {
583      assertComputed(el, '1px');
584      assertComputed(el, ['', 'none'], 'content', '::after');
585    });
586
587    test(':host(...)', function() {
588      var el2 = document.createElement('x-scoped');
589      el2.classList.add('wide');
590      document.body.appendChild(el2);
591      flush();
592      assertComputed(el2, '2px');
593      assertComputed(el2, ['"-content-"', '-content-'], 'content', '::after');
594      document.body.removeChild(el2);
595    });
596
597    test('scoped selectors, simple and complex', function() {
598      assertComputed(findNode('el.$.simple'), '3px');
599      assertComputed(findNode('el.$.complex1'), '4px');
600      assertComputed(findNode('el.$.complex2'), '4px');
601    });
602
603    test('media query scoped selectors', function() {
604      assertComputed(findNode('el.$.media'), '5px');
605    });
606
607    test('upper bound encapsulation', function() {
608      var d = document.createElement('div');
609      d.classList.add('scoped');
610      document.body.appendChild(d);
611      assertComputed(d, '0px');
612      document.body.removeChild(d);
613    });
614
615    test('lower bound encapsulation', function() {
616      assertComputed(findNode('el.$.child.$.simple'), '0px');
617      assertComputed(findNode('el.$.child.$.complex1'), '0px');
618      assertComputed(findNode('el.$.child.$.complex2'), '0px');
619      assertComputed(findNode('el.$.child.$.media'), '0px');
620    });
621
622    test('nested templates', function() {
623      var el = document.createElement('nested-templates');
624      document.body.appendChild(el);
625      // Append nested template content. Note the <template> in <svg> is not
626      // an HTML template with .content at this point; it is just an unknown
627      // SVGElement so we don't have to stamp it
628      var t1 = el.shadowRoot.querySelector('#t1');
629      el.shadowRoot.appendChild(t1.content.cloneNode(true));
630      var t2 = el.shadowRoot.querySelector('#t2');
631      el.shadowRoot.appendChild(t2.content.cloneNode(true));
632      // Everything should now have 'opacity: 0.5'
633      var els = Array.from(el.shadowRoot.querySelectorAll('[id]'));
634      assert.deepEqual(els.map(e => e.getAttribute('id')), ['a', 't1', 't3', 'g', 'circle', 'b', 'c', 't2', 'd']);
635      els.forEach(e => {
636        assert.equal(getComputedStyle(e).opacity, '0.5', `Element with id "${e.id}" does not have the correct opacity`);
637      });
638      document.body.removeChild(el);
639    });
640
641  });
642
643  suite('slotted', function() {
644
645    test('::slotted selectors', function() {
646      var el = document.createElement('x-scoped');
647      document.body.appendChild(el);
648      var content1 = document.createElement('div');
649      content1.slot = 'content1';
650      var content2 = document.createElement('div');
651      content2.slot = 'content2';
652      var content = document.createElement('div');
653      content.className = 'content';
654      el.appendChild(content1);
655      el.appendChild(content2);
656      el.appendChild(content);
657      flush();
658
659      assertComputed(content, '6px');
660      assertComputed(content1, '13px');
661      assertComputed(content2, '14px');
662      document.body.removeChild(el);
663    });
664
665    test('auto ::slotted selector', function() {
666      var x = document.createElement('x-slotted');
667      var d1 = document.createElement('div');
668      d1.classList.add('auto-content');
669      d1.textContent = 'auto-content';
670      document.body.appendChild(x);
671      x.appendChild(d1);
672      flush();
673      assertComputed(d1, '2px');
674      document.body.removeChild(x);
675    });
676
677    test('::slotted + child in complex selector', function() {
678      var x = document.createElement('x-slotted');
679      var d1 = document.createElement('div');
680      d1.classList.add('complex-child');
681      d1.textContent = 'complex-child';
682      document.body.appendChild(x);
683      x.appendChild(d1);
684      flush();
685      assertComputed(d1, '6px');
686      document.body.removeChild(x);
687    });
688
689    test('::slotted + named slot', function() {
690      var x = document.createElement('x-slotted');
691      var d1 = document.createElement('div');
692      d1.setAttribute('slot', 'container')
693      d1.textContent = 'named slot child';
694      document.body.appendChild(x);
695      x.appendChild(d1);
696      flush();
697      assertComputed(d1, '8px');
698      document.body.removeChild(x);
699    });
700
701  });
702
703  suite('dynamic changes', function() {
704
705    test('elements dynamically added/removed from root', function() {
706      var el = document.createElement('x-scoped');
707      document.body.appendChild(el);
708      flush();
709      var d = document.createElement('div');
710      d.classList.add('scoped');
711      d.textContent = 'Dynamically... Scoped!';
712      el.shadowRoot.appendChild(d);
713      flush();
714      assertComputed(d, '4px');
715      document.body.appendChild(d);
716      flush();
717      assert.notInclude(d.getAttribute('style-scoped') || '', el.is, 'scoping attribute not removed when added to other root');
718      assert.notInclude(d.className, el.is, 'scoping class not removed when added to other root');
719      el.shadowRoot.appendChild(d);
720      flush();
721      assertComputed(d, '4px');
722      el.shadowRoot.removeChild(d);
723      flush();
724      assert.notInclude(d.getAttribute('style-scoped') || '', el.is, 'scoping attribute not removed when removed from root');
725      assert.notInclude(d.className, el.is, 'scoping class not removed when removed from root');
726      el.shadowRoot.appendChild(d);
727      flush();
728      assertComputed(d, '4px');
729      document.body.removeChild(el);
730    });
731
732    test('elements dynamically added/removed from host', function() {
733      var el = document.createElement('x-scoped');
734      document.body.appendChild(el);
735      var d = document.createElement('div');
736      d.classList.add('scoped');
737      d.slot = 'blank';
738      d.textContent = 'Dynamically... unScoped!';
739      el.appendChild(d);
740      flush();
741      assertComputed(d, '0px');
742      el.removeChild(d);
743      flush();
744      assert.notInclude(d.getAttribute('style-scoped') || '', el.is, 'scoping attribute not removed when added to other root');
745      assert.notInclude(d.className, el.is, 'scoping class not removed when added to other root');
746      el.appendChild(d);
747      flush();
748      assertComputed(d, '0px');
749      el.removeChild(d);
750      flush();
751      assert.notInclude(d.getAttribute('style-scoped') || '', el.is, 'scoping attribute not removed when removed from root');
752      assert.notInclude(d.className, el.is, 'scoping class not removed when removed from root');
753      el.appendChild(d);
754      flush();
755      assertComputed(d, '0px');
756      document.body.removeChild(el);
757    });
758
759    test('element subtree added via dom api', function() {
760      var el = document.createElement('x-dynamic-scope');
761      document.body.appendChild(el);
762      flush();
763      var container = el.shadowRoot.querySelector('#container');
764      var a = container.querySelector('.added');
765      assertComputed(a, '17px');
766      var b = container.querySelector('.sub-added');
767      assertComputed(b, '18px');
768      document.body.removeChild(el);
769    });
770
771    test('changes to class attribute', function() {
772      var el = document.createElement('x-scoped');
773      el.id = 'el'
774      document.body.appendChild(el);
775      flush();
776      var d = findNode('el.$.computed');
777      assertComputed(d, '0px');
778      d.setAttribute('class', 'computed');
779      assertComputed(d, '15px');
780      d.setAttribute('class', '', 'empty class attr does not remove class');
781      assertComputed(d, '0px');
782      d.setAttribute('class', 'computed ', 'class attr with space does not apply');
783      assertComputed(d, '15px');
784      document.body.removeChild(el);
785    });
786
787  });
788
789  suite('misc', function() {
790
791    var el;
792    setup(function() {
793      el = document.createElement('x-scoped');
794      el.id = 'el';
795      document.body.appendChild(el);
796      flush();
797    });
798
799    teardown(function() {
800      document.body.removeChild(el);
801    });
802
803    test('keyframes change scope', function(done) {
804      var xKeyframes = findNode('el.$.keyframes');
805      // Edge 16 does not support CSS Custom Properties in keyframes
806      if (window.ShadyCSS.nativeCss && navigator.userAgent.match(/Edge/)) {
807        this.skip();
808      }
809      var onAnimationEnd = function() {
810        xKeyframes.removeEventListener('animationend', onAnimationEnd);
811        xKeyframes.removeEventListener('webkitAnimationEnd', onAnimationEnd);
812        assertComputed(xKeyframes, '100px', 'left');
813
814        xKeyframes = findNode('el.$.keyframes2');
815
816        onAnimationEnd = function() {
817          xKeyframes.removeEventListener('animationend', onAnimationEnd);
818          xKeyframes.removeEventListener('webkitAnimationEnd', onAnimationEnd);
819          assertComputed(xKeyframes, '200px', 'left');
820          done();
821        };
822
823        xKeyframes.addEventListener('animationend', onAnimationEnd);
824        xKeyframes.addEventListener('webkitAnimationEnd', onAnimationEnd);
825
826        xKeyframes.classList.add('special');
827        xKeyframes.setAttribute('animated', '');
828        window.ShadyCSS.ScopingShim.styleElement(xKeyframes);
829      };
830      xKeyframes.addEventListener('animationend', onAnimationEnd);
831      xKeyframes.addEventListener('webkitAnimationEnd', onAnimationEnd);
832      xKeyframes.setAttribute('animated', '');
833      assertComputed(xKeyframes, '0px', 'left');
834    });
835
836    test('keyframe names are transformed correctly', function(done) {
837      makeElement('scoped-keyframes');
838      var e = document.createElement('scoped-keyframes');
839      document.body.appendChild(e);
840      flush();
841      var target = e.shadowRoot.querySelector('#target');
842      var onAnimationEnd = function() {
843        assertComputed(target, '10px');
844        target.removeEventListener('animationend', onAnimationEnd);
845        target.removeEventListener('webkitAnimationEnd', onAnimationEnd);
846        document.body.removeChild(e);
847        done();
848      };
849      target.addEventListener('animationend', onAnimationEnd);
850      target.addEventListener('webkitAnimationEnd', onAnimationEnd);
851      e.setAttribute('animate', '');
852      assertComputed(target, '0px');
853    });
854
855    test('attribute inclusive selector and general sibling selectors', function() {
856      var x = document.createElement('x-attr-selector');
857      x.id = 'x';
858      document.body.appendChild(x);
859      flush();
860      assertComputed(findNode('x.$.bar1'), '2px');
861      assertComputed(findNode('x.$.bar2'), '4px');
862      assertComputed(findNode('x.$.bar3'), '6px');
863      document.body.removeChild(x);
864    });
865
866    test('adjacent sibling selectors', function() {
867      makeElement('x-adjacent-sibling');
868      var x = document.createElement('x-adjacent-sibling');
869      x.id = 'x';
870      document.body.appendChild(x);
871      flush();
872      assertComputed(findNode('x.$.foo1'), '20px');
873      assertComputed(findNode('x.$.foo2'), '4px');
874      assertComputed(findNode('x.$.foo3'), '6px');
875      document.body.removeChild(x);
876    })
877
878    test('svg classes are dynamically scoped correctly', function() {
879      var x = document.createElement('x-dynamic-svg');
880      x.id = 'x';
881      document.body.appendChild(x);
882      flush();
883      var container = findNode('x.$.container');
884      var svg = container.querySelector('.svg');
885      var computed = getComputedStyle(svg);
886      assert.equal(computed.height, '24px');
887      assert.equal(computed.width, '24px');
888      var circle = container.querySelector('#circle');
889      computed = getComputedStyle(circle);
890      assert.equal(computed['fill-opacity'], '0.5');
891      document.body.removeChild(x);
892    });
893
894    test(':host selectors always lowest priority', function() {
895      var priority = findNode('el.$.priority');
896      assertComputed(priority, '9px');
897      el.setAttribute('class', 'wide');
898      assertComputed(priority, '9px');
899    });
900
901    test('svg elements properly scoped', function() {
902      if (window.ShadyCSS.nativeShadow) {
903        this.skip();
904      }
905      var circle = findNode('el.$.circle');
906      var classes = (circle.getAttribute('class') || '').split(/\s+/);
907      assert.include(classes, 'x-scoped');
908      assert.include(classes, 'style-scope');
909      assert.notInclude(classes, 'null');
910      assertComputed(circle, '1px', 'strokeWidth');
911    });
912
913    test('set attribute class has style scoping selectors', function() {
914      if (window.ShadyCSS.nativeShadow) {
915        this.skip();
916      }
917      var s = findNode('el.$.scopeClass');
918      var scope = findNode('el.$.scopeClass.$.scope');
919      assert.isTrue(s.classList.contains('style-scope'));
920      assert.isTrue(s.classList.contains('x-scoped'));
921      s.setAttribute('class', 'foo');
922      assert.isTrue(s.classList.contains('foo'));
923      assert.isTrue(s.classList.contains('style-scope'));
924      assert.isTrue(s.classList.contains('x-scoped'));
925      //
926      assert.isTrue(scope.classList.contains('style-scope'));
927      assert.isTrue(scope.classList.contains('x-scope-class'));
928      scope.setAttribute('class', 'foo');
929      assert.isTrue(scope.classList.contains('foo'));
930      assert.isTrue(scope.classList.contains('style-scope'));
931      assert.isTrue(scope.classList.contains('x-scope-class'));
932    });
933
934    test('specificity of :host selector with class', function() {
935      var e1 = document.createElement('x-specificity');
936      document.body.appendChild(e1);
937      flush();
938      assertComputed(e1, '1px');
939      document.body.removeChild(e1);
940      var e2 = document.createElement('x-specificity');
941      e2.setAttribute('class', 'bar');
942      document.body.appendChild(e2);
943      flush();
944      assertComputed(e2, '2px');
945      document.body.removeChild(e2);
946    });
947
948    test('self-use is supported', function() {
949      makeElement('self-test');
950      var e = document.createElement('self-test');
951      document.body.appendChild(e);
952      flush();
953      assertComputed(e.shadowRoot.querySelector('a'), '10px');
954      document.body.removeChild(e);
955    });
956
957    test('nth-child selectors work correctly with plusses', function() {
958      makeElement('nth-plus-one');
959      var e = document.createElement('nth-plus-one');
960      document.body.appendChild(e);
961      flush();
962      assertComputed(e.shadowRoot.querySelector('.foo'), 'rgb(255, 0, 0)', 'color');
963      document.body.removeChild(e);
964    });
965
966    test(':-webkit-any and :-moz-any selectors are supported', function() {
967      if (navigator.userAgent.match(/Trident|Edge/)) {
968        this.skip();
969      }
970      makeElement('any-selector');
971      var e = document.createElement('any-selector');
972      document.body.appendChild(e);
973      flush();
974      assertComputed(e.shadowRoot.querySelector('div'), 'rgb(123, 123, 123)', 'color');
975      assertComputed(e.shadowRoot.querySelector('span'), 'rgb(123, 123, 123)', 'color');
976      document.body.removeChild(e);
977    });
978
979    test(':host() sets mixin definitions correctly', function() {
980      makeElement('bad-mixin');
981      var e = document.createElement('bad-mixin');
982      document.body.appendChild(e);
983      flush();
984      assertComputed(e.shadowRoot.querySelector('div'), '0px');
985      document.body.removeChild(e);
986    });
987
988    test('trees with elements missing styles render correctly', function() {
989      makeElement('x-parent-skip');
990      makeElement('x-skip');
991      makeElement('x-child-skip');
992      const p = document.createElement('x-parent-skip');
993      document.body.appendChild(p);
994      flush();
995      const inner = p.shadowRoot.querySelector('x-skip').shadowRoot.querySelector('x-child-skip').shadowRoot.querySelector('div');
996      assertComputed(inner, '10px');
997      document.body.removeChild(p);
998    });
999
1000    test('trees with elements missing templates render correctly', function() {
1001      makeElement('no-shadow');
1002      const p = document.createElement('x-parent-skip');
1003      const n = document.createElement('no-shadow');
1004      const c = document.createElement('x-child-skip');
1005      document.body.appendChild(p);
1006      p.shadowRoot.appendChild(n);
1007      n.shadowRoot.appendChild(c);
1008      flush();
1009      const inner = c.shadowRoot.querySelector('div');
1010      assertComputed(inner, '10px');
1011      document.body.removeChild(p);
1012    })
1013
1014  });
1015
1016  suite('unscoping', function() {
1017    suiteSetup(function() {
1018      makeElement('shady-unscoped');
1019    });
1020    test('styles with "shady-unscoped" attr work in Shady and Shadow', function() {
1021      var el = document.createElement('shady-unscoped');
1022      document.body.appendChild(el);
1023      flush();
1024      var div = el.shadowRoot.querySelector('div');
1025      assertComputed(div, 'rgb(255, 0, 0)', 'color');
1026      document.body.removeChild(el);
1027    });
1028    test('styles with "shady-unscoped" attr deduplicate', function(){
1029      if (window.ShadyCSS.nativeShadow) {
1030        this.skip();
1031      }
1032      makeElement('shady-unscoped-2');
1033      var el1 = document.createElement('shady-unscoped');
1034      var el2 = document.createElement('shady-unscoped-2');
1035      document.body.appendChild(el1);
1036      document.body.appendChild(el2);
1037      flush();
1038      assert.equal(document.querySelectorAll('style[shady-unscoped]').length, 1);
1039      document.body.removeChild(el1);
1040      document.body.removeChild(el2);
1041    });
1042    test('@apply does not work in shady-unscoped', function() {
1043      makeElement('unscoped-apply-user');
1044      makeElement('unscoped-apply');
1045      var el = document.createElement('unscoped-apply');
1046      document.body.appendChild(el);
1047      flush();
1048      var inner = el.shadowRoot.querySelector('unscoped-apply-user');
1049      var target = inner.shadowRoot.querySelector('div');
1050      assertComputed(target, '0px');
1051      document.body.removeChild(el);
1052    });
1053  });
1054
1055})();
1056</script>
1057</body>
1058</html>
1059