diff --git a/.changeset/add-event-bubble-support.md b/.changeset/add-event-bubble-support.md new file mode 100644 index 0000000000..0e9085a471 --- /dev/null +++ b/.changeset/add-event-bubble-support.md @@ -0,0 +1,5 @@ +--- +"@lynx-js/web-core": patch +--- + +feat(web-core): add `is_bubble` parameter to `common_event_handler` to properly handle non-bubbling events like `window.Event('click', { bubbles: false })`. diff --git a/.changeset/fix-query-component-processevalresult.md b/.changeset/fix-query-component-processevalresult.md new file mode 100644 index 0000000000..ab5bbbd0b4 --- /dev/null +++ b/.changeset/fix-query-component-processevalresult.md @@ -0,0 +1,5 @@ +--- +"@lynx-js/web-core": patch +--- + +fix(web-core): fallback to the original export chunk when `processEvalResult` is absent during `queryComponent` execution diff --git a/packages/web-platform/web-core-e2e/resources/mock-component.json b/packages/web-platform/web-core-e2e/resources/mock-component.json new file mode 100644 index 0000000000..00bea5ffad --- /dev/null +++ b/packages/web-platform/web-core-e2e/resources/mock-component.json @@ -0,0 +1,9 @@ +{ + "appType": "lazy", + "lepusCode": { + "root": "({ mockResult: 'MOCKED_EVAL_RESULT' })" + }, + "pageConfig": { + "enableCSSSelector": true + } +} diff --git a/packages/web-platform/web-core-e2e/tests/web-core.test.ts b/packages/web-platform/web-core-e2e/tests/web-core.test.ts index bc24f6046a..a7ccc40a66 100644 --- a/packages/web-platform/web-core-e2e/tests/web-core.test.ts +++ b/packages/web-platform/web-core-e2e/tests/web-core.test.ts @@ -172,6 +172,74 @@ test.describe('web core tests', () => { expect(fail).toBe(false); }); + test('__QueryComponent without processEvalResult', async ({ page, browserName }) => { + // firefox dose not support this. + test.skip(browserName === 'firefox'); + await goto(page); + + await page.evaluate(() => { + delete (globalThis as any).runtime.processEvalResult; + }); + + const evalResult = await page.evaluate(async () => { + return new Promise((resolve) => { + globalThis.addEventListener('unhandledrejection', (e) => { + resolve({ error: e.reason?.message ?? String(e.reason) }); + }); + globalThis.runtime.__QueryComponent( + '/resources/mock-component.json', + (res: any) => { + resolve(res); + }, + ); + }); + }); + + expect(evalResult).toMatchObject({ + code: 0, + }); + expect((evalResult as any).data.url).toBe('/resources/mock-component.json'); + expect((evalResult as any).data.evalResult).toEqual({ + mockResult: 'MOCKED_EVAL_RESULT', + }); + }); + + test('__QueryComponent with processEvalResult', async ({ page, browserName }) => { + // firefox dose not support this. + test.skip(browserName === 'firefox'); + await goto(page); + + await page.evaluate(() => { + (globalThis as any).runtime.processEvalResult = ( + exports: any, + url: string, + ) => { + return 'OVERRIDDEN_MOCKED_EVAL_RESULT'; + }; + }); + + const evalResult = await page.evaluate(async () => { + return new Promise((resolve) => { + globalThis.addEventListener('unhandledrejection', (e) => { + resolve({ error: e.reason?.message ?? String(e.reason) }); + }); + globalThis.runtime.__QueryComponent( + '/resources/mock-component.json', + (res: any) => { + resolve(res); + }, + ); + }); + }); + + expect(evalResult).toMatchObject({ + code: 0, + }); + expect((evalResult as any).data.evalResult).toBe( + 'OVERRIDDEN_MOCKED_EVAL_RESULT', + ); + }); + test('api-enableJSDataProcessor disables processData', async ({ page, browserName }) => { test.skip(browserName === 'firefox'); await goto(page, 'web-core.enable-js-data-processor.json'); diff --git a/packages/web-platform/web-core/binary/client/client.d.ts b/packages/web-platform/web-core/binary/client/client.d.ts index 45ff3e8e57..525ea62e99 100644 --- a/packages/web-platform/web-core/binary/client/client.d.ts +++ b/packages/web-platform/web-core/binary/client/client.d.ts @@ -21,7 +21,7 @@ export class MainThreadWasmContext { add_cross_thread_event(unique_id: number, event_type: string, event_name: string, event_handler_identifier?: string | null): void; add_dataset(unique_id: number, key: any, value: any): void; add_run_worklet_event(unique_id: number, event_type: string, event_name: string, event_handler_identifier?: any | null): void; - common_event_handler(event: any, bubble_unique_id_path: Uint32Array, event_name: string): void; + common_event_handler(event: any, bubble_unique_id_path: Uint32Array, event_name: string, is_bubble: boolean): void; create_element_common(parent_component_unique_id: number, dom: HTMLElement, css_id?: number | null, component_id?: string | null): number; dispatch_event_by_path(bubble_unique_id_path: Uint32Array, event_name: string, is_capture: boolean, serialized_event: any): boolean; get_component_id(unique_id: number): string | undefined; @@ -199,7 +199,7 @@ export interface InitOutput { readonly mainthreadwasmcontext_add_cross_thread_event: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => void; readonly mainthreadwasmcontext_add_dataset: (a: number, b: number, c: any, d: any) => [number, number]; readonly mainthreadwasmcontext_add_run_worklet_event: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void; - readonly mainthreadwasmcontext_common_event_handler: (a: number, b: any, c: number, d: number, e: number, f: number) => void; + readonly mainthreadwasmcontext_common_event_handler: (a: number, b: any, c: number, d: number, e: number, f: number, g: number) => void; readonly mainthreadwasmcontext_create_element_common: (a: number, b: number, c: any, d: number, e: number, f: number) => number; readonly mainthreadwasmcontext_dispatch_event_by_path: (a: number, b: number, c: number, d: number, e: number, f: number, g: any) => number; readonly mainthreadwasmcontext_get_component_id: (a: number, b: number) => [number, number, number, number]; diff --git a/packages/web-platform/web-core/binary/client/client_bg.wasm.d.ts b/packages/web-platform/web-core/binary/client/client_bg.wasm.d.ts index b4a232f8d5..28ee68a718 100644 --- a/packages/web-platform/web-core/binary/client/client_bg.wasm.d.ts +++ b/packages/web-platform/web-core/binary/client/client_bg.wasm.d.ts @@ -22,7 +22,7 @@ export const get_style_content: (a: any) => [number, number, number, number]; export const mainthreadwasmcontext_add_cross_thread_event: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => void; export const mainthreadwasmcontext_add_dataset: (a: number, b: number, c: any, d: any) => [number, number]; export const mainthreadwasmcontext_add_run_worklet_event: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void; -export const mainthreadwasmcontext_common_event_handler: (a: number, b: any, c: number, d: number, e: number, f: number) => void; +export const mainthreadwasmcontext_common_event_handler: (a: number, b: any, c: number, d: number, e: number, f: number, g: number) => void; export const mainthreadwasmcontext_create_element_common: (a: number, b: number, c: any, d: number, e: number, f: number) => number; export const mainthreadwasmcontext_dispatch_event_by_path: (a: number, b: number, c: number, d: number, e: number, f: number, g: any) => number; export const mainthreadwasmcontext_get_component_id: (a: number, b: number) => [number, number, number, number]; diff --git a/packages/web-platform/web-core/binary/client_legacy/client.d.ts b/packages/web-platform/web-core/binary/client_legacy/client.d.ts index 74b4e3e92a..cb4310ddf3 100644 --- a/packages/web-platform/web-core/binary/client_legacy/client.d.ts +++ b/packages/web-platform/web-core/binary/client_legacy/client.d.ts @@ -21,7 +21,7 @@ export class MainThreadWasmContext { add_cross_thread_event(unique_id: number, event_type: string, event_name: string, event_handler_identifier?: string | null): void; add_dataset(unique_id: number, key: any, value: any): void; add_run_worklet_event(unique_id: number, event_type: string, event_name: string, event_handler_identifier?: any | null): void; - common_event_handler(event: any, bubble_unique_id_path: Uint32Array, event_name: string): void; + common_event_handler(event: any, bubble_unique_id_path: Uint32Array, event_name: string, is_bubble: boolean): void; create_element_common(parent_component_unique_id: number, dom: HTMLElement, css_id?: number | null, component_id?: string | null): number; dispatch_event_by_path(bubble_unique_id_path: Uint32Array, event_name: string, is_capture: boolean, serialized_event: any): boolean; get_component_id(unique_id: number): string | undefined; @@ -199,7 +199,7 @@ export interface InitOutput { readonly mainthreadwasmcontext_add_cross_thread_event: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => void; readonly mainthreadwasmcontext_add_dataset: (a: number, b: number, c: number, d: number, e: number) => void; readonly mainthreadwasmcontext_add_run_worklet_event: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void; - readonly mainthreadwasmcontext_common_event_handler: (a: number, b: number, c: number, d: number, e: number, f: number) => void; + readonly mainthreadwasmcontext_common_event_handler: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void; readonly mainthreadwasmcontext_create_element_common: (a: number, b: number, c: number, d: number, e: number, f: number) => number; readonly mainthreadwasmcontext_dispatch_event_by_path: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => number; readonly mainthreadwasmcontext_get_component_id: (a: number, b: number, c: number) => void; diff --git a/packages/web-platform/web-core/binary/client_legacy/client_bg.wasm.d.ts b/packages/web-platform/web-core/binary/client_legacy/client_bg.wasm.d.ts index acca35352c..75ae3b0b4a 100644 --- a/packages/web-platform/web-core/binary/client_legacy/client_bg.wasm.d.ts +++ b/packages/web-platform/web-core/binary/client_legacy/client_bg.wasm.d.ts @@ -22,7 +22,7 @@ export const get_style_content: (a: number, b: number) => void; export const mainthreadwasmcontext_add_cross_thread_event: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => void; export const mainthreadwasmcontext_add_dataset: (a: number, b: number, c: number, d: number, e: number) => void; export const mainthreadwasmcontext_add_run_worklet_event: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void; -export const mainthreadwasmcontext_common_event_handler: (a: number, b: number, c: number, d: number, e: number, f: number) => void; +export const mainthreadwasmcontext_common_event_handler: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void; export const mainthreadwasmcontext_create_element_common: (a: number, b: number, c: number, d: number, e: number, f: number) => number; export const mainthreadwasmcontext_dispatch_event_by_path: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => number; export const mainthreadwasmcontext_get_component_id: (a: number, b: number, c: number) => void; diff --git a/packages/web-platform/web-core/src/main_thread/client/element_apis/event_apis.rs b/packages/web-platform/web-core/src/main_thread/client/element_apis/event_apis.rs index 3064fabd9b..0630ffe44e 100644 --- a/packages/web-platform/web-core/src/main_thread/client/element_apis/event_apis.rs +++ b/packages/web-platform/web-core/src/main_thread/client/element_apis/event_apis.rs @@ -279,10 +279,15 @@ impl MainThreadWasmContext { event: JsValue, bubble_unique_id_path: Vec, event_name: &str, + is_bubble: bool, ) { let caught = self.dispatch_event_by_path(&bubble_unique_id_path, event_name, true, &event); if !caught { - self.dispatch_event_by_path(&bubble_unique_id_path, event_name, false, &event); + if is_bubble { + self.dispatch_event_by_path(&bubble_unique_id_path, event_name, false, &event); + } else if let Some(target_id) = bubble_unique_id_path.first() { + self.dispatch_event_by_path(&[*target_id], event_name, false, &event); + } } } } diff --git a/packages/web-platform/web-core/tests/element-apis.spec.ts b/packages/web-platform/web-core/tests/element-apis.spec.ts index c9c2cfb4f1..844204d601 100644 --- a/packages/web-platform/web-core/tests/element-apis.spec.ts +++ b/packages/web-platform/web-core/tests/element-apis.spec.ts @@ -1264,6 +1264,44 @@ describe('Element APIs', () => { ); }); + test('event with bubbles: false should not bubble to parent', () => { + vi.spyOn(mtsBinding, 'addEventListener'); + vi.spyOn(mtsBinding, 'publishEvent'); + let page = mtsGlobalThis.__CreatePage('0', 0); + let parent = mtsGlobalThis.__CreateComponent( + 0, + 'id1', + 0, + 'test_entry', + 'name', + 'path', + {}, + {}, + ); + let parentUid = mtsGlobalThis.__GetElementUniqueID(parent); + let child = mtsGlobalThis.__CreateView(parentUid); + mtsGlobalThis.__AppendElement(page, parent); + mtsGlobalThis.__AppendElement(parent, child); + mtsGlobalThis.__SetID(parent, 'parent_id'); + mtsGlobalThis.__SetID(child, 'child_id'); + mtsGlobalThis.__AddEvent(parent, 'bindEvent', 'tap', 'parent_hname'); + mtsGlobalThis.__AddEvent(child, 'bindEvent', 'tap', 'child_hname'); + mtsGlobalThis.__FlushElementTree(); + const event = new window.Event('click', { bubbles: false }); + rootDom.querySelector('#child_id')?.dispatchEvent(event); + expect(mtsBinding.addEventListener).toBeCalledWith('tap'); + expect(mtsBinding.publishEvent).toBeCalledTimes(1); + expect(mtsBinding.publishEvent).toBeCalledWith( + 'child_hname', + 'id1', + expect.any(Object), + expect.any(Number), + undefined, + expect.any(Number), + undefined, + ); + }); + test('__UpdateComponentInfo', () => { let ele = mtsGlobalThis.__CreateComponent( 0, diff --git a/packages/web-platform/web-core/ts/client/mainthread/LynxViewInstance.ts b/packages/web-platform/web-core/ts/client/mainthread/LynxViewInstance.ts index 18eb56975c..fe18f9b244 100644 --- a/packages/web-platform/web-core/ts/client/mainthread/LynxViewInstance.ts +++ b/packages/web-platform/web-core/ts/client/mainthread/LynxViewInstance.ts @@ -249,7 +249,7 @@ export class LynxViewInstance implements AsyncDisposable { lepusRootChunkExport = this.mainThreadGlobalThis.processEvalResult?.( lepusRootChunkExport, url, - ); + ) ?? lepusRootChunkExport; return lepusRootChunkExport; }); this.#queryComponentCache.set(url, promise); diff --git a/packages/web-platform/web-core/ts/client/mainthread/elementAPIs/WASMJSBinding.ts b/packages/web-platform/web-core/ts/client/mainthread/elementAPIs/WASMJSBinding.ts index b246882e55..539b67d46e 100644 --- a/packages/web-platform/web-core/ts/client/mainthread/elementAPIs/WASMJSBinding.ts +++ b/packages/web-platform/web-core/ts/client/mainthread/elementAPIs/WASMJSBinding.ts @@ -174,6 +174,7 @@ export class WASMJSBinding implements RustMainthreadContextBinding { eventObject, bubblePath.slice(0, bubblePathLength), eventObject.type, + event.bubbles, ); };