Skip to content

Commit 55746ea

Browse files
committed
Address review comments
1 parent 5f2892a commit 55746ea

File tree

6 files changed

+45
-14
lines changed

6 files changed

+45
-14
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,11 @@ This allows HTML with shadow roots to be serialized to plain HTML, and the seria
1313
`<template shadowroot>` elements are transformed bottom up so that in the case where they're nested, all elements within the declarative shadow tree stay inert until they have all been moved from their `<template shadowroot>` elements.
1414

1515
If native support for `<template shadowroot>` is present, `hydrateShadowRoots` does nothing when called.
16+
17+
### Known limitations
18+
19+
* Does not currently look into imperatively created shadow roots.
20+
* The mutation observer implementation
21+
* May not work properly in the face of streaming HTML parsing. Needs investigation.
22+
* Will not notice imperatively created `<template shadowroot>` elements inside of other shadow roots.
23+

bench/many_deep.html

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@
88
import { hydrateShadowRoots as manualWalkHydration } from '../_implementation/manual_walk.js';
99
import { hydrateShadowRoots as querySelectorHydration } from '../_implementation/queryselectorall.js';
1010

11+
const implausiblyLongTimeToIndicateFailure = 9 * 1000 * 1000;
12+
1113
function benchmark(hydrateMethod) {
1214
let hydrateShadowRoots;
1315
if (hydrateMethod === 'manualWalk') {
1416
hydrateShadowRoots = manualWalkHydration;
1517
} else if (hydrateMethod === 'querySelector') {
1618
hydrateShadowRoots = querySelectorHydration;
1719
} else {
18-
return 9 * 1000 * 1000;
20+
return implausiblyLongTimeToIndicateFailure;
1921
}
2022
let html = '';
2123
const n = 1000;
@@ -40,7 +42,7 @@
4042
return duration;
4143
} else {
4244
// hydration failed, still see templates in the document!
43-
return 9 * 1000 * 1000;
45+
return implausiblyLongTimeToIndicateFailure;
4446
}
4547
}
4648

bench/many_flat.html

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@
88
import { hydrateShadowRoots as manualWalkHydration } from '../_implementation/manual_walk.js';
99
import { hydrateShadowRoots as querySelectorHydration } from '../_implementation/queryselectorall.js';
1010

11+
const implausiblyLongTimeToIndicateFailure = 9 * 1000 * 1000;
12+
1113
function benchmark(hydrateMethod) {
1214
let hydrateShadowRoots;
1315
if (hydrateMethod === 'manualWalk') {
1416
hydrateShadowRoots = manualWalkHydration;
1517
} else if (hydrateMethod === 'querySelector') {
1618
hydrateShadowRoots = querySelectorHydration;
1719
} else {
18-
return 9 * 1000 * 1000;
20+
return implausiblyLongTimeToIndicateFailure;
1921
}
2022
const html = `
2123
<div>
@@ -33,7 +35,7 @@
3335
return duration;
3436
} else {
3537
// hydration failed, still see templates in the document!
36-
return 9 * 1000 * 1000;
38+
return implausiblyLongTimeToIndicateFailure;
3739
}
3840
}
3941

src/_implementation/manual_walk.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
* http://polymer.github.io/PATENTS.txt
1313
*/
1414

15-
import { hasNativeDeclarativeShadowRoots } from './feature_detect.js';
16-
import { isTemplate, isElement, hasNoParentElement } from './util.js';
15+
import {hasNativeDeclarativeShadowRoots} from './feature_detect.js';
16+
import {hasNoParentElement, isElement, isTemplate} from './util.js';
1717

1818
/*
1919
* Traverses the DOM to find all <template> elements with a `shadowroot`
@@ -75,8 +75,8 @@ export const hydrateShadowRoots = (root: ParentNode) => {
7575
const shadow = host.attachShadow({mode, delegatesFocus});
7676
shadow.append(template.content);
7777
} catch {
78-
// there was already a closed shadow root, so do nothing, and
79-
// don't delete the template
78+
// there was already a shadow root.
79+
// TODO(rictic): log an error event?
8080
}
8181
} else {
8282
template = undefined;

src/_implementation/mutation_observer.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,13 @@ export function transformShadowRoots(within: Node = document) {
2020
if (hasNativeDeclarativeShadowRoots()) {
2121
return {mutationObserver: undefined, cleanup() {}}; // do nothing
2222
}
23-
debugger;
2423
const mutationObserver = new MutationObserver((mutations, _) => {
25-
debugger;
2624
for (const mutation of mutations) {
25+
// TODO(rictic): test with streamed HTML that pauses in the middle
26+
// of a <template shadowroot> element. Do we get a mutation
27+
// with only part of the template's contents? What happens when
28+
// we remove the template from the DOM?
29+
// Tricky!
2730
hydrateShadowRoots(mutation.target as unknown as ParentNode);
2831
}
2932
});

src/_implementation/queryselectorall.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,22 @@
1414

1515
import {hasNativeDeclarativeShadowRoots} from './feature_detect.js';
1616

17+
interface StackEntry {
18+
/**
19+
* The nearest ancestor <template> element, if any, of the templates.
20+
*/
21+
template: HTMLTemplateElement|undefined;
22+
/**
23+
* A stack of template elements inside `template` – or `root` if `template` is
24+
* undefined – to be processed.
25+
*
26+
* The array is in reverse document order, such that the elements at the end
27+
* of the array should be processed first (so that we can use templates.pop()
28+
* to get the next element to process).
29+
*/
30+
templates: HTMLTemplateElement[];
31+
}
32+
1733
/*
1834
* Traverses the DOM to find all <template> elements with a `shadowroot`
1935
* attribute and move their content into a ShadowRoot on their parent element.
@@ -26,8 +42,8 @@ export const hydrateShadowRoots = (root: Element|DocumentFragment) => {
2642
if (hasNativeDeclarativeShadowRoots()) {
2743
return; // nothing to do
2844
}
29-
const stack = [{
30-
template: undefined as undefined | HTMLTemplateElement,
45+
const stack: StackEntry[] = [{
46+
template: undefined,
3147
templates: Array.from(root.querySelectorAll('template')).reverse()
3248
}];
3349
while (stack.length > 0) {
@@ -45,8 +61,8 @@ export const hydrateShadowRoots = (root: Element|DocumentFragment) => {
4561
const shadow = host.attachShadow({mode, delegatesFocus});
4662
shadow.append(template.content);
4763
} catch {
48-
// there was already a closed shadow root, so do nothing, and
49-
// don't delete the template
64+
// there was already a shadow root.
65+
// TODO(rictic): log an error event?
5066
}
5167
host.removeChild(template);
5268
}

0 commit comments

Comments
 (0)