Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f13a6f4
CI and tooling: update Node and Playwright, remove Saucelabs, add a f…
Oct 1, 2026
d52d0d0
scoped-custom-element-registry: redesign the polyfill
Oct 1, 2026
ffc8d71
scoped-custom-element-registry: rewrite the test suite
Oct 1, 2026
698b7c2
scoped-custom-element-registry: README and CHANGELOG
Oct 1, 2026
5b7f00f
CI: pin actions to commit hashes, restrict permissions, keep changelo…
Oct 1, 2026
a4e22c9
Fix the Closure build: no new.target; quote getName, states and disab…
Oct 1, 2026
9190319
fix tests on older browsers
Oct 1, 2026
729a707
fix typo
Oct 1, 2026
00371ed
add test script
Oct 1, 2026
aa6e5b4
update test config
Oct 1, 2026
8ed2ddc
update readme to remove wip note
Oct 1, 2026
0d323d8
Detection: a null registry must keep an element from being customized…
Oct 2, 2026
8b60db6
Constructor identity, upgrade re-entry, importNode options, template …
Oct 4, 2026
e399716
Customize parsed elements once inserted, before a following script runs
Oct 7, 2026
f60f443
attachShadow: support the earlier registry and customElements options…
Oct 7, 2026
0de794e
Share the polyfill's state across same-origin windows
Oct 5, 2026
32a1cc4
Adopt nodes as the spec says; upgrade in document association order
Oct 5, 2026
8a59772
Entry points first customize elements still waiting for their microtask
Oct 5, 2026
208da39
Tests: adoption rules and across windows
Oct 5, 2026
ba12fce
Run the WPT custom element registry tests in the suite, with per-brow…
Oct 5, 2026
f1a67c2
webkit wpt baseline
Oct 5, 2026
7284ff6
WPT: use its own port so test:wpt can run alongside test:forced
Oct 6, 2026
44bd74b
Reflect template.shadowRootCustomElementRegistry; harden the WPT proxy
Oct 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ packages/webcomponentsjs/custom-elements-es5-adapter.js
packages/webcomponentsjs/webcomponents-bundle.d.ts
packages/webcomponentsjs/webcomponents-bundle.js*

# Vendored web platform tests (see test/wpt/README.md)
packages/scoped-custom-element-registry/test/wpt/custom-elements/
packages/scoped-custom-element-registry/test/wpt/resources/
packages/scoped-custom-element-registry/test/wpt/common/

packages/scoped-custom-element-registry/**/.wireit/
packages/scoped-custom-element-registry/**/.tsbuildinfo
packages/scoped-custom-element-registry/**/build/
48 changes: 14 additions & 34 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@ name: tests

on: [push, pull_request]

permissions:
contents: read

jobs:
lint:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 16
node-version: 24.5

- name: NPM install
run: npm ci
Expand All @@ -25,47 +28,24 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 16
node-version: 24.5

- name: NPM install
run: npm ci

- name: Lerna bootstrap
run: npm run bootstrap

- name: Build
run: npm run build

- name: Test
run: npm test

sauce:
# Skip if this is from a forked repository because we can't access Sauce
# secrets.
if: github.event.pull_request.base.repo.id == github.event.pull_request.head.repo.id

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16

- name: NPM install
run: npm ci

- name: Lerna bootstrap
run: npm run bootstrap
- name: Install playwright browsers (+ deps)
run: |
cd packages/tests && npx playwright install --with-deps
cd $GITHUB_WORKSPACE/packages/scoped-custom-element-registry && npx playwright install --with-deps

- name: Build
run: npm run build

- name: Test
env:
SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
run: npm run test-sauce
run: lerna run test --concurrency 1 --stream --no-bail
5 changes: 5 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ packages/scoped-custom-element-registry/**/.wireit/
packages/scoped-custom-element-registry/**/.tsbuildinfo
packages/scoped-custom-element-registry/**/build/

# Vendored web platform tests (see test/wpt/README.md)
packages/scoped-custom-element-registry/test/wpt/custom-elements/
packages/scoped-custom-element-registry/test/wpt/resources/
packages/scoped-custom-element-registry/test/wpt/common/

LICENSE
packages/*/LICENSE.md
packages/tests/html-imports/html/imports/unclosed-import.html
4 changes: 4 additions & 0 deletions packages/scoped-custom-element-registry/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Vendored web platform tests (see test/wpt/README.md)
test/wpt/custom-elements/
test/wpt/resources/
test/wpt/common/
4 changes: 4 additions & 0 deletions packages/scoped-custom-element-registry/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Vendored web platform tests (see test/wpt/README.md)
test/wpt/custom-elements/
test/wpt/resources/
test/wpt/common/
103 changes: 103 additions & 0 deletions packages/scoped-custom-element-registry/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,112 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

- `define` no longer writes `formAssociated` onto the class; whether a tag is
form-associated is still fixed by its first definition
- `element.constructor === HTMLElement` holds for elements whose prototype is
`HTMLElement.prototype`, including ones not yet customized
- As natively, an upgrade's constructor can't construct its own class again
after calling `super()`; a direct construction (`createElement`, the main
parser) still can
- `importNode` honors `selfOnly`, and rejects a null `customElementRegistry`
with a `TypeError`
- `Range.createContextualFragment` in a template's context gives elements a
null registry

### Changed

- Updated to latest [proposed spec](https://github.com/whatwg/html/issues/10854)
- Elements are customized once their tree is known: at the end of the DOM call
that created them, or, for the main document's parser, once inserted, before
any script that follows them runs
- The registry of parsed content is worked out from where it is, so reading
registries and calling `initialize` during parsing match native
- Cloning keeps each node's registry, including in clonable shadow roots
- Adoption into another document changes null and global registries, as
natively
- `attachInternals` returns the element's single `ElementInternals`, and
honors `disabledFeatures`

### Added

- customElements.initialize: sets registry on a DOM tree
- document.createElement(NS): takes options with {customElementRegistry}
- document.importNode: takes options with {selfOnly, customElementRegistry}
- Node.customElementRegistry set to creating registry
- Null registries: `customElementRegistry: null` options, and the
`customelementregistry` / `scopedcustomelementregistry` attributes
- Declarative shadow roots with a null registry, via
`shadowrootcustomelementregistry` plus `polyfill-shadowrootcustomelementregistry`
on the host
- `polyfill-customelementregistry` / `polyfill-scopedcustomelementregistry`:
null registry attributes always handled by the polyfill
- `:state(polyfill-undefined)` on elements the polyfill hasn't customized, for
use with or instead of `:defined`
- `Range.cloneContents`, `Range.createContextualFragment`, `Range.insertNode`
and `Range.surroundContents` respect registries

### Fixed

- Fixes [issue](https://github.com/webcomponents/polyfills/issues/613) with setting `shadowRoot.customElements` on Safari's native implementation
- Errors thrown by constructors and callbacks are reported rather than thrown,
and don't stop other elements from upgrading
- `define` with an invalid name no longer leaves the registry in a bad state
- `connectedMoveCallback` is called for `moveBefore`, instead of disconnecting
and reconnecting the element
- Upgrade order: an element whose constructor calls a DOM API (e.g.
`attachShadow`) finishes before other elements created with it upgrade
- `importNode(node)` copies shallowly by default
- `disabledFeatures: ['shadow']` is honored
- An adopted element inserted into a disconnected tree, or passed to
`upgrade()`, no longer loses its upgrade when later connected
- A constructor can create or construct other elements before calling
`super()`
- An upgrade fails if the constructor returns a different object
- A constructor that throws before calling `super()` runs once
- `define` throws a `TypeError` for a non-constructor and a `SyntaxError` for an
invalid name before reading the class
- `define` reads `observedAttributes` only when there's an
`attributeChangedCallback`, and form callbacks only for a form-associated
class; it captures `disabledFeatures` once
- Customized built-in elements work in the global registry; a scoped registry
rejects `extends` with a `NotSupportedError`
- `form.elements` is the native collection again, live and with native named
lookup, filtered through a `Proxy` to leave out elements whose own class
isn't form-associated; those elements can no longer set form values
- `define` reads the class in the specified order and rejects callbacks that
aren't functions and `observedAttributes` or `disabledFeatures` that aren't
iterable; a customized built-in isn't visible until its class has been read
- An upgrade fails if the class disables shadow roots and the element has one
- `attachInternals` respects `disabledFeatures` for a directly constructed
element; an element whose constructor failed gets no form callbacks and
isn't a form control; `form.elements` treats only canonical numbers as
indexes, resolves names like `entries` to controls, and filters its keys and
property descriptors too
- A closed shadow root copied on a host without internals, including one
nested in another, is paired before any constructor can change its source,
or else keeps only its registry, instead of a reference to its source
- A copy with a global registry in a document with a scoped registry gets
null; `ElementInternals` form members (including `form`, `labels`,
`validity`) throw for an element that isn't form-associated, or whose
constructor failed; `define` rejects a class whose prototype isn't an
object; a directly constructed element whose class isn't form-associated,
of a tag that is, isn't part of its form
- Copies keep their sources' registries in template contents (without
`importNode`'s registry), in `Range.extractContents`, and in a cloned
document, which keeps a scoped registry; `whenDefined` rejects invalid names
itself
- An error in a callback the polyfill calls (initial attribute callbacks,
`connectedCallback` on upgrade, `attributeChangedCallback` from
`setAttribute`) is reported, not thrown, and the remaining callbacks run
- `attributeChangedCallback` follows each element's own definition: a class
defined in several registries, or a subclass of another defined class, no
longer gets extra or duplicate callbacks; removing an absent attribute
doesn't call back
- `define` leaves the class unchanged if it fails, throws `NotSupportedError`
for a used name or constructor, and while another definition is running
- `Range.createContextualFragment` gives new elements a null context registry
- A parsed element keeps its registry when moved or removed before anything
needed it

## [0.0.10] - 2025-02-26

Expand Down
Loading
Loading