diff --git a/.changeset/upset-bats-bathe.md b/.changeset/upset-bats-bathe.md new file mode 100644 index 0000000000..756bf71cf3 --- /dev/null +++ b/.changeset/upset-bats-bathe.md @@ -0,0 +1,7 @@ +--- +"@lynx-js/web-worker-runtime": patch +"@lynx-js/web-constants": patch +"@lynx-js/web-core": patch +--- + +feat: support lynx.reload() diff --git a/packages/web-platform/web-constants/src/endpoints.ts b/packages/web-platform/web-constants/src/endpoints.ts index 816f299f6f..313e53c66c 100644 --- a/packages/web-platform/web-constants/src/endpoints.ts +++ b/packages/web-platform/web-constants/src/endpoints.ts @@ -260,3 +260,8 @@ export const loadTemplateMultiThread = createRpcEndpoint< [string], LynxTemplate >('loadTemplateMultiThread', false, true); + +export const reloadEndpoint = createRpcEndpoint< + [], + void +>('reload', false, false); diff --git a/packages/web-platform/web-core-wasm-e2e/tests/reactlynx.spec.ts b/packages/web-platform/web-core-wasm-e2e/tests/reactlynx.spec.ts index 29c5987412..8d05f69dd9 100644 --- a/packages/web-platform/web-core-wasm-e2e/tests/reactlynx.spec.ts +++ b/packages/web-platform/web-core-wasm-e2e/tests/reactlynx.spec.ts @@ -239,6 +239,19 @@ test.describe('reactlynx3 tests', () => { page.locator('#wheat'), ).toHaveAttribute('style', /wheat/g); }); + + test('basic-lynx-reload', async ({ page }, { title }) => { + await goto(page, title); + await wait(100); + const target = page.locator('#target'); + await target.click(); + await wait(100); + await expect(await target.getAttribute('style')).toContain('green'); + await page.locator('#reload').click(); + await wait(100); + await expect(await target.getAttribute('style')).toContain('pink'); + }); + test( 'basic-wrapper-element-do-not-impact-layout', async ({ page }, { title }) => { diff --git a/packages/web-platform/web-core-wasm-e2e/tests/reactlynx/basic-lynx-reload/index.jsx b/packages/web-platform/web-core-wasm-e2e/tests/reactlynx/basic-lynx-reload/index.jsx new file mode 100644 index 0000000000..3235fd99ac --- /dev/null +++ b/packages/web-platform/web-core-wasm-e2e/tests/reactlynx/basic-lynx-reload/index.jsx @@ -0,0 +1,32 @@ +// Copyright 2023 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. +import { useState, root } from '@lynx-js/react'; +function App() { + const [color, setColor] = useState('pink'); + return ( + <> + setColor(color === 'pink' ? 'green' : 'pink')} + style={{ + height: '100px', + width: '100px', + background: color, + }} + /> + { + lynx.reload(); + }} + style={{ + height: '100px', + width: '100px', + background: color, + }} + /> + + ); +} +root.render(); diff --git a/packages/web-platform/web-core-wasm/ts/client/background/background-apis/createBackgroundLynx.ts b/packages/web-platform/web-core-wasm/ts/client/background/background-apis/createBackgroundLynx.ts index a4c20f9515..db503a3d44 100644 --- a/packages/web-platform/web-core-wasm/ts/client/background/background-apis/createBackgroundLynx.ts +++ b/packages/web-platform/web-core-wasm/ts/client/background/background-apis/createBackgroundLynx.ts @@ -5,6 +5,7 @@ import { dispatchCoreContextOnBackgroundEndpoint, dispatchJSContextOnMainThreadEndpoint, + reloadEndpoint, } from '../../endpoints.js'; import type { Rpc } from '@lynx-js/web-worker-rpc'; import { createGetCustomSection } from './crossThreadHandlers/createGetCustomSection.js'; @@ -56,5 +57,8 @@ export function createBackgroundLynx( }, ) => void, ) => nativeApp.queryComponent(source, callback), + reload: () => { + mainThreadRpc.invoke(reloadEndpoint, []); + }, }; } diff --git a/packages/web-platform/web-core-wasm/ts/client/endpoints.ts b/packages/web-platform/web-core-wasm/ts/client/endpoints.ts index e73d23a986..200b28bc34 100644 --- a/packages/web-platform/web-core-wasm/ts/client/endpoints.ts +++ b/packages/web-platform/web-core-wasm/ts/client/endpoints.ts @@ -212,3 +212,8 @@ export const updateBTSChunkEndpoint = createRpcEndpoint< [/** url */ string, Record], void >('updateBTSChunkEndpoint', false, true); + +export const reloadEndpoint = createRpcEndpoint< + [], + void +>('reload', false, false); diff --git a/packages/web-platform/web-core-wasm/ts/client/mainthread/Background.ts b/packages/web-platform/web-core-wasm/ts/client/mainthread/Background.ts index d9d522e397..a30da8c9b1 100644 --- a/packages/web-platform/web-core-wasm/ts/client/mainthread/Background.ts +++ b/packages/web-platform/web-core-wasm/ts/client/mainthread/Background.ts @@ -42,6 +42,7 @@ import { registerTriggerComponentEventHandler } from './crossThreadHandlers/regi import { registerTriggerElementMethodEndpointHandler } from './crossThreadHandlers/registerTriggerElementMethodEndpointHandler.js'; import { registerNapiModulesCallHandler } from './crossThreadHandlers/registerNapiModulesCallHandler.js'; import { registerNativeModulesCallHandler } from './crossThreadHandlers/registerNativeModulesCallHandler.js'; +import { registerReloadHandler } from './crossThreadHandlers/registerReloadHandler.js'; function createWebWorker(): Worker { return new Worker( @@ -236,6 +237,7 @@ export class BackgroundThread implements AsyncDisposable { }); }, ); + registerReloadHandler(this.#rpc, this.#lynxViewInstance); registerGetPathInfoHandler(this.#rpc, this.#lynxViewInstance); registerInvokeUIMethodHandler(this.#rpc, this.#lynxViewInstance); registerNapiModulesCallHandler(this.#rpc, this.#lynxViewInstance); diff --git a/packages/web-platform/web-core-wasm/ts/client/mainthread/crossThreadHandlers/registerReloadHandler.ts b/packages/web-platform/web-core-wasm/ts/client/mainthread/crossThreadHandlers/registerReloadHandler.ts new file mode 100644 index 0000000000..035d3eab52 --- /dev/null +++ b/packages/web-platform/web-core-wasm/ts/client/mainthread/crossThreadHandlers/registerReloadHandler.ts @@ -0,0 +1,19 @@ +// Copyright 2026 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +import type { Rpc } from '@lynx-js/web-worker-rpc'; +import { reloadEndpoint } from '../../endpoints.js'; +import type { LynxViewInstance } from '../LynxViewInstance.js'; + +export function registerReloadHandler( + rpc: Rpc, + lynxViewInstance: LynxViewInstance, +) { + rpc.registerHandler( + reloadEndpoint, + () => { + lynxViewInstance.parentDom.reload(); + }, + ); +} diff --git a/packages/web-platform/web-core/src/apis/LynxView.ts b/packages/web-platform/web-core/src/apis/LynxView.ts index 90a2111fa9..bbd6e9da28 100644 --- a/packages/web-platform/web-core/src/apis/LynxView.ts +++ b/packages/web-platform/web-core/src/apis/LynxView.ts @@ -480,6 +480,9 @@ export class LynxView extends HTMLElement { ); }, customTemplateLoader: this.customTemplateLoader, + reload: () => { + this.reload(); + }, }, ssr: ssrData ? JSON.parse(decodeURI(ssrData)) as SSRDumpInfo diff --git a/packages/web-platform/web-core/src/uiThread/crossThreadHandlers/registerReloadHandler.ts b/packages/web-platform/web-core/src/uiThread/crossThreadHandlers/registerReloadHandler.ts new file mode 100644 index 0000000000..0f1d9e3bfa --- /dev/null +++ b/packages/web-platform/web-core/src/uiThread/crossThreadHandlers/registerReloadHandler.ts @@ -0,0 +1,16 @@ +// Copyright 2026 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +import type { Rpc } from '@lynx-js/web-worker-rpc'; +import { reloadEndpoint } from '@lynx-js/web-constants'; + +export function registerReloadHandler( + rpc: Rpc, + reloadHandler: () => void, +) { + rpc.registerHandler( + reloadEndpoint, + reloadHandler, + ); +} diff --git a/packages/web-platform/web-core/src/uiThread/startBackground.ts b/packages/web-platform/web-core/src/uiThread/startBackground.ts index 3af4655168..6d4bca3327 100644 --- a/packages/web-platform/web-core/src/uiThread/startBackground.ts +++ b/packages/web-platform/web-core/src/uiThread/startBackground.ts @@ -19,6 +19,7 @@ import { registerTriggerElementMethodEndpointHandler } from './crossThreadHandle import type { StartUIThreadCallbacks } from './startUIThread.js'; import { registerReportErrorHandler } from './crossThreadHandlers/registerReportErrorHandler.js'; import { registerGetPathInfoHandler } from './crossThreadHandlers/registerGetPathInfoHandler.js'; +import { registerReloadHandler } from './crossThreadHandlers/registerReloadHandler.js'; export function startBackground( backgroundRpc: Rpc, @@ -60,6 +61,10 @@ export function startBackground( 'app-service.js', callbacks.onError, ); + registerReloadHandler( + backgroundRpc, + callbacks.reload, + ); const sendGlobalEvent = backgroundRpc.createCall(sendGlobalEventEndpoint); const markTiming = backgroundRpc.createCall(markTimingEndpoint); diff --git a/packages/web-platform/web-core/src/uiThread/startUIThread.ts b/packages/web-platform/web-core/src/uiThread/startUIThread.ts index 3b343a4443..e7df545ed7 100644 --- a/packages/web-platform/web-core/src/uiThread/startUIThread.ts +++ b/packages/web-platform/web-core/src/uiThread/startUIThread.ts @@ -28,6 +28,7 @@ export type StartUIThreadCallbacks = { napiModulesCall: NapiModulesCall; onError?: (err: Error, release: string, fileName: string) => void; customTemplateLoader?: TemplateLoader; + reload: () => void; }; export function startUIThread( diff --git a/packages/web-platform/web-tests/tests/react.spec.ts b/packages/web-platform/web-tests/tests/react.spec.ts index 9a5536f2a0..bd391966f4 100644 --- a/packages/web-platform/web-tests/tests/react.spec.ts +++ b/packages/web-platform/web-tests/tests/react.spec.ts @@ -250,6 +250,18 @@ test.describe('reactlynx3 tests', () => { page.locator('#wheat'), ).toHaveAttribute('style', /wheat/g); }); + + test('basic-lynx-reload', async ({ page }, { title }) => { + await goto(page, title); + await wait(100); + const target = page.locator('#target'); + await target.click(); + await wait(100); + await expect(await target.getAttribute('style')).toContain('green'); + await page.locator('#reload').click(); + await wait(100); + await expect(await target.getAttribute('style')).toContain('pink'); + }); test( 'basic-wrapper-element-do-not-impact-layout', async ({ page }, { title }) => { diff --git a/packages/web-platform/web-tests/tests/react/basic-lynx-reload/index.jsx b/packages/web-platform/web-tests/tests/react/basic-lynx-reload/index.jsx new file mode 100644 index 0000000000..3235fd99ac --- /dev/null +++ b/packages/web-platform/web-tests/tests/react/basic-lynx-reload/index.jsx @@ -0,0 +1,32 @@ +// Copyright 2023 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. +import { useState, root } from '@lynx-js/react'; +function App() { + const [color, setColor] = useState('pink'); + return ( + <> + setColor(color === 'pink' ? 'green' : 'pink')} + style={{ + height: '100px', + width: '100px', + background: color, + }} + /> + { + lynx.reload(); + }} + style={{ + height: '100px', + width: '100px', + background: color, + }} + /> + + ); +} +root.render(); diff --git a/packages/web-platform/web-worker-runtime/src/backgroundThread/background-apis/createBackgroundLynx.ts b/packages/web-platform/web-worker-runtime/src/backgroundThread/background-apis/createBackgroundLynx.ts index c574734e8c..ecb300e161 100644 --- a/packages/web-platform/web-worker-runtime/src/backgroundThread/background-apis/createBackgroundLynx.ts +++ b/packages/web-platform/web-worker-runtime/src/backgroundThread/background-apis/createBackgroundLynx.ts @@ -6,6 +6,7 @@ import { dispatchCoreContextOnBackgroundEndpoint, dispatchJSContextOnMainThreadEndpoint, LynxCrossThreadContext, + reloadEndpoint, type BackMainThreadContextConfig, type NativeApp, } from '@lynx-js/web-constants'; @@ -57,5 +58,8 @@ export function createBackgroundLynx( }, ) => void, ) => nativeApp.queryComponent(source, callback), + reload: () => { + uiThreadRpc.invoke(reloadEndpoint, []); + }, }; }