Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
10 changes: 10 additions & 0 deletions .changeset/loose-crews-slide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@lynx-js/web-core": patch
"@lynx-js/web-mainthread-apis": patch
---

fix: mts freeze after reload()

The mts may be freezed after reload() called.

We fixed it by waiting until the all-on-ui Javascript realm implementation, an iframe, to be fully loaded.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
type StartMainThreadContextConfig,
type RpcCallType,
updateDataEndpoint,
type MainThreadGlobalThis,
type I18nResourceTranslationOptions,
type CloneableObject,
i18nResourceMissedEventName,
Expand Down Expand Up @@ -38,27 +37,30 @@ const {
* Creates a isolated JavaScript context for executing mts code.
* This context has its own global variables and functions.
*/
function createIFrameRealm(parent: Node): JSRealm {
async function createIFrameRealm(parent: Node): Promise<JSRealm> {
const iframe = document.createElement('iframe');
const iframeReadyPromise = new Promise<void>((resolve) => {
const listener = (event: MessageEvent) => {
if ((event as MessageEvent).data === 'lynx:mtsready') {
resolve();
globalThis.removeEventListener('message', listener);
}
};
globalThis.addEventListener('message', listener);
});
iframe.style.display = 'none';
iframe.srcdoc =
'<!DOCTYPE html><html><head></head><body style="display:none"></body></html>';
'<!DOCTYPE html><html><head><script>parent.postMessage("lynx:mtsready")</script></head><body style="display:none"></body></html>';
iframe.sandbox = 'allow-same-origin allow-scripts'; // Restrict capabilities for security
iframe.loading = 'eager';
parent.appendChild(iframe);
await iframeReadyPromise;
const iframeWindow = iframe.contentWindow! as unknown as typeof globalThis;
Comment thread
PupilTong marked this conversation as resolved.
const loadScript: (url: string) => Promise<unknown> = async (url) => {
const script = iframe.contentDocument!.createElement('script');
script.fetchPriority = 'high';
script.defer = true;
script.async = false;
if (!iframe.contentDocument!.head) {
await new Promise<void>((resolve) => {
iframe.onload = () => resolve();
// In case iframe is already loaded, wait a macro task
setTimeout(() => resolve(), 0);
});
}
iframe.contentDocument!.head.appendChild(script);
return new Promise(async (resolve, reject) => {
script.onload = () => {
Expand Down Expand Up @@ -123,9 +125,6 @@ export function createRenderAllOnUI(
const i18nResources = new I18nResources();
const { exposureChangedCallback } = createExposureMonitor(shadowRoot);
const mtsRealm = createIFrameRealm(shadowRoot);
const mtsGlobalThis = mtsRealm.globalWindow as
& typeof globalThis
& MainThreadGlobalThis;
const { startMainThread, handleUpdatedData } = prepareMainThreadAPIs(
mainToBackgroundRpc,
shadowRoot,
Expand All @@ -144,9 +143,6 @@ export function createRenderAllOnUI(
},
loadTemplate,
);
const pendingUpdateCalls: Parameters<
RpcCallType<typeof updateDataEndpoint>
>[] = [];

const start = async (configs: StartMainThreadContextConfig) => {
if (ssrDumpInfo) {
Expand Down Expand Up @@ -192,26 +188,15 @@ export function createRenderAllOnUI(
} else {
await startMainThread(configs);
}

// Process any pending update calls that were queued while mtsGlobalThis was undefined
for (const [newData, options] of pendingUpdateCalls) {
handleUpdatedData(newData, options);
}
pendingUpdateCalls.length = 0;
};
const updateDataMainThread: RpcCallType<typeof updateDataEndpoint> = async (
newData,
options,
) => {
if (mtsGlobalThis) {
return handleUpdatedData(
newData,
options,
);
} else {
// Cache the call if mtsGlobalThis is not yet initialized
pendingUpdateCalls.push([newData, options]);
}
return handleUpdatedData(
newData,
options,
);
};
const updateI18nResourcesMainThread = (data: Cloneable) => {
i18nResources.setData(data as InitI18nResources);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export function prepareMainThreadAPIs(
backgroundThreadRpc: Rpc,
rootDom: Document | ShadowRoot,
document: Document,
mtsRealm: JSRealm,
mtsRealmPromise: JSRealm | Promise<JSRealm>,
commitDocument: (
exposureChangedElements: HTMLElement[],
) => Promise<void> | void,
Expand Down Expand Up @@ -97,6 +97,7 @@ export function prepareMainThreadAPIs(
customSections,
cardType,
} = template;
const mtsRealm = await mtsRealmPromise;
markTimingInternal('decode_start');
await initWasmPromise;
const jsContext = new LynxCrossThreadContext({
Expand Down Expand Up @@ -254,10 +255,11 @@ export function prepareMainThreadAPIs(
await mtsRealm.loadScript(template.lepusCode.root);
jsContext.__start(); // start the jsContext after the runtime is created
}
function handleUpdatedData(
async function handleUpdatedData(
newData: Cloneable,
options: UpdateDataOptions | undefined,
) {
const mtsRealm = await mtsRealmPromise;
const runtime = mtsRealm.globalWindow as
& typeof globalThis
& MainThreadGlobalThis;
Expand Down
9 changes: 3 additions & 6 deletions packages/web-platform/web-tests/tests/react.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,7 @@ test.describe('reactlynx3 tests', () => {
await expect(target).toHaveCSS('background-color', 'rgb(255, 192, 203)');
});

test('basic-reload', async ({ page, browserName }, { title }) => {
test.skip(browserName === 'webkit', 'playwright issue, tested locally');
test('basic-reload', async ({ page }, { title }) => {
await goto(page, title);
await wait(100);
const target = page.locator('#target');
Expand All @@ -91,8 +90,7 @@ test.describe('reactlynx3 tests', () => {
await wait(100);
await expect(await target.getAttribute('style')).toContain('pink');
});
test('basic-reload-page-only-one', async ({ page, browserName }) => {
test.skip(browserName === 'webkit', 'playwright issue, tested locally');
test('basic-reload-page-only-one', async ({ page }) => {
await goto(page, 'basic-reload');
await wait(100);
await page.evaluate(() => {
Expand Down Expand Up @@ -175,8 +173,7 @@ test.describe('reactlynx3 tests', () => {
'green',
);
});
test('basic-globalProps-reload', async ({ page, browserName }, {}) => {
test.skip(browserName === 'webkit', 'playwright issue, tested locally');
test('basic-globalProps-reload', async ({ page }, {}) => {
await goto(page, 'basic-globalProps');
await wait(100);
expect(await page.locator('#target').getAttribute('style')).toContain(
Expand Down