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

feat: support \_\_MarkTemplateElement, \_\_MarkPartElement and \_\_GetTemplateParts for all-on-ui
4 changes: 4 additions & 0 deletions packages/web-platform/web-constants/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ export const lynxComponentConfigAttribute = 'l-comp-cfg' as const;

export const lynxDisposedAttribute = 'l-disposed' as const;

export const lynxElementTemplateMarkerAttribute = 'l-template' as const;

export const lynxPartIdAttribute = 'l-part' as const;

export const lynxDefaultDisplayLinearAttribute =
'lynx-default-display-linear' as const;

Expand Down
1 change: 1 addition & 0 deletions packages/web-platform/web-constants/src/types/Element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export interface ElementAnimationOptions {
}

export interface WebFiberElementImpl {
querySelectorAll?: (selectors: string) => WebFiberElementImpl[];
getAttributeNames: () => string[];
getAttribute: (name: string) => string | null;
setAttribute: (name: string, value: string) => void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export type FirstElementPAPI = (

export type GetChildrenPAPI = (
element: WebFiberElementImpl,
) => WebFiberElementImpl[];
) => WebFiberElementImpl[] | null;

export type GetParentPAPI = (
element: WebFiberElementImpl,
Expand Down Expand Up @@ -263,15 +263,29 @@ export type SetCSSIdPAPI = (

export type GetPageElementPAPI = () => WebFiberElementImpl | undefined;

export type MarkTemplateElementPAPI = (
element: WebFiberElementImpl,
) => void;

export type MarkPartElementPAPI = (
element: WebFiberElementImpl,
partId: string,
) => void;

export type GetTemplatePartsPAPI = (
templateElement: WebFiberElementImpl,
) => Record<string, WebFiberElementImpl> | undefined;
) => Record<string, WebFiberElementImpl>;

interface JSErrorInfo {
release: string;
}

export interface MainThreadGlobalThis {
// __GetTemplateParts currently only provided by the thread-strategy = "all-on-ui" (default)
__GetTemplateParts?: GetTemplatePartsPAPI;

__MarkPartElement: MarkPartElementPAPI;
__MarkTemplateElement: MarkTemplateElementPAPI;
__AddEvent: AddEventPAPI;
__GetEvent: GetEventPAPI;
__GetEvents: GetEventsPAPI;
Expand Down Expand Up @@ -322,7 +336,6 @@ export interface MainThreadGlobalThis {
__SetInlineStyles: SetInlineStylesPAPI;
__SetCSSId: SetCSSIdPAPI;
__GetPageElement: GetPageElementPAPI;
__GetTemplateParts: GetTemplatePartsPAPI;
__globalProps: unknown;
SystemInfo: typeof systemInfo;
globalThis?: MainThreadGlobalThis;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ const mainThreadInjectVars = [
'SystemInfo',
'_I18nResourceTranslation',
'_AddEventListener',
'__GetTemplateParts',
'__MarkPartElement',
'__MarkTemplateElement',
];

const backgroundInjectVars = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ import {
type SetCSSIdPAPI,
type AddClassPAPI,
type SetClassesPAPI,
type GetTemplatePartsPAPI,
type GetPageElementPAPI,
type MinimalRawEventObject,
type I18nResourceTranslationOptions,
Expand Down Expand Up @@ -86,8 +85,11 @@ import {
__GetID,
__GetParent,
__GetTag,
__GetTemplateParts,
__InsertElementBefore,
__LastElement,
__MarkPartElement,
__MarkTemplateElement,
__NextElement,
__RemoveElement,
__ReplaceElement,
Expand Down Expand Up @@ -143,7 +145,9 @@ export interface MainThreadRuntimeConfig {
lepusCode: Record<string, LynxJSModule>;
browserConfig: BrowserConfig;
tagMap: Record<string, string>;
rootDom: Pick<Element, 'append' | 'addEventListener'>;
rootDom:
& Pick<Element, 'append' | 'addEventListener'>
& Partial<Pick<Element, 'querySelectorAll'>>;
jsContext: LynxContextEventTarget;
}

Expand Down Expand Up @@ -652,17 +656,18 @@ export function createMainThreadGlobalThis(
);
};

const __GetTemplateParts: GetTemplatePartsPAPI = () => {
return undefined;
};

const __GetPageElement: GetPageElementPAPI = () => {
return pageElement;
};

let release = '';
const isCSSOG = !pageConfig.enableCSSSelector;
const mtsGlobalThis: MainThreadGlobalThis = {
__GetTemplateParts: rootDom.querySelectorAll
? __GetTemplateParts
: undefined,
__MarkTemplateElement,
__MarkPartElement,
__AddEvent,
__GetEvent,
__GetEvents,
Expand Down Expand Up @@ -714,7 +719,6 @@ export function createMainThreadGlobalThis(
__SetInlineStyles,
__LoadLepusChunk,
__GetPageElement,
__GetTemplateParts,
__globalProps: globalProps,
SystemInfo: {
...systemInfo,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
cssIdAttribute,
lynxComponentConfigAttribute,
lynxDatasetAttribute,
lynxElementTemplateMarkerAttribute,
lynxPartIdAttribute,
lynxTagAttribute,
lynxUniqueIdAttribute,
type AddClassPAPI,
Expand All @@ -27,8 +29,11 @@ import {
type GetIDPAPI,
type GetParentPAPI,
type GetTagPAPI,
type GetTemplatePartsPAPI,
type InsertElementBeforePAPI,
type LastElementPAPI,
type MarkPartElementPAPI,
type MarkTemplateElementPAPI,
type NextElementPAPI,
type RemoveElementPAPI,
type ReplaceElementPAPI,
Expand All @@ -40,6 +45,7 @@ import {
type SetIDPAPI,
type SetInlineStylesPAPI,
type UpdateComponentIDPAPI,
type WebFiberElementImpl,
} from '@lynx-js/web-constants';
import { queryCSSProperty } from './style/cssPropertyMap.js';
import {
Expand All @@ -64,7 +70,7 @@ export const __FirstElement: FirstElementPAPI = /*#__PURE__*/ (

export const __GetChildren: GetChildrenPAPI = /*#__PURE__*/ (
element,
) => element.children;
) => element.children ? [...element.children] : null;

export const __GetParent: GetParentPAPI = /*#__PURE__*/ (
element,
Expand Down Expand Up @@ -319,3 +325,39 @@ export const __SetInlineStyles: SetInlineStylesPAPI = /*#__PURE__*/ (
element.setAttribute('style', transformedStyleStr);
}
};

export const __GetTemplateParts: GetTemplatePartsPAPI = (
templateElement,
) => {
const isTemplate =
templateElement.getAttribute(lynxElementTemplateMarkerAttribute)
!== null;
if (!isTemplate) {
return {};
}
const templateUniqueId = __GetElementUniqueID(templateElement);
const parts: Record<string, WebFiberElementImpl> = {};
const partElements = templateElement.querySelectorAll!(
`[${lynxUniqueIdAttribute}="${templateUniqueId}"] [${lynxPartIdAttribute}]:not([${lynxUniqueIdAttribute}="${templateUniqueId}"] [${lynxElementTemplateMarkerAttribute}] [${lynxPartIdAttribute}])`,
);
for (const partElement of partElements) {
const partId = partElement.getAttribute(lynxPartIdAttribute);
if (partId) {
parts[partId] = partElement as WebFiberElementImpl;
}
}
return parts;
};

export const __MarkTemplateElement: MarkTemplateElementPAPI = (
element,
) => {
element.setAttribute(lynxElementTemplateMarkerAttribute, '');
};

export const __MarkPartElement: MarkPartElementPAPI = (
element,
partId,
) => {
element.setAttribute(lynxPartIdAttribute, partId);
};
2 changes: 1 addition & 1 deletion packages/web-platform/web-tests/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const testMatch: string | undefined = (() => {
return '**/fp-only.spec.ts';
}
if (enableMultiThread || enableSSR) {
return '**/{react,web-core}.{test,spec}.ts';
return '**/{react,web-core,main-thread-apis}.{test,spec}.ts';
}
return undefined;
})();
Expand Down
38 changes: 21 additions & 17 deletions packages/web-platform/web-tests/shell-project/mainthread-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,41 @@ import {
_onEvent,
OffscreenDocument,
} from '@lynx-js/offscreen-document/webworker';
import type {
ElementOperation,
OffscreenElement,
} from '@lynx-js/offscreen-document';

import {
lynxTagAttribute,
lynxUniqueIdAttribute,
type MainThreadGlobalThis,
type WebFiberElementImpl,
} from '@lynx-js/web-constants';

const ENABLE_MULTI_THREAD = !!process.env.ENABLE_MULTI_THREAD;

type ComparableElementJson = {
tag: string;
children: ComparableElementJson[];
parentUid?: number;
};
let runtime!: MainThreadGlobalThis;
let elementOperations: ElementOperation[] = [];
let elementOperations: unknown[] = [];

const div: HTMLElement = document.createElement('div');
div.id = 'root';
const shadowRoot = div.attachShadow({ mode: 'open' });
document.body.appendChild(div);
const docu = new OffscreenDocument({
onCommit(operations) {
elementOperations = operations;
},
});
const { decodeOperation } = initOffscreenDocument({
shadowRoot,
onEvent: docu[_onEvent],
});
const docu = ENABLE_MULTI_THREAD
? new OffscreenDocument({
onCommit(operations) {
elementOperations = operations;
},
})
: document;
const { decodeOperation } = ENABLE_MULTI_THREAD
? initOffscreenDocument({
shadowRoot,
onEvent: docu[_onEvent],
})
: {};

function serializeElementThreadElement(
element: WebFiberElementImpl,
Expand Down Expand Up @@ -120,15 +123,16 @@ function initializeMainThreadTest() {
enableJSDataProcessor: false,
},
// @ts-expect-error
rootDom: docu,
rootDom: ENABLE_MULTI_THREAD ? docu : shadowRoot,
styleInfo: {},
globalProps: {},
callbacks: {
mainChunkReady: function(): void {
},
flushElementTree: () => {
docu.commit();
decodeOperation(elementOperations);
// @ts-expect-error
docu.commit?.();
decodeOperation?.(elementOperations as any);
},
_ReportError: function(): void {
document.body.innerHTML = '';
Expand Down
56 changes: 56 additions & 0 deletions packages/web-platform/web-tests/tests/main-thread-apis.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import { test, expect } from './coverage-fixture.js';
import type { Page } from '@playwright/test';

const ENABLE_MULTI_THREAD = !!process.env.ENABLE_MULTI_THREAD;

const wait = async (ms: number) => {
await new Promise((resolve) => {
setTimeout(resolve, ms);
Expand All @@ -26,7 +28,7 @@
const domTree = await page.evaluate(() => {
return globalThis.genDomElementTree() as Record<string, unknown>;
});
expect(fiberTree).toStrictEqual(domTree);

Check failure on line 31 in packages/web-platform/web-tests/tests/main-thread-apis.test.ts

View workflow job for this annotation

GitHub Actions / Playwright (Default) / check

[webkit] › tests/main-thread-apis.test.ts:1183:3 › main thread api tests › publicComponentEvent

2) [webkit] › tests/main-thread-apis.test.ts:1183:3 › main thread api tests › publicComponentEvent Error: expect(received).toStrictEqual(expected) // deep equality - Expected - 1 + Received + 17 - Object {} + Object { + "children": Array [ + Object { + "children": Array [ + Object { + "children": Array [], + "parentUid": 2, + "tag": "view", + }, + ], + "parentUid": 1, + "tag": "view", + }, + ], + "parentUid": undefined, + "tag": "page", + } 29 | return globalThis.genDomElementTree() as Record<string, unknown>; 30 | }); > 31 | expect(fiberTree).toStrictEqual(domTree); | ^ 32 | }); 33 | 34 | test('createElementView', async ({ page }, { title }) => { at /home/runner/_work/lynx-stack/lynx-stack/packages/web-platform/web-tests/tests/main-thread-apis.test.ts:31:23
});

test('createElementView', async ({ page }, { title }) => {
Expand Down Expand Up @@ -1179,7 +1181,7 @@
);

test('publicComponentEvent', async ({ page }, { title }) => {
const ret = await page.evaluate(() => {

Check failure on line 1184 in packages/web-platform/web-tests/tests/main-thread-apis.test.ts

View workflow job for this annotation

GitHub Actions / Playwright (Default) / check

[webkit] › tests/main-thread-apis.test.ts:1183:3 › main thread api tests › publicComponentEvent

2) [webkit] › tests/main-thread-apis.test.ts:1183:3 › main thread api tests › publicComponentEvent Error: page.evaluate: TypeError: undefined is not an object (evaluating 'wasm.malloc') 1182 | 1183 | test('publicComponentEvent', async ({ page }, { title }) => { > 1184 | const ret = await page.evaluate(() => { | ^ 1185 | let page = globalThis.__CreatePage('0', 0); 1186 | let parent = globalThis.__CreateComponent( 1187 | 0, at /home/runner/_work/lynx-stack/lynx-stack/packages/web-platform/web-tests/tests/main-thread-apis.test.ts:1184:28
let page = globalThis.__CreatePage('0', 0);
let parent = globalThis.__CreateComponent(
0,
Expand Down Expand Up @@ -1214,4 +1216,58 @@
await expect(publicComponentEventArgs.hname).toBe('hname');
await expect(publicComponentEventArgs.componentId).toBe('id1');
});

test(
'__MarkTemplate_and_Get_Parts',
async ({ page }, { title }) => {
test.skip(ENABLE_MULTI_THREAD, 'NYI for multi-thread');
/*
* <view template> <!-- grand parent template -->
* <view part>
* <view template> <!-- target template -->
* <view> <!-- normal node -->
* <view part id="target"> <!-- target part -->
* <view template> <!-- sub template -->
* <view part> <!-- sub part, should be able to "get part" from the target -->
* </view>
* </view>
* </view>
* </view>
* </view>
* </view>
*/
const result = await page.evaluate(() => {
const root = globalThis.__CreatePage('page', 0);
const grandParentTemplate = globalThis.__CreateView(0);
globalThis.__MarkTemplateElement(grandParentTemplate);
let view = globalThis.__CreateView(0);
globalThis.__MarkPartElement(view, 'grandParentPart');
globalThis.__AppendElement(grandParentTemplate, view);
const targetTemplate = globalThis.__CreateView(0);
globalThis.__MarkTemplateElement(targetTemplate);
globalThis.__AppendElement(view, targetTemplate);
view = globalThis.__CreateView(0);
globalThis.__AppendElement(targetTemplate, view);
const targetPart = globalThis.__CreateView(0);
globalThis.__MarkPartElement(targetPart, 'targetPart');
globalThis.__AppendElement(view, targetPart);
const subTemplate = globalThis.__CreateView(0);
globalThis.__MarkTemplateElement(subTemplate);
globalThis.__AppendElement(targetPart, subTemplate);
const subPart = globalThis.__CreateView(0);
globalThis.__MarkPartElement(subPart, 'subPart');
globalThis.__AppendElement(subTemplate, subPart);
globalThis.__FlushElementTree();
return {
targetPartLength:
Object.keys(globalThis.__GetTemplateParts(targetTemplate)).length,
targetPartExist:
globalThis.__GetTemplateParts(targetTemplate)['targetPart']
=== targetPart,
};
});
expect(result.targetPartLength).toBe(1);
expect(result.targetPartExist).toBe(true);
},
);
});
Loading