fix: prevent panic in event dispatching when element data is missing …#2508
Conversation
…and add regression tests
🦋 Changeset detectedLatest commit: c5f070c The changes in this PR will be included in the next version bump. This PR includes changesets to release 9 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
📝 WalkthroughWalkthroughThis PR fixes a panic in the Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/web-platform/web-core/src/main_thread/client/element_apis/event_apis.rs (1)
167-179:⚠️ Potential issue | 🔴 CriticalHandle missing element data gracefully in
get_eventandget_eventslike other event APIs do.Lines 167 and 178 still use
.unwrap()which will panic if element data is missing. The recent fix (commit c5f070c) addressed this panic risk indispatch_event_by_path(returningfalse) andcommon_event_handler(usingmatchwith graceful handling). However,get_eventandget_eventswere excluded from that fix.Since these methods are called from JS via
__GetEventand__GetEventswith uniqueIds extracted from DOM elements, stale IDs from removed elements can cause panics. Other similar public methods incomponent_apis.rsanddataset_apis.rsreturnResult<T, JsError>instead. Consider updatingget_eventandget_eventsto follow the same pattern—either returningResult<T, JsError>or using graceful error handling consistent withdispatch_event_by_path.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/web-platform/web-core/src/main_thread/client/element_apis/event_apis.rs` around lines 167 - 179, get_event and get_events currently call self.get_element_data_by_unique_id(...).unwrap() which can panic for stale/missing elements; change them to handle missing element data gracefully like dispatch_event_by_path and common_event_handler: replace the unwrap() usage in get_event and get_events with a match or early-return that converts the missing element case into a JS-friendly error (e.g., return Result<JsValue, JsError> for get_event and Result<Vec<EventInfo>, JsError> for get_events) or return a sensible default/error value, ensuring you use the same error creation/JsError helper used elsewhere so JS callers (__GetEvent/__GetEvents) receive a non-panicking failure instead of a panic.
🧹 Nitpick comments (1)
packages/web-platform/web-core/tests/element-apis.spec.ts (1)
82-117: Deadeventlocals in both new tests.
eventis constructed viadocument.createEvent(...)/initEvent(...)but never used — the tests passeventObjecttocommon_event_handler. Dropping the unused setup keeps the regression tests focused.♻️ Proposed cleanup
test('#commonEventHandler should not crash on invalid uniqueId', () => { - // We send an event with a bubblePath containing a non-existent uniqueId const invalidUniqueId = 999999; - const event = document.createEvent('Event') as any; - event.initEvent('touchstart', true, true); - - // Create cross thread event manually for the test const eventObject = { type: 'touchstart', detail: {} } as any; ... test('#commonEventHandler should not crash on empty path', () => { - // We send an event with a bubblePath containing a non-existent uniqueId - const event = document.createEvent('Event') as any; - event.initEvent('touchstart', true, true); - - // Create cross thread event manually for the test const eventObject = { type: 'touchstart', detail: {} } as any;Also consider strengthening the assertion beyond
not.toThrow()— e.g. spy onmtsBinding.publishEventand assert it is not called for the invalid/empty cases, so a future regression that silently dispatches to the wrong element would still fail.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/web-platform/web-core/tests/element-apis.spec.ts` around lines 82 - 117, Remove the dead local `event` setup in both tests "#commonEventHandler should not crash on invalid uniqueId" and "#commonEventHandler should not crash on empty path" (the `document.createEvent(...)` and `initEvent(...)` lines) since the tests call mtsBinding.wasmContext!.common_event_handler with `eventObject`; then optionally strengthen the assertions by spying on `mtsBinding.publishEvent` (or equivalent) and asserting it was not called for the invalidUniqueId and empty Uint32Array cases to ensure no silent dispatch happens.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@packages/web-platform/web-core/src/main_thread/client/element_apis/event_apis.rs`:
- Around line 212-218: The code currently uses
bubble_unique_id_path.first().cloned().unwrap_or_default() which maps an empty
path to target_unique_id = 0 and then looks up get_element_data_by_unique_id(0);
change this to explicitly handle an empty bubble_unique_id_path by returning
false (or early exit) before computing target_unique_id so you don't
accidentally resolve to a real element with id 0; update the logic around
bubble_unique_id_path, target_unique_id, and the match on
get_element_data_by_unique_id to check bubble_unique_id_path.is_empty() first
and bail out before calling get_element_data_by_unique_id.
---
Outside diff comments:
In
`@packages/web-platform/web-core/src/main_thread/client/element_apis/event_apis.rs`:
- Around line 167-179: get_event and get_events currently call
self.get_element_data_by_unique_id(...).unwrap() which can panic for
stale/missing elements; change them to handle missing element data gracefully
like dispatch_event_by_path and common_event_handler: replace the unwrap() usage
in get_event and get_events with a match or early-return that converts the
missing element case into a JS-friendly error (e.g., return Result<JsValue,
JsError> for get_event and Result<Vec<EventInfo>, JsError> for get_events) or
return a sensible default/error value, ensuring you use the same error
creation/JsError helper used elsewhere so JS callers (__GetEvent/__GetEvents)
receive a non-panicking failure instead of a panic.
---
Nitpick comments:
In `@packages/web-platform/web-core/tests/element-apis.spec.ts`:
- Around line 82-117: Remove the dead local `event` setup in both tests
"#commonEventHandler should not crash on invalid uniqueId" and
"#commonEventHandler should not crash on empty path" (the
`document.createEvent(...)` and `initEvent(...)` lines) since the tests call
mtsBinding.wasmContext!.common_event_handler with `eventObject`; then optionally
strengthen the assertions by spying on `mtsBinding.publishEvent` (or equivalent)
and asserting it was not called for the invalidUniqueId and empty Uint32Array
cases to ensure no silent dispatch happens.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 5301e0a0-d179-4fbb-a4fe-79d382e779c1
📒 Files selected for processing (3)
.changeset/fix-event-apis-panic.mdpackages/web-platform/web-core/src/main_thread/client/element_apis/event_apis.rspackages/web-platform/web-core/tests/element-apis.spec.ts
Merging this PR will improve performance by 23.84%
Performance Changes
Comparing Footnotes
|
React External#633 Bundle Size — 674.83KiB (0%).c5f070c(current) vs ea5e30e main#622(baseline) Bundle metrics
|
| Current #633 |
Baseline #622 |
|
|---|---|---|
0B |
0B |
|
0B |
0B |
|
0% |
98.35% |
|
0 |
0 |
|
3 |
3 |
|
17 |
17 |
|
5 |
5 |
|
8.59% |
8.59% |
|
0 |
0 |
|
0 |
0 |
Bundle analysis report Branch PupilTong:p/hw/fix-empty-bubble-... Project dashboard
Generated by RelativeCI Documentation Report issue
React Example#7515 Bundle Size — 224.41KiB (0%).c5f070c(current) vs ea5e30e main#7504(baseline) Bundle metrics
|
| Current #7515 |
Baseline #7504 |
|
|---|---|---|
0B |
0B |
|
0B |
0B |
|
0% |
0% |
|
0 |
0 |
|
4 |
4 |
|
179 |
179 |
|
69 |
69 |
|
44.51% |
44.51% |
|
2 |
2 |
|
0 |
0 |
Bundle size by type no changes
| Current #7515 |
Baseline #7504 |
|
|---|---|---|
145.76KiB |
145.76KiB |
|
78.65KiB |
78.65KiB |
Bundle analysis report Branch PupilTong:p/hw/fix-empty-bubble-... Project dashboard
Generated by RelativeCI Documentation Report issue
React MTF Example#647 Bundle Size — 195.57KiB (0%).c5f070c(current) vs ea5e30e main#637(baseline) Bundle metrics
|
| Current #647 |
Baseline #637 |
|
|---|---|---|
0B |
0B |
|
0B |
0B |
|
0% |
0% |
|
0 |
0 |
|
3 |
3 |
|
173 |
173 |
|
66 |
66 |
|
44% |
44% |
|
2 |
2 |
|
0 |
0 |
Bundle size by type no changes
| Current #647 |
Baseline #637 |
|
|---|---|---|
111.23KiB |
111.23KiB |
|
84.34KiB |
84.34KiB |
Bundle analysis report Branch PupilTong:p/hw/fix-empty-bubble-... Project dashboard
Generated by RelativeCI Documentation Report issue
Web Explorer#9087 Bundle Size — 899.79KiB (+0.12%).c5f070c(current) vs ea5e30e main#9077(baseline) Bundle metrics
Bundle size by type
Bundle analysis report Branch PupilTong:p/hw/fix-empty-bubble-... Project dashboard Generated by RelativeCI Documentation Report issue |
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @lynx-js/react@0.120.0 ### Minor Changes - Bump `@lynx-js/internal-preact` from `10.28.4-dfff9aa` to `10.29.1-20260424024911-12b794f` ([diff](lynx-family/internal-preact@10.28.4-dfff9aa...10.29.1-20260424024911-12b794f)). ([#2512](#2512)) Fixes wrong DOM order when a keyed child moves to a different `$N` slot across a re-render. Cross-slot moves now land at the correct slot position instead of being appended past stable siblings. - Refactor preact to support multi-slots children and reduce the number and depth of snapshot. ([#1764](#1764)) ### Patch Changes - Fix stale callback-local references when transforming JSX inside `children={array.map(...)}` prop expressions. ([#2524](#2524)) - Fix ref callbacks not being cleaned up or re-applied correctly when the ref at the same element slot changes across rerenders that happen before hydration (e.g. a `useEffect` triggering `setState` during the initial background render). ([#2500](#2500)) - fix: reduce redundant updates for main-thread handlers and gestures ([#2188](#2188)) - Updates are faster when the main-thread event handler or gesture object is stable across rerenders (fewer unnecessary native updates). - Spread props rerenders that don't semantically change the handler/gesture no longer trigger redundant updates. - Removing a gesture from spread props reliably clears the gesture state on the target element. - Fix hydration edge cases by tolerating serialized snapshot nodes with missing `values` ([#2481](#2481)) - Keep ReactLynx Testing Library imports aligned with the contained snapshot runtime paths. ([#2498](#2498)) ## @lynx-js/template-webpack-plugin@0.11.0 ### Minor Changes - Add CSS source map support and source-mapped template encode diagnostics. ([#2483](#2483)) ### Patch Changes - fix: genStyleInfo should also preserve CSS variable fallback values when encoding web-core stylesheets so declarations like `var(--token, rgba(...))` are emitted with their fallback intact. ([#2502](#2502)) - Updated dependencies \[[`e179680`](e179680), [`8352530`](8352530), [`30f0277`](30f0277), [`887b8aa`](887b8aa), [`1d4abfc`](1d4abfc), [`25e196b`](25e196b), [`fb7bc84`](fb7bc84), [`9e149c4`](9e149c4), [`30f0277`](30f0277), [`9e149c4`](9e149c4)]: - @lynx-js/css-serializer@0.1.6 - @lynx-js/web-core@0.20.3 ## @lynx-js/i18next-translation-dedupe@0.0.1 ### Patch Changes - Introduce `@lynx-js/i18next-translation-dedupe` package to avoid bundling i18next translations twice in Lynx apps. ([#2482](#2482)) The package reads translations extracted by `rsbuild-plugin-i18next-extractor`, skips the extractor's default rendered asset, and writes the translations into the Lynx bundle custom section: ```json { "customSections": { "i18next-translations": { "content": { "en-US": { "hello": "Hello" }, "zh-CN": { "hello": "你好" } } } } } ``` ## @lynx-js/docs-mcp-server@0.2.2 ### Patch Changes - Fix Windows startup error. ([#2474](#2474)) ## @lynx-js/react-umd@0.120.0 ### Patch Changes - Support compile main-thread script to bytecode in external bundle ([#2459](#2459)) ## @lynx-js/rspeedy@0.14.3 ### Patch Changes - add a `sourceMap.css` option to emit CSS sourcemaps. ([#2442](#2442)) By default, `sourceMap.css` is false. You can set it to true to emit CSS sourcemaps. ```js import { defineConfig } from "@lynx-js/rspeedy"; export default defineConfig({ output: { sourceMap: { css: true, }, }, }); ``` - bump rsdoctor to 1.5.6 ([#2410](#2410)) - Enable CSS source maps by default in Rspeedy output config. ([#2483](#2483)) - Prefer physical routable IPv4 addresses over tunnel and link-local interfaces when resolving the dev host IP for generated preview and bundle URLs. ([#2409](#2409)) - Updated dependencies \[]: - @lynx-js/web-rsbuild-server-middleware@0.20.3 ## @lynx-js/lynx-bundle-rslib-config@0.3.2 ### Patch Changes - Support compile main-thread script to bytecode in external bundle ([#2459](#2459)) - Updated dependencies \[[`e179680`](e179680)]: - @lynx-js/css-serializer@0.1.6 ## @lynx-js/react-rsbuild-plugin@0.16.1 ### Patch Changes - Respect `dev.hmr: false` when installing React Refresh integrations so disabled HMR no longer injects the refresh loader or plugin. ([#2487](#2487)) - Fix stale callback-local references when transforming JSX inside `children={array.map(...)}` prop expressions. ([#2524](#2524)) - Supports @lynx-js/react 0.120.0 ([#1764](#1764)) - Updated dependencies \[[`e179680`](e179680), [`13655ac`](13655ac), [`f15494b`](f15494b), [`e179680`](e179680), [`e179680`](e179680)]: - @lynx-js/template-webpack-plugin@0.11.0 - @lynx-js/css-extract-webpack-plugin@0.7.1 - @lynx-js/react-webpack-plugin@0.9.2 - @lynx-js/react-alias-rsbuild-plugin@0.16.1 - @lynx-js/use-sync-external-store@1.5.0 - @lynx-js/react-refresh-webpack-plugin@0.3.5 ## @lynx-js/css-serializer@0.1.6 ### Patch Changes - Add CSS source map support and source-mapped template encode diagnostics. ([#2483](#2483)) ## @lynx-js/web-core@0.20.3 ### Patch Changes - fix: `__AddClass` triggers style updates when `enableCSSSelector` is `false` ([#2515](#2515)) `__AddClass` was missing the expected call to `update_css_og_style` when CSS selectors are disabled (`enableCSSSelector: false`). With this fix, dynamically adding a class correctly delegates style population from the template AST into the DOM, mirroring the behavior of `__SetClasses`. Added behavioral unit test and end-to-end playwright validations using dynamically generated JSON AST `styleInfo` mocks. - fix(web-core): skip setting lynxEntryNameAttribute for **Card** and use constants for server element APIs ([#2510](#2510)) - Fix componentCSSID behavior for SSR and main thread by calculating element css_id from parent component correctly. ([#2495](#2495)) - fix: avoid panic in dispatch_event_by_path when element data cannot be retrieved ([#2508](#2508)) - fix: filter out -1 uniqueId in commonEventHandler ([#2493](#2493)) - feat: add x-markdown support ([#2412](#2412)) Add opt-in support for the `x-markdown` element on Lynx Web, including Markdown rendering together with its related styling, interaction, animation, truncation, range rendering, and effect capabilities exposed through the component API. Update the `web-core`, `web-core-wasm`, and `web-mainthread-apis` runtime paths to use the shared property-or-attribute setter from `web-constants`, so custom elements such as `x-markdown` can receive structured property values correctly instead of being forced through string-only attribute updates. ```javascript import "@lynx-js/web-elements/XMarkdown"; ``` - fix: transformVH not work with cqw unit as the base length ([#2469](#2469)) - fix: add cardType resolution for legacy json lynx bundle ([#2510](#2510)) - fix: the default value of rpx is supposed to be 1/750 cqw ([#2469](#2469)) - Updated dependencies \[[`e179680`](e179680), [`647334c`](647334c), [`fb7bc84`](fb7bc84), [`9454dc4`](9454dc4), [`bdec498`](bdec498), [`b0247f9`](b0247f9), [`eec539a`](eec539a)]: - @lynx-js/css-serializer@0.1.6 - @lynx-js/web-elements@0.12.1 - @lynx-js/web-worker-rpc@0.20.3 ## @lynx-js/web-elements@0.12.1 ### Patch Changes - fix: XMarkdown slot created should not have prefix ([#2520](#2520)) - feat: add x-markdown support ([#2412](#2412)) Add opt-in support for the `x-markdown` element on Lynx Web, including Markdown rendering together with its related styling, interaction, animation, truncation, range rendering, and effect capabilities exposed through the component API. Update the `web-core`, `web-core-wasm`, and `web-mainthread-apis` runtime paths to use the shared property-or-attribute setter from `web-constants`, so custom elements such as `x-markdown` can receive structured property values correctly instead of being forced through string-only attribute updates. ```javascript import "@lynx-js/web-elements/XMarkdown"; ``` - fix: x-markdown inline view injection no longer queries light DOM children when the content attribute changes. Consumers must now pre-set `slot="{id}"` on the child element they want to project into `inlineview://{id}`. ([#2516](#2516)) - fix: list cannot drag-scroll inside x-foldview-slot-ng ([#2507](#2507)) Cause: `touchstart` used `elementsFromPoint(pageX, pageY)` (expects `clientX/clientY`), so hit-testing can miss the real inner scroller (e.g. `x-list` shadow `#content`) when the document is scrolled. Fix: use `elementsFromPoint(clientX, clientY)` + `event.composedPath()` for Shadow DOM, and keep `previousPageX` updated during `touchmove`. - fix: line-height of markdown-style should be added `px` ([#2509](#2509)) - fix: list `bindscrolltolower` may not trigger because the lower threshold ([#2484](#2484)) sentinel had no effective size or offset, causing the bottom `IntersectionObserver` to miss the list boundary ## @lynx-js/web-explorer@0.0.17 ### Patch Changes - bump rsdoctor to 1.5.6 ([#2410](#2410)) ## @lynx-js/css-extract-webpack-plugin@0.7.1 ### Patch Changes - Fix CSS source map line offsets when wrapping extracted CSS with cssId metadata. ([#2514](#2514)) - Support `@lynx-js/template-webpack-plugin` v0.11.0. ([#2483](#2483)) ## @lynx-js/react-webpack-plugin@0.9.2 ### Patch Changes - Support `@lynx-js/template-webpack-plugin` v0.11.0. ([#2483](#2483)) ## create-rspeedy@0.14.3 ## @lynx-js/react-alias-rsbuild-plugin@0.16.1 ## upgrade-rspeedy@0.14.3 ## @lynx-js/web-rsbuild-server-middleware@0.20.3 ## @lynx-js/web-worker-rpc@0.20.3 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…and add regression tests
Summary by CodeRabbit
Checklist