Skip to content

Interactivity API: Fix router initialization race condition on Safari/Firefox#76053

Merged
luisherranz merged 26 commits intoWordPress:trunkfrom
markusfoo:Router-initialization-race-condition-on-Safari/Firefox
Mar 5, 2026
Merged

Interactivity API: Fix router initialization race condition on Safari/Firefox#76053
luisherranz merged 26 commits intoWordPress:trunkfrom
markusfoo:Router-initialization-race-condition-on-Safari/Firefox

Conversation

@markusfoo
Copy link

@markusfoo markusfoo commented Mar 2, 2026

Description

This PR resolves the race condition issue diagnosed in Firefox and Safari, where @wordpress/interactivity-router evaluates before hydrateRegions() completes, leading to premature toVdom() calls, islands marked as hydrated, and completely non-functional interactive regions (dead DOM).

The root cause and step-by-step solution have been successfully implemented in the commits of this pull request, following the exact structure proposed and refined through detailed discussion in #75778.

The fix has been manually verified on the test site:

https://markuss.cu.ma/shop/

(All changes were applied directly to the compiled JS equivalent of the presented .ts files, matching the proposed structure.)

Special thanks to @DAreRodz for the insightful feedback, clear architectural guidance, and collaborative help in resolving this longstanding issue with the Interactivity API.

Summary

Fixes a critical race condition where @wordpress/interactivity-router evaluates before hydrateRegions() completes on slower JavaScript engines. This resulted in interactive regions being permanently non-functional (dead DOM) on Firefox and Safari.

Fixes #75778.

The Problem

As extensively documented in #75778, the router accesses initialVdom (a WeakMap) at module-evaluation time. On Safari (JavaScriptCore) and Firefox (SpiderMonkey), the router often evaluates before the hydration phase has populated this map.

The router falls back to calling toVdom() on the full document. This function has an intentional side-effect: it calls hydratedIslands.add() for every [data-wp-interactive] node to handle nested islands. When the actual hydrateRegions() subsequently runs, it sees all islands marked as hydrated and skips them.

Result: The page renders visually, but all event listeners are missing. The DOM is "dead".

Video_260302213150.mp4

Symptoms:

  • Mini-Cart, Add to Cart buttons, and custom data-wp-on--click elements unresponsive.
  • Reproducible on new tabs, warm HTTP cache, and tab restoration.
  • Chrome (V8) typically masked this due to faster module evaluation speeds.

The Fix

This PR introduces a synchronization promise to enforce the correct initialization order.

  • packages/interactivity/src/hydration.ts
    Added initialVdomPromise, a Promise that resolves with the WeakMap only after hydrateRegions() finishes.
  • packages/interactivity/src/index.ts
    Exposed initialVdomPromise through privateApis.
  • packages/interactivity-router/src/index.ts
    Wrapped the initial pages.set() logic in an async IIFE that awaits initialVdomPromise.

The router now explicitly waits for the signal that hydration is complete, removing the dependency on engine execution speed.

What is not changed

  • vdom.ts: hydratedIslands.add() logic is untouched; it is correct and necessary for nested islands.
  • Public API: No breaking changes. initialVdom type remains WeakMap.

Testing

Verified on the reproduction site (WordPress 7.0 Beta 2 + WooCommerce + Hybrid Theme):

  • Firefox 148.0 (Current) / 147.0.4 / Nightly 150.0a1 ✅ Fixed (previously dead DOM on warm cache).
  • Chrome 138.0.7204.305 ✅ No regression.
  • Opera 127.0.5778.76 ✅ No regression.
  • Safari iOS 18 ✅ Fixed (previously dead DOM on new tabs/restoration).

All interactive elements are now fully functional across all tested engines.

References

Fixes #75778.

Marcus Karlos added 4 commits March 2, 2026 19:44
…dition in Interactivity Router

+ Add a new exported promise initialVdomPromise that resolves with the populated initialVdom WeakMap after the hydration loop completes. This allows dependent modules (like the router) to await hydration without altering the type or synchronous access to initialVdom.
+ Introduce a resolver function resolveInitialVdom to control the promise resolution.
+ Resolve the promise at the end of hydrateRegions() after all nodes are processed, ensuring the WeakMap is fully populated.
Added "Definite Assignment Assertion" line 33 for resolveInitialVdom
- Added `initialVdomPromise` to the return object of the `privateApis` function.
- This exposes the promise to internal packages (like the Router) without making it a public API.
- Imported `initialVdomPromise` from `@wordpress/interactivity`.
- Wrapped the initial page caching logic inside an async IIFE that awaits `initialVdomPromise`.
- This ensures the Router waits for hydration to complete before processing the initial page, preventing "dead DOM" issues on Safari and Firefox where module evaluation order was unpredictable.
@github-actions github-actions bot added [Package] Interactivity /packages/interactivity [Package] Interactivity Router /packages/interactivity-router labels Mar 2, 2026
@github-actions
Copy link

github-actions bot commented Mar 2, 2026

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: markusfoo <power2009@git.wordpress.org>
Co-authored-by: DAreRodz <darerodz@git.wordpress.org>
Co-authored-by: luisherranz <luisherranz@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@github-actions github-actions bot added the First-time Contributor Pull request opened by a first-time contributor to Gutenberg repository label Mar 2, 2026
@github-actions
Copy link

github-actions bot commented Mar 2, 2026

👋 Thanks for your first Pull Request and for helping build the future of Gutenberg and WordPress, @markusfoo! In case you missed it, we'd love to have you join us in our Slack community.

If you want to learn more about WordPress development in general, check out the Core Handbook full of helpful information.

Marcus Karlos added 3 commits March 2, 2026 22:07
I have fixed the TypeScript error (`TS18004`) by adding the missing import for `initialVdomPromise` in import
Resolve linting error by breaking long type definitions onto multiple lines to comply with Prettier rules.
@DAreRodz
Copy link
Contributor

DAreRodz commented Mar 3, 2026

Thanks, @markusfoo!

Could you add an e2e test that reproduces the issue and passes with your changes? It would require a test page enqueuing the iAPI router, so we ensure the router is evaluated before the iAPI runtime in all browsers.

@DAreRodz DAreRodz added the [Type] Bug An existing feature does not function as intended label Mar 3, 2026
Registers the test/router-race-condition block. Declares viewScriptModule and render so WordPress picks up the other files automatically.
Marcus Karlos added 7 commits March 3, 2026 16:19
Renders two buttons with data-testid attributes. One is bound to local context (context.counter), one to global state (state.counter). Both bindings are only attached if hydration succeeds — if the race causes hydration to be skipped, clicks produce no change and the test fails.
…l browsers

Declares @wordpress/interactivity-router as 'import' => 'static' instead of 'dynamic'. This places the router in the page's initial ES module graph, guaranteeing its module-level code runs before hydrateRegions() in every browser — not only Firefox and Safari.
Registers the router-race-condition store with two actions: increment (mutates local context) and incrementGlobal (mutates global state). Covers both binding types independently.
Two Playwright tests asserting that context-driven and state-driven data-wp-on--click bindings are correctly attached after hydration, even when the router is evaluated before hydrateRegions() completes. Both tests fail on the un-patched codebase and pass with initialVdomPromise in place. (see WordPress#75778)
@markusfoo
Copy link
Author

Thanks, @markusfoo!

Could you add an e2e test that reproduces the issue and passes with your changes? It would require a test page enqueuing the iAPI router, so we ensure the router is evaluated before the iAPI runtime in all browsers.

Hi again and thank for review @DAreRodz — done! Added the e2e test in test/e2e/specs/interactivity/router-race-condition.spec.ts with a dedicated test block in packages/e2e-tests/plugins/interactive-blocks/router-race-condition/.

The fixture uses 'import' => 'static' for the router in view.asset.php, which places @wordpress/interactivity-router at the root of the ES module graph — guaranteeing its module-level code runs before hydrateRegions() in every browser, making the race deterministic and reproducible in CI (not just Firefox/Safari).

Two tests cover both binding types:

  • context-driven: data-wp-on--click bound to local context
  • global-state: data-wp-on--click bound to global state

What our test does:

view.asset.php with 'import' => 'static' is the key. When the router is declared as a static dependency, the browser places it in the initial ES module graph. This guarantees the router executes before hydrateRegions() — in Chrome, Firefox, Safari, any browser. This is exactly the race condition we were fixing.

What happens without the fix:

  • Router executes first → reads empty initialVdom WeakMap
  • Calls toVdom() on the entire document → all islands are added to hydratedIslands
  • hydrateRegions() sees islands already "processed" → skips them
  • Dead DOM — buttons don't respond to clicks

What our test verifies:

  • Clicks a button → expects the counter to change
  • If the DOM is dead — the click does nothing → test fails with a timeout

All 32 checks passed, including Playwright 1 – 8. ✅

Copy link
Member

@luisherranz luisherranz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're missing a changelog.

@DAreRodz DAreRodz self-requested a review March 4, 2026 12:35
@@ -17,6 +17,7 @@ import {
const {
getRegionRootFragment,
initialVdom,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The initialVdom no longer needs to be exported as a private API. The router already obtains the value through the promise.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done ✅

@markusfoo
Copy link
Author

Thanks, @markusfoo, for this PR!

I did some minor changes to the e2e test files so the actual bug is covered, and also simplified the comments present in the spec file. The rest of the code is good to me, and the bug is fixed. 🙂

@luisherranz, it would be good to have a second approval here before merging the PR, just in case.

Thank you so much, @DAreRodz! Your architectural guidance was invaluable throughout this process. Regarding the changelog — I was aware it was needed, but wanted to wait until the overall solution approach was confirmed and approved before finalizing it. Now that we've aligned on the architecture, the changelog entries have been added for both packages. Really appreciate your thorough review and the improvements you made to the e2e tests! 🙏

@markusfoo
Copy link
Author

We're missing a changelog.

Thank you for the feedback, @luisherranz! I've addressed both of your requests:

  • ✅ Changelog entries added to both packages/interactivity/CHANGELOG.md and packages/interactivity-router/CHANGELOG.md under ## Unreleased.
  • ✅ Removed initialVdom from privateApis exports — the router now obtains the value exclusively through initialVdomPromise, as you correctly pointed out.

Appreciate the precise review!

@markusfoo markusfoo requested a review from luisherranz March 4, 2026 14:08
@luisherranz luisherranz dismissed their stale review March 5, 2026 09:08

Requests addressed

@luisherranz
Copy link
Member

LGTM now, thanks @markusfoo.

I'll leave it to @DAreRodz to do the final review and merge.

Copy link
Contributor

@DAreRodz DAreRodz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Thanks, @markusfoo!

@luisherranz luisherranz merged commit 924f82e into WordPress:trunk Mar 5, 2026
42 of 45 checks passed
@luisherranz luisherranz added the Backport to WP 7.0 Beta/RC Pull request that needs to be backported to the WordPress major release that's currently in beta label Mar 5, 2026
@github-actions github-actions bot added this to the Gutenberg 22.8 milestone Mar 5, 2026
@github-actions
Copy link

github-actions bot commented Mar 5, 2026

There was a conflict while trying to cherry-pick the commit to the wp/7.0 branch. Please resolve the conflict manually and create a PR to the wp/7.0 branch.

PRs to wp/7.0 are similar to PRs to trunk, but you should base your PR on the wp/7.0 branch instead of trunk.

# Checkout the wp/7.0 branch instead of trunk.
git checkout wp/7.0

# Create a new branch for your PR.
git checkout -b my-branch

# Cherry-pick the commit.
git cherry-pick 924f82eca47d405f5bac143baae9444e6db9c162

# Check which files have conflicts.
git status

# Resolve the conflict...
# Add the resolved files to the staging area.
git status
git add .
git cherry-pick --continue

# Push the branch to the repository
git push origin my-branch

# Create a PR and set the base to the wp/7.0 branch.
# See https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-base-branch-of-a-pull-request.

luisherranz added a commit that referenced this pull request Mar 5, 2026
…/Firefox (#76053)

* Introduce synchronization promise for initialVdom to resolve race condition in Interactivity Router

+ Add a new exported promise initialVdomPromise that resolves with the populated initialVdom WeakMap after the hydration loop completes. This allows dependent modules (like the router) to await hydration without altering the type or synchronous access to initialVdom.
+ Introduce a resolver function resolveInitialVdom to control the promise resolution.
+ Resolve the promise at the end of hydrateRegions() after all nodes are processed, ensuring the WeakMap is fully populated.

* Update hydration.ts

Added "Definite Assignment Assertion" line 33 for resolveInitialVdom

* Expose initialVdomPromise via privateApis

- Added `initialVdomPromise` to the return object of the `privateApis` function.
- This exposes the promise to internal packages (like the Router) without making it a public API.

* Fix router race condition by awaiting hydration

- Imported `initialVdomPromise` from `@wordpress/interactivity`.
- Wrapped the initial page caching logic inside an async IIFE that awaits `initialVdomPromise`.
- This ensures the Router waits for hydration to complete before processing the initial page, preventing "dead DOM" issues on Safari and Firefox where module evaluation order was unpredictable.

* Fixed the TypeScript error (`TS18004`)

I have fixed the TypeScript error (`TS18004`) by adding the missing import for `initialVdomPromise` in import

* Fix Prettier formatting in hydration.ts

Resolve linting error by breaking long type definitions onto multiple lines to comply with Prettier rules.

* Fix Prettier formatting for resolveInitialVdom type

* Add test block registration for router hydration race condition

Registers the test/router-race-condition block. Declares viewScriptModule and render so WordPress picks up the other files automatically.

* Interactive markup for router race condition test block

Renders two buttons with data-testid attributes. One is bound to local context (context.counter), one to global state (state.counter). Both bindings are only attached if hydration succeeds — if the race causes hydration to be skipped, clicks produce no change and the test fails.

* Force static router import to make hydration race deterministic in all browsers

Declares @wordpress/interactivity-router as 'import' => 'static' instead of 'dynamic'. This places the router in the page's initial ES module graph, guaranteeing its module-level code runs before hydrateRegions() in every browser — not only Firefox and Safari.

* Add store with context and state actions for router race condition test

Registers the router-race-condition store with two actions: increment (mutates local context) and incrementGlobal (mutates global state). Covers both binding types independently.

* Add e2e test for router hydration race condition

Two Playwright tests asserting that context-driven and state-driven data-wp-on--click bindings are correctly attached after hydration, even when the router is evaluated before hydrateRegions() completes. Both tests fail on the un-patched codebase and pass with initialVdomPromise in place. (see #75778)

* Add PHP docblock to render.php to satisfy PHPCS coding standards

* Fix TypeScript errors in spec: use requestUtils.createPage instead of admin.insertBlock

* Fix e2e spec: use interactivityUtils fixture to activate test plugin

* Evaluate router code

* Make interactive region a router region

* Run e2e tests in Safari and Firefox too

* Move goto to a beforeEach callback and simplify comments

* Update CHANGELOG.md

* Update CHANGELOG iAPI

* Update CHANGELOG.md

* Update CHANGELOG.md (iAPI-router)

* Interactivity: Remove initialVdom from privateApis exports

* Interactivity Router: Remove initialVdom import, use WeakMap type in VdomParams

---------

Co-authored-by: markusfoo <power2009@git.wordpress.org>
Co-authored-by: DAreRodz <darerodz@git.wordpress.org>
Co-authored-by: luisherranz <luisherranz@git.wordpress.org>
@luisherranz
Copy link
Member

PR with the merge fix:

luisherranz added a commit that referenced this pull request Mar 5, 2026
…/Firefox (#76053) (#76191)

* Introduce synchronization promise for initialVdom to resolve race condition in Interactivity Router

+ Add a new exported promise initialVdomPromise that resolves with the populated initialVdom WeakMap after the hydration loop completes. This allows dependent modules (like the router) to await hydration without altering the type or synchronous access to initialVdom.
+ Introduce a resolver function resolveInitialVdom to control the promise resolution.
+ Resolve the promise at the end of hydrateRegions() after all nodes are processed, ensuring the WeakMap is fully populated.

* Update hydration.ts

Added "Definite Assignment Assertion" line 33 for resolveInitialVdom

* Expose initialVdomPromise via privateApis

- Added `initialVdomPromise` to the return object of the `privateApis` function.
- This exposes the promise to internal packages (like the Router) without making it a public API.

* Fix router race condition by awaiting hydration

- Imported `initialVdomPromise` from `@wordpress/interactivity`.
- Wrapped the initial page caching logic inside an async IIFE that awaits `initialVdomPromise`.
- This ensures the Router waits for hydration to complete before processing the initial page, preventing "dead DOM" issues on Safari and Firefox where module evaluation order was unpredictable.

* Fixed the TypeScript error (`TS18004`)

I have fixed the TypeScript error (`TS18004`) by adding the missing import for `initialVdomPromise` in import

* Fix Prettier formatting in hydration.ts

Resolve linting error by breaking long type definitions onto multiple lines to comply with Prettier rules.

* Fix Prettier formatting for resolveInitialVdom type

* Add test block registration for router hydration race condition

Registers the test/router-race-condition block. Declares viewScriptModule and render so WordPress picks up the other files automatically.

* Interactive markup for router race condition test block

Renders two buttons with data-testid attributes. One is bound to local context (context.counter), one to global state (state.counter). Both bindings are only attached if hydration succeeds — if the race causes hydration to be skipped, clicks produce no change and the test fails.

* Force static router import to make hydration race deterministic in all browsers

Declares @wordpress/interactivity-router as 'import' => 'static' instead of 'dynamic'. This places the router in the page's initial ES module graph, guaranteeing its module-level code runs before hydrateRegions() in every browser — not only Firefox and Safari.

* Add store with context and state actions for router race condition test

Registers the router-race-condition store with two actions: increment (mutates local context) and incrementGlobal (mutates global state). Covers both binding types independently.

* Add e2e test for router hydration race condition

Two Playwright tests asserting that context-driven and state-driven data-wp-on--click bindings are correctly attached after hydration, even when the router is evaluated before hydrateRegions() completes. Both tests fail on the un-patched codebase and pass with initialVdomPromise in place. (see #75778)

* Add PHP docblock to render.php to satisfy PHPCS coding standards

* Fix TypeScript errors in spec: use requestUtils.createPage instead of admin.insertBlock

* Fix e2e spec: use interactivityUtils fixture to activate test plugin

* Evaluate router code

* Make interactive region a router region

* Run e2e tests in Safari and Firefox too

* Move goto to a beforeEach callback and simplify comments

* Update CHANGELOG.md

* Update CHANGELOG iAPI

* Update CHANGELOG.md

* Update CHANGELOG.md (iAPI-router)

* Interactivity: Remove initialVdom from privateApis exports

* Interactivity Router: Remove initialVdom import, use WeakMap type in VdomParams

---------

Co-authored-by: Marcus Karlos <manager1@onmail.com>
Co-authored-by: markusfoo <power2009@git.wordpress.org>
Co-authored-by: DAreRodz <darerodz@git.wordpress.org>
pento pushed a commit to WordPress/wordpress-develop that referenced this pull request Mar 5, 2026
CI run: #11167.

See #64595.

---

I've included a log of the Gutenberg changes with the following command:

git log --reverse --format="- %s" 022d8dd3d461f91b15c1f0410649d3ebb027207f..e499abfb843a43ac88455ca319220c5f181e1cf3 | sed 's|#\([0-9][0-9]*\)|https://github.com/WordPress/gutenberg/pull/\1|g; /github\.com\/WordPress\/gutenberg\/pull/!d' | pbcopy

- Add documentation for contentRole and listView block supports (WordPress/gutenberg#75903)
- Interactivity Router: fix back and forward navigation after refresh (WordPress/gutenberg#75927)
- Real-time collaboration: Fix disconnect dialog on navigate (WordPress/gutenberg#75886)
- Real Time Collab: Throttle syncing for inactive tabs. (WordPress/gutenberg#75843)
- Components: Specify line-height to avoid inheriting default values (WordPress/gutenberg#75880)
- Pattern Editing: Fix sibling blocks to edited pattern not being disabled (WordPress/gutenberg#75994)
- Sync connector PHP behavior with Core backport changes (WordPress/gutenberg#75968)
- Connectors: Avoid manual string concatenation (WordPress/gutenberg#75997)
- DataForm: fix field label for panel (should not be uppercase) (WordPress/gutenberg#75944)
- Views: add support for more overrides (all developer-defined config) (WordPress/gutenberg#75971)
- Use homeUrl instead of siteUrl for link badge evaluations (WordPress/gutenberg#75978)
- DataViews: Right-align `integer` and `number` fields (WordPress/gutenberg#75917)
- Navigation Link: Compare internal links by host instead of origin (WordPress/gutenberg#76015)
- Fix: Skip scaled image sideload for images below big image threshold (WordPress/gutenberg#75990)
- Client side media cherry pick for 7.0 (WordPress/gutenberg#75998)
- Show transform dropdown previews on focus as well as hover (WordPress/gutenberg#75940) (WordPress/gutenberg#75992)
- RTC: Fix syncing of emoji / surrogate pairs (WordPress/gutenberg#76049)
- [Real-time Collaboration] Fix sync issue on refresh (WordPress/gutenberg#76017)
- Real-time collaboration: Improve disconnect dialog (WordPress/gutenberg#75970)
- DataViews: Fix filter toggle flickering when there are locked or primary filters (WordPress/gutenberg#75913) (WordPress/gutenberg#76068)
- Connectors: Dynamically register providers from WP AI Client registry (WordPress/gutenberg#76014)
- PHP-only Blocks: Reflect bound attribute values in inspector controls (WordPress/gutenberg#76040)
- Fix: Set quality and strip metadata in client-side image resize (WordPress/gutenberg#76029)
- RTC: Prevent duplicate poll cycles (WordPress/gutenberg#76059)
- RTC: Fix stale CRDT document persisted on save (WordPress/gutenberg#75975)
- RTC: Disable multiple collaborators if meta boxes are present (WordPress/gutenberg#75939)
- Directly inject styles in overlay to make styles stay consistently mounted (WordPress/gutenberg#75700)
- Real-time collaboration: Fix comment syncing on site editor (WordPress/gutenberg#75746)
- Real-time Collaboration: Bug fix for CRDT user selection and add tests (WordPress/gutenberg#75075)
- RTC: Updates from backport PR (WordPress/gutenberg#75711)
- RTC: Fix undefined array_first() call in sync storage (WordPress/gutenberg#75869)
- RTC: Fix fallthrough for sync update switch statement (WordPress/gutenberg#76060)
- Real-time collaboration: Remove block client IDs from Awareness, fix "Show Template" view (WordPress/gutenberg#75590)
- RTC: Add session activity notifications (WordPress/gutenberg#76065)
- Prevent non-reproducible Sass/CSS builds. (WordPress/gutenberg#76098)
- Block toolbar and context menu: hide pattern actions in Revisions UI (WordPress/gutenberg#76066)
- Try enabling style variation transforms for blocks in contentOnly mode (WordPress/gutenberg#75761)
- Block toolbar: hide styles dropdown in Revisions UI (WordPress/gutenberg#76119)
- Image block: fix lightbox srcset size (WordPress/gutenberg#76092)
- Fix writing flow navigation for annotation style, or any other block with border radius (WordPress/gutenberg#76072)
- Image: Hide 'Set as featured image' for in-editor revisions (WordPress/gutenberg#76123)
- Connectors: Gate unavailable install actions behind install capability (WordPress/gutenberg#75980)
- build: Exclude experimental pages from Core builds (WordPress/gutenberg#76038)
- HTML & Shortcode: Disable viewport visibility support (WordPress/gutenberg#76138)
- RTC: Verify client ID to avoid awareness mutation (WordPress/gutenberg#76056)
- wp-build: Do not remove Core's default script modules registration (WordPress/gutenberg#75705)
- wp-build: Deregister script modules before re-registering (WordPress/gutenberg#75909)
- Remove `! function_exists()` checks from PHP templates (WordPress/gutenberg#76062)
- Connectors: Update page identifier to options-connectors (WordPress/gutenberg#76156)
- Connectors: Align init hook priorities with Core overrides (WordPress/gutenberg#76161)
- Icon Block: Clean up selectors config (WordPress/gutenberg#75786)
- Icons: Fix incorrect icon slug (WordPress/gutenberg#76165)
- RTC: Enable RTC by default (WordPress/gutenberg#75739)
- Rename and visibility modals: gate shortcuts behind canEditBlock to prevent triggering in revisions UI (WordPress/gutenberg#76168)
- Fix: Block style variations not rendering in Site Editor Patterns page (WordPress/gutenberg#76122)
- Client-side media processing: only use media upload provider when not in preview mode (WordPress/gutenberg#76124)
- Notes: Disable for in-editor revisions (WordPress/gutenberg#76180)
- Core Data: Support reading revision data in useEntityProp (fixes footnotes in revisions UI) (WordPress/gutenberg#76106)
- Client-side media processing: Try plumbing invalidation to the block-editor's mediaUpload onSuccess callback (WordPress/gutenberg#76173)
- Connectors: Improve responsive layout on small screens (WordPress/gutenberg#76186)
- Interactivity API: Fix router initialization race condition on Safari/Firefox (WordPress/gutenberg#76053) (WordPress/gutenberg#76191)
- Interactivity: Fix crypto.randomUUID crash in non-secure contexts (WordPress/gutenberg#76151)


git-svn-id: https://develop.svn.wordpress.org/trunk@61843 602fd350-edb4-49c9-b593-d223f7449a82
markjaquith pushed a commit to markjaquith/WordPress that referenced this pull request Mar 5, 2026
CI run: WordPress/wordpress-develop#11167.

See #64595.

---

I've included a log of the Gutenberg changes with the following command:

git log --reverse --format="- %s" 022d8dd3d461f91b15c1f0410649d3ebb027207f..e499abfb843a43ac88455ca319220c5f181e1cf3 | sed 's|#\([0-9][0-9]*\)|https://github.com/WordPress/gutenberg/pull/\1|g; /github\.com\/WordPress\/gutenberg\/pull/!d' | pbcopy

- Add documentation for contentRole and listView block supports (WordPress/gutenberg#75903)
- Interactivity Router: fix back and forward navigation after refresh (WordPress/gutenberg#75927)
- Real-time collaboration: Fix disconnect dialog on navigate (WordPress/gutenberg#75886)
- Real Time Collab: Throttle syncing for inactive tabs. (WordPress/gutenberg#75843)
- Components: Specify line-height to avoid inheriting default values (WordPress/gutenberg#75880)
- Pattern Editing: Fix sibling blocks to edited pattern not being disabled (WordPress/gutenberg#75994)
- Sync connector PHP behavior with Core backport changes (WordPress/gutenberg#75968)
- Connectors: Avoid manual string concatenation (WordPress/gutenberg#75997)
- DataForm: fix field label for panel (should not be uppercase) (WordPress/gutenberg#75944)
- Views: add support for more overrides (all developer-defined config) (WordPress/gutenberg#75971)
- Use homeUrl instead of siteUrl for link badge evaluations (WordPress/gutenberg#75978)
- DataViews: Right-align `integer` and `number` fields (WordPress/gutenberg#75917)
- Navigation Link: Compare internal links by host instead of origin (WordPress/gutenberg#76015)
- Fix: Skip scaled image sideload for images below big image threshold (WordPress/gutenberg#75990)
- Client side media cherry pick for 7.0 (WordPress/gutenberg#75998)
- Show transform dropdown previews on focus as well as hover (WordPress/gutenberg#75940) (WordPress/gutenberg#75992)
- RTC: Fix syncing of emoji / surrogate pairs (WordPress/gutenberg#76049)
- [Real-time Collaboration] Fix sync issue on refresh (WordPress/gutenberg#76017)
- Real-time collaboration: Improve disconnect dialog (WordPress/gutenberg#75970)
- DataViews: Fix filter toggle flickering when there are locked or primary filters (WordPress/gutenberg#75913) (WordPress/gutenberg#76068)
- Connectors: Dynamically register providers from WP AI Client registry (WordPress/gutenberg#76014)
- PHP-only Blocks: Reflect bound attribute values in inspector controls (WordPress/gutenberg#76040)
- Fix: Set quality and strip metadata in client-side image resize (WordPress/gutenberg#76029)
- RTC: Prevent duplicate poll cycles (WordPress/gutenberg#76059)
- RTC: Fix stale CRDT document persisted on save (WordPress/gutenberg#75975)
- RTC: Disable multiple collaborators if meta boxes are present (WordPress/gutenberg#75939)
- Directly inject styles in overlay to make styles stay consistently mounted (WordPress/gutenberg#75700)
- Real-time collaboration: Fix comment syncing on site editor (WordPress/gutenberg#75746)
- Real-time Collaboration: Bug fix for CRDT user selection and add tests (WordPress/gutenberg#75075)
- RTC: Updates from backport PR (WordPress/gutenberg#75711)
- RTC: Fix undefined array_first() call in sync storage (WordPress/gutenberg#75869)
- RTC: Fix fallthrough for sync update switch statement (WordPress/gutenberg#76060)
- Real-time collaboration: Remove block client IDs from Awareness, fix "Show Template" view (WordPress/gutenberg#75590)
- RTC: Add session activity notifications (WordPress/gutenberg#76065)
- Prevent non-reproducible Sass/CSS builds. (WordPress/gutenberg#76098)
- Block toolbar and context menu: hide pattern actions in Revisions UI (WordPress/gutenberg#76066)
- Try enabling style variation transforms for blocks in contentOnly mode (WordPress/gutenberg#75761)
- Block toolbar: hide styles dropdown in Revisions UI (WordPress/gutenberg#76119)
- Image block: fix lightbox srcset size (WordPress/gutenberg#76092)
- Fix writing flow navigation for annotation style, or any other block with border radius (WordPress/gutenberg#76072)
- Image: Hide 'Set as featured image' for in-editor revisions (WordPress/gutenberg#76123)
- Connectors: Gate unavailable install actions behind install capability (WordPress/gutenberg#75980)
- build: Exclude experimental pages from Core builds (WordPress/gutenberg#76038)
- HTML & Shortcode: Disable viewport visibility support (WordPress/gutenberg#76138)
- RTC: Verify client ID to avoid awareness mutation (WordPress/gutenberg#76056)
- wp-build: Do not remove Core's default script modules registration (WordPress/gutenberg#75705)
- wp-build: Deregister script modules before re-registering (WordPress/gutenberg#75909)
- Remove `! function_exists()` checks from PHP templates (WordPress/gutenberg#76062)
- Connectors: Update page identifier to options-connectors (WordPress/gutenberg#76156)
- Connectors: Align init hook priorities with Core overrides (WordPress/gutenberg#76161)
- Icon Block: Clean up selectors config (WordPress/gutenberg#75786)
- Icons: Fix incorrect icon slug (WordPress/gutenberg#76165)
- RTC: Enable RTC by default (WordPress/gutenberg#75739)
- Rename and visibility modals: gate shortcuts behind canEditBlock to prevent triggering in revisions UI (WordPress/gutenberg#76168)
- Fix: Block style variations not rendering in Site Editor Patterns page (WordPress/gutenberg#76122)
- Client-side media processing: only use media upload provider when not in preview mode (WordPress/gutenberg#76124)
- Notes: Disable for in-editor revisions (WordPress/gutenberg#76180)
- Core Data: Support reading revision data in useEntityProp (fixes footnotes in revisions UI) (WordPress/gutenberg#76106)
- Client-side media processing: Try plumbing invalidation to the block-editor's mediaUpload onSuccess callback (WordPress/gutenberg#76173)
- Connectors: Improve responsive layout on small screens (WordPress/gutenberg#76186)
- Interactivity API: Fix router initialization race condition on Safari/Firefox (WordPress/gutenberg#76053) (WordPress/gutenberg#76191)
- Interactivity: Fix crypto.randomUUID crash in non-secure contexts (WordPress/gutenberg#76151)

Built from https://develop.svn.wordpress.org/trunk@61843


git-svn-id: http://core.svn.wordpress.org/trunk@61130 1a063a9b-81f0-0310-95a4-ce76da25c4cd
@ellatrix ellatrix added Backported to WP Core Pull request that has been successfully merged into WP Core and removed Backport to WP 7.0 Beta/RC Pull request that needs to be backported to the WordPress major release that's currently in beta labels Mar 5, 2026
@markusfoo
Copy link
Author

Thank you both @DAreRodz and @luisherranz — really appreciate the
collaborative review process and the architectural guidance throughout.
Glad this one made it in time for 7.0! 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Backported to WP Core Pull request that has been successfully merged into WP Core First-time Contributor Pull request opened by a first-time contributor to Gutenberg repository [Package] Interactivity Router /packages/interactivity-router [Package] Interactivity /packages/interactivity [Type] Bug An existing feature does not function as intended

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Interactivity API fails to re-hydrate on iOS Safari after bfcache restoration

4 participants