@lynx-js/web-core 0.9.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # @lynx-js/web-core
2
2
 
3
+ ## 0.10.0
4
+
5
+ ### Minor Changes
6
+
7
+ - feat: rewrite the main thread Element PAPIs ([#343](https://github.com/lynx-family/lynx-stack/pull/343))
8
+
9
+ In this commit we've rewritten the main thread apis.
10
+
11
+ The most highlighted change is that
12
+
13
+ - Before this commit we send events directly to bts
14
+ - After this change, we send events to mts then send them to bts with some data combined.
15
+
16
+ ### Patch Changes
17
+
18
+ - refactor: timing system ([#378](https://github.com/lynx-family/lynx-stack/pull/378))
19
+
20
+ Now we moved the timing system to the background thread.
21
+
22
+ - feat: support `defaultOverflowVisible` config ([#406](https://github.com/lynx-family/lynx-stack/pull/406))
23
+
24
+ - fix(web): rsbuild will bundle 2 exactly same chunk for two same `new Worker` stmt ([#372](https://github.com/lynx-family/lynx-stack/pull/372))
25
+
26
+ the bundle size will be optimized about 28.2KB
27
+
28
+ - fix: inline style will be removed for value number `0` ([#368](https://github.com/lynx-family/lynx-stack/pull/368))
29
+
30
+ the inline style value could be incorrectly removed for number value `0`;
31
+
32
+ For example, `flex-shrink:0` may be ignored.
33
+
34
+ - feat: The onNapiModulesCall function of lynx-view provides the fourth parameter: `lynxView`, which is the actual lynx-view DOM. ([#350](https://github.com/lynx-family/lynx-stack/pull/350))
35
+
36
+ - fix: publicComponentEvent args order ([#401](https://github.com/lynx-family/lynx-stack/pull/401))
37
+
38
+ - Updated dependencies [[`3a8dabd`](https://github.com/lynx-family/lynx-stack/commit/3a8dabd877084c15db1404c912dd8a19c7a0fc59), [`a521759`](https://github.com/lynx-family/lynx-stack/commit/a5217592f5aebea4b17860e729d523ecabb5f691), [`890c6c5`](https://github.com/lynx-family/lynx-stack/commit/890c6c51470c82104abb1049681f55e5d97cf9d6)]:
39
+ - @lynx-js/web-worker-runtime@0.10.0
40
+ - @lynx-js/web-constants@0.10.0
41
+ - @lynx-js/web-worker-rpc@0.10.0
42
+
3
43
  ## 0.9.1
4
44
 
5
45
  ### Patch Changes
package/README.md CHANGED
@@ -10,8 +10,24 @@ import '@lynx-js/web-core/index.css';
10
10
 
11
11
  document.body.innerHTML = `
12
12
  <lynx-view
13
- style="height:100vh; width:100vw;"
14
- url="http://localhost:3000/main/main-thread.js"
13
+ style="height:100vh; width:100vw;"
14
+ url="http://localhost:3000/main/main-thread.js"
15
+ >
16
+ </lynx-view>`;
17
+ ```
18
+
19
+ If you use Lynx elements (view, text, image, etc.), you need to import `@lynx-js/web-elements`:
20
+
21
+ ```javascript
22
+ import '@lynx-js/web-core';
23
+ import '@lynx-js/web-core/index.css';
24
+ import '@lynx-js/web-elements/all';
25
+ import '@lynx-js/web-elements/index.css';
26
+
27
+ document.body.innerHTML = `
28
+ <lynx-view
29
+ style="height:100vh; width:100vw;"
30
+ url="http://localhost:3000/main/main-thread.js"
15
31
  >
16
32
  </lynx-view>`;
17
33
  ```
@@ -1,4 +1,11 @@
1
1
  import { type Cloneable, type NapiModulesCall, type NapiModulesMap, type NativeModulesCall, type NativeModulesMap, type UpdateDataType } from '@lynx-js/web-constants';
2
+ export type INapiModulesCall = (name: string, data: any, moduleName: string, lynxView: LynxView) => Promise<{
3
+ data: unknown;
4
+ transfer?: Transferable[];
5
+ }> | {
6
+ data: unknown;
7
+ transfer?: Transferable[];
8
+ } | undefined;
2
9
  /**
3
10
  * Based on our experiences, these elements are almost used in all lynx cards.
4
11
  */
@@ -12,7 +19,7 @@ import { type Cloneable, type NapiModulesCall, type NapiModulesMap, type NativeM
12
19
  * @param {"auto" | null} height [optional] set it to "auto" for height auto-sizing
13
20
  * @param {"auto" | null} width [optional] set it to "auto" for width auto-sizing
14
21
  * @param {NapiModulesMap} napiModulesMap [optional] the napiModule which is called in lynx-core. key is module-name, value is esm url.
15
- * @param {NapiModulesCall} onNapiModulesCall [optional] the NapiModule value handler.
22
+ * @param {INapiModulesCall} onNapiModulesCall [optional] the NapiModule value handler.
16
23
  * @param {"false" | "true" | null} injectHeadLinks [optional] @default true set it to "false" to disable injecting the <link href="" ref="stylesheet"> styles into shadowroot
17
24
  *
18
25
  * @event error lynx card fired an error
@@ -95,7 +102,7 @@ export declare class LynxView extends HTMLElement {
95
102
  * @property
96
103
  */
97
104
  get onNapiModulesCall(): NapiModulesCall | undefined;
98
- set onNapiModulesCall(handler: NapiModulesCall);
105
+ set onNapiModulesCall(handler: INapiModulesCall);
99
106
  /**
100
107
  * @public
101
108
  * @method
@@ -17,7 +17,7 @@ import { inShadowRootStyles } from './inShadowRootStyles.js';
17
17
  * @param {"auto" | null} height [optional] set it to "auto" for height auto-sizing
18
18
  * @param {"auto" | null} width [optional] set it to "auto" for width auto-sizing
19
19
  * @param {NapiModulesMap} napiModulesMap [optional] the napiModule which is called in lynx-core. key is module-name, value is esm url.
20
- * @param {NapiModulesCall} onNapiModulesCall [optional] the NapiModule value handler.
20
+ * @param {INapiModulesCall} onNapiModulesCall [optional] the NapiModule value handler.
21
21
  * @param {"false" | "true" | null} injectHeadLinks [optional] @default true set it to "false" to disable injecting the <link href="" ref="stylesheet"> styles into shadowroot
22
22
  *
23
23
  * @event error lynx card fired an error
@@ -167,7 +167,9 @@ export class LynxView extends HTMLElement {
167
167
  return this.#onNapiModulesCall;
168
168
  }
169
169
  set onNapiModulesCall(handler) {
170
- this.#onNapiModulesCall = handler;
170
+ this.#onNapiModulesCall = (name, data, moduleName) => {
171
+ return handler(name, data, moduleName, this);
172
+ };
171
173
  }
172
174
  /**
173
175
  * @public
@@ -289,7 +291,9 @@ export class LynxView extends HTMLElement {
289
291
  const styleElement = document.createElement('style');
290
292
  this.shadowRoot.append(styleElement);
291
293
  const styleSheet = styleElement.sheet;
292
- styleSheet.insertRule(inShadowRootStyles);
294
+ for (const rule of inShadowRootStyles) {
295
+ styleSheet.insertRule(rule);
296
+ }
293
297
  const injectHeadLinks = this.getAttribute('inject-head-links') !== 'false';
294
298
  if (injectHeadLinks) {
295
299
  document.head.querySelectorAll('link[rel="stylesheet"]').forEach((linkElement) => {
@@ -1 +1 @@
1
- export declare const inShadowRootStyles = "\n[lynx-default-display-linear=\"false\"] * {\n --lynx-display: flex;\n --lynx-display-toggle: var(--lynx-display-flex);\n}\n";
1
+ export declare const inShadowRootStyles: string[];
@@ -1,7 +1,10 @@
1
- export const inShadowRootStyles = `
2
- [lynx-default-display-linear="false"] * {
3
- --lynx-display: flex;
4
- --lynx-display-toggle: var(--lynx-display-flex);
5
- }
6
- `;
1
+ export const inShadowRootStyles = [
2
+ ` [lynx-default-display-linear="false"] * {
3
+ --lynx-display: flex;
4
+ --lynx-display-toggle: var(--lynx-display-flex);
5
+ }`,
6
+ `[lynx-default-overflow-visible="true"] x-view{
7
+ overflow: visible;
8
+ }`,
9
+ ];
7
10
  //# sourceMappingURL=inShadowRootStyles.js.map
package/dist/index.d.ts CHANGED
@@ -1,3 +1,2 @@
1
1
  export { createLynxView } from './apis/createLynxView.js';
2
2
  export { LynxView } from './apis/LynxView.js';
3
- export { lynxRuntimeValue } from '@lynx-js/web-constants';
package/dist/index.js CHANGED
@@ -3,5 +3,4 @@
3
3
  // LICENSE file in the root directory of this source tree.
4
4
  export { createLynxView } from './apis/createLynxView.js';
5
5
  export { LynxView } from './apis/LynxView.js';
6
- export { lynxRuntimeValue } from '@lynx-js/web-constants';
7
6
  //# sourceMappingURL=index.js.map
@@ -19,10 +19,7 @@ export function bootWorkers() {
19
19
  function createMainWorker() {
20
20
  const channelToMainThread = new MessageChannel();
21
21
  const channelMainThreadWithBackground = new MessageChannel();
22
- const mainThreadWorker = new Worker(new URL('@lynx-js/web-worker-runtime', import.meta.url), {
23
- type: 'module',
24
- name: `lynx-main`,
25
- });
22
+ const mainThreadWorker = createWebWorker();
26
23
  const mainThreadMessage = {
27
24
  mode: 'main',
28
25
  toUIThread: channelToMainThread.port2,
@@ -42,10 +39,7 @@ function createMainWorker() {
42
39
  }
43
40
  function createBackgroundWorker(channelMainThreadWithBackground) {
44
41
  const channelToBackground = new MessageChannel();
45
- const backgroundThreadWorker = new Worker(new URL('@lynx-js/web-worker-runtime', import.meta.url), {
46
- type: 'module',
47
- name: `lynx-bg`,
48
- });
42
+ const backgroundThreadWorker = createWebWorker();
49
43
  const backgroundThreadMessage = {
50
44
  mode: 'background',
51
45
  toUIThread: channelToBackground.port2,
@@ -59,4 +53,10 @@ function createBackgroundWorker(channelMainThreadWithBackground) {
59
53
  const backgroundRpc = new Rpc(channelToBackground.port1, 'ui-to-bg');
60
54
  return { backgroundRpc, backgroundThreadWorker };
61
55
  }
56
+ function createWebWorker() {
57
+ return new Worker(new URL('@lynx-js/web-worker-runtime', import.meta.url), {
58
+ type: 'module',
59
+ name: `lynx-web`,
60
+ });
61
+ }
62
62
  //# sourceMappingURL=bootWorkers.js.map
@@ -0,0 +1,2 @@
1
+ import type { Rpc } from '@lynx-js/web-worker-rpc';
2
+ export declare function registerDispatchLynxViewEventHandler(rpc: Rpc, shadowRoot: ShadowRoot): void;
@@ -0,0 +1,15 @@
1
+ // Copyright 2023 The Lynx Authors. All rights reserved.
2
+ // Licensed under the Apache License Version 2.0 that can be found in the
3
+ // LICENSE file in the root directory of this source tree.
4
+ import { dispatchLynxViewEventEndpoint } from '@lynx-js/web-constants';
5
+ export function registerDispatchLynxViewEventHandler(rpc, shadowRoot) {
6
+ rpc.registerHandler(dispatchLynxViewEventEndpoint, (eventType, detail) => {
7
+ shadowRoot.dispatchEvent(new CustomEvent(eventType, {
8
+ detail,
9
+ bubbles: true,
10
+ cancelable: true,
11
+ composed: true,
12
+ }));
13
+ });
14
+ }
15
+ //# sourceMappingURL=registerDispatchLynxViewEventHandler.js.map
@@ -1,15 +1,6 @@
1
- import { type PageConfig, type flushElementTreeEndpoint } from '@lynx-js/web-constants';
2
1
  import type { Rpc } from '@lynx-js/web-worker-rpc';
3
- import type { RuntimePropertyOnElement } from '../../types/RuntimePropertyOnElement.js';
4
- export declare function registerFlushElementTreeHandler(mainThreadRpc: Rpc, endpoint: typeof flushElementTreeEndpoint, options: {
5
- pageConfig: PageConfig;
6
- backgroundRpc: Rpc;
2
+ export declare function registerFlushElementTreeHandler(mainThreadRpc: Rpc, options: {
7
3
  shadowRoot: ShadowRoot;
8
4
  }, onCommit: (info: {
9
- pipelineId: string | undefined;
10
- timingFlags: string[];
11
5
  isFP: boolean;
12
- }) => void, markTimingInternal: (timingKey: string, pipelineId?: string, timeStamp?: number) => void): {
13
- uniqueIdToElement: WeakRef<HTMLElement & RuntimePropertyOnElement>[];
14
- uniqueIdToCssInJsRule: WeakRef<CSSStyleRule>[];
15
- };
6
+ }) => void): void;
@@ -1,97 +1,24 @@
1
1
  // Copyright 2023 The Lynx Authors. All rights reserved.
2
2
  // Licensed under the Apache License Version 2.0 that can be found in the
3
3
  // LICENSE file in the root directory of this source tree.
4
- import { componentIdAttribute, lynxDefaultDisplayLinearAttribute, lynxRuntimeValue, lynxTagAttribute, lynxUniqueIdAttribute, parentComponentUniqueIdAttribute, postMainThreadEvent, publicComponentEventEndpoint, publishEventEndpoint, } from '@lynx-js/web-constants';
5
- import { decodeElementOperation } from '../decodeElementOperation.js';
6
- import { createCrossThreadEvent } from '../../utils/createCrossThreadEvent.js';
7
- function applyPageAttributes(page, pageConfig) {
8
- if (pageConfig.defaultDisplayLinear === false) {
9
- page.setAttribute(lynxDefaultDisplayLinearAttribute, 'false');
10
- }
11
- }
12
- export function registerFlushElementTreeHandler(mainThreadRpc, endpoint, options, onCommit, markTimingInternal) {
13
- const { pageConfig, backgroundRpc, shadowRoot, } = options;
14
- const uniqueIdToElement = [];
15
- const uniqueIdToCssInJsRule = [];
16
- const rootStyleElementForCssInJs = document.createElement('style');
17
- if (!pageConfig.enableCSSSelector) {
18
- shadowRoot.append(rootStyleElementForCssInJs);
19
- }
20
- const createElementImpl = (tag) => {
21
- const element = document.createElement(tag);
22
- element[lynxRuntimeValue] = {
23
- dataset: {},
24
- eventHandler: {},
25
- };
26
- return element;
27
- };
28
- const createStyleRuleImpl = (uniqueId, initialStyle) => {
29
- const commonStyleSheetText = `[${lynxUniqueIdAttribute}="${uniqueId.toString()}"]{${initialStyle}}`;
30
- const idx = rootStyleElementForCssInJs.sheet.insertRule(commonStyleSheetText);
31
- return rootStyleElementForCssInJs.sheet.cssRules[idx];
32
- };
33
- const mtsHandler = (event) => {
34
- const crossThreadEvent = createCrossThreadEvent(event);
35
- mainThreadRpc.invoke(postMainThreadEvent, [crossThreadEvent]);
36
- };
37
- const btsHandler = (event) => {
38
- const crossThreadEvent = createCrossThreadEvent(event);
39
- const currentTarget = event.currentTarget;
40
- const parentComponentUniqueId = currentTarget.getAttribute(parentComponentUniqueIdAttribute) ?? '0';
41
- const componentTargetDom = shadowRoot.querySelector(`[${lynxUniqueIdAttribute}="${parentComponentUniqueId}"]`);
42
- const componentId = componentTargetDom?.getAttribute(lynxTagAttribute) !== 'page'
43
- ? componentTargetDom?.getAttribute(componentIdAttribute) ?? undefined
44
- : undefined;
45
- const hname = currentTarget[lynxRuntimeValue]
46
- .eventHandler[crossThreadEvent.type].hname;
47
- if (componentId) {
48
- backgroundRpc.invoke(publicComponentEventEndpoint, [
49
- componentId,
50
- hname,
51
- crossThreadEvent,
52
- ]);
53
- }
54
- else {
55
- backgroundRpc.invoke(publishEventEndpoint, [
56
- hname,
57
- crossThreadEvent,
58
- ]);
59
- }
60
- };
61
- mainThreadRpc.registerHandler(endpoint, (operations, options, cardCss, timingFlags) => {
62
- const { pipelineOptions } = options;
63
- const pipelineId = pipelineOptions?.pipelineID;
64
- markTimingInternal('dispatch_start', pipelineId);
65
- markTimingInternal('layout_start', pipelineId);
66
- markTimingInternal('ui_operation_flush_start', pipelineId);
67
- const page = decodeElementOperation(operations, {
68
- uniqueIdToElement,
69
- uniqueIdToCssInJsRule,
70
- createElementImpl,
71
- createStyleRuleImpl,
72
- eventHandler: {
73
- mtsHandler,
74
- btsHandler,
75
- },
76
- });
77
- markTimingInternal('ui_operation_flush_end', pipelineId);
78
- const isFP = !!page;
79
- if (isFP) {
80
- // on FP
81
- const styleElement = document.createElement('style');
82
- styleElement.innerHTML = cardCss;
83
- shadowRoot.append(styleElement);
84
- shadowRoot.append(page);
85
- applyPageAttributes(page, pageConfig);
86
- }
87
- markTimingInternal('layout_end', pipelineId);
88
- markTimingInternal('dispatch_end', pipelineId);
4
+ import { flushElementTreeEndpoint, postOffscreenEventEndpoint, } from '@lynx-js/web-constants';
5
+ import { initOffscreenDocument } from '@lynx-js/offscreen-document/main';
6
+ export function registerFlushElementTreeHandler(mainThreadRpc, options, onCommit) {
7
+ const { shadowRoot, } = options;
8
+ const onEvent = mainThreadRpc.createCall(postOffscreenEventEndpoint);
9
+ const { decodeOperation } = initOffscreenDocument({
10
+ shadowRoot,
11
+ onEvent,
12
+ });
13
+ let isFP = true;
14
+ mainThreadRpc.registerHandler(flushElementTreeEndpoint, (operations) => {
15
+ decodeOperation(operations);
89
16
  onCommit({
90
- pipelineId,
91
- timingFlags,
92
17
  isFP,
93
18
  });
19
+ if (isFP) {
20
+ isFP = false;
21
+ }
94
22
  });
95
- return { uniqueIdToElement, uniqueIdToCssInJsRule };
96
23
  }
97
24
  //# sourceMappingURL=registerFlushElementTreeHandler.js.map
@@ -1,13 +1,14 @@
1
+ // Copyright 2023 The Lynx Authors. All rights reserved.
2
+ // Licensed under the Apache License Version 2.0 that can be found in the
3
+ // LICENSE file in the root directory of this source tree.
1
4
  import { Rpc } from '@lynx-js/web-worker-rpc';
2
5
  import { queryNodes } from './queryNodes.js';
3
- import { ErrorCode, lynxRuntimeValue, invokeUIMethodEndpoint, } from '@lynx-js/web-constants';
6
+ import { ErrorCode, invokeUIMethodEndpoint } from '@lynx-js/web-constants';
4
7
  const methodAlias = {
5
8
  'boundingClientRect': (element) => {
6
9
  const rect = element.getBoundingClientRect();
7
10
  return {
8
11
  id: element.id,
9
- dataset: element[lynxRuntimeValue]
10
- .dataset,
11
12
  width: rect.width,
12
13
  height: rect.height,
13
14
  left: rect.left,
@@ -1,7 +1,6 @@
1
1
  // Copyright 2023 The Lynx Authors. All rights reserved.
2
2
  // Licensed under the Apache License Version 2.0 that can be found in the
3
3
  // LICENSE file in the root directory of this source tree.
4
- import { createExposureService } from './crossThreadHandlers/createExposureService.js';
5
4
  import { registerInvokeUIMethodHandler } from './crossThreadHandlers/registerInvokeUIMethodHandler.js';
6
5
  import { registerNativePropsHandler } from './crossThreadHandlers/registerSetNativePropsHandler.js';
7
6
  import { registerNativeModulesCallHandler } from './crossThreadHandlers/registerNativeModulesCallHandler.js';
@@ -9,13 +8,13 @@ import { bootWorkers } from './bootWorkers.js';
9
8
  import { registerReportErrorHandler } from './crossThreadHandlers/registerReportErrorHandler.js';
10
9
  import { registerFlushElementTreeHandler } from './crossThreadHandlers/registerFlushElementTreeHandler.js';
11
10
  import { createDispose } from './crossThreadHandlers/createDispose.js';
12
- import { bootTimingSystem } from './crossThreadHandlers/bootTimingSystem.js';
13
11
  import { registerTriggerComponentEventHandler } from './crossThreadHandlers/registerTriggerComponentEventHandler.js';
14
12
  import { registerSelectComponentHandler } from './crossThreadHandlers/registerSelectComponentHandler.js';
15
- import { flushElementTreeEndpoint, mainThreadChunkReadyEndpoint, mainThreadStartEndpoint, sendGlobalEventEndpoint, uiThreadFpReadyEndpoint, } from '@lynx-js/web-constants';
13
+ import { mainThreadChunkReadyEndpoint, mainThreadStartEndpoint, markTimingEndpoint, sendGlobalEventEndpoint, uiThreadFpReadyEndpoint, } from '@lynx-js/web-constants';
16
14
  import { loadTemplate } from '../utils/loadTemplate.js';
17
15
  import { createUpdateData } from './crossThreadHandlers/createUpdateData.js';
18
16
  import { registerNapiModulesCallHandler } from './crossThreadHandlers/registerNapiModulesCallHandler.js';
17
+ import { registerDispatchLynxViewEventHandler } from './crossThreadHandlers/registerDispatchLynxViewEventHandler.js';
19
18
  export function startUIThread(templateUrl, configs, shadowRoot, callbacks) {
20
19
  const createLynxStartTiming = performance.now() + performance.timeOrigin;
21
20
  const { nativeModulesMap, napiModulesMap } = configs;
@@ -23,7 +22,12 @@ export function startUIThread(templateUrl, configs, shadowRoot, callbacks) {
23
22
  const sendGlobalEvent = backgroundRpc.createCall(sendGlobalEventEndpoint);
24
23
  const uiThreadFpReady = backgroundRpc.createCall(uiThreadFpReadyEndpoint);
25
24
  const mainThreadStart = mainThreadRpc.createCall(mainThreadStartEndpoint);
26
- const { markTimingInternal, sendTimingResult } = bootTimingSystem(mainThreadRpc, backgroundRpc, shadowRoot);
25
+ const markTiming = backgroundRpc.createCall(markTimingEndpoint);
26
+ const markTimingInternal = (timingKey, pipelineId, timeStamp) => {
27
+ if (!timeStamp)
28
+ timeStamp = performance.now() + performance.timeOrigin;
29
+ markTiming(timingKey, pipelineId, timeStamp);
30
+ };
27
31
  markTimingInternal('create_lynx_start', undefined, createLynxStartTiming);
28
32
  markTimingInternal('load_template_start');
29
33
  loadTemplate(templateUrl).then((template) => {
@@ -36,24 +40,20 @@ export function startUIThread(templateUrl, configs, shadowRoot, callbacks) {
36
40
  });
37
41
  });
38
42
  registerReportErrorHandler(mainThreadRpc, callbacks.onError);
39
- mainThreadRpc.registerHandler(mainThreadChunkReadyEndpoint, (mainChunkInfo) => {
40
- const { pageConfig } = mainChunkInfo;
41
- registerFlushElementTreeHandler(mainThreadRpc, flushElementTreeEndpoint, {
42
- pageConfig,
43
- backgroundRpc,
43
+ registerDispatchLynxViewEventHandler(backgroundRpc, shadowRoot);
44
+ mainThreadRpc.registerHandler(mainThreadChunkReadyEndpoint, () => {
45
+ registerFlushElementTreeHandler(mainThreadRpc, {
44
46
  shadowRoot,
45
47
  }, (info) => {
46
- const { pipelineId, timingFlags, isFP } = info;
48
+ const { isFP } = info;
47
49
  if (isFP) {
48
50
  registerInvokeUIMethodHandler(backgroundRpc, shadowRoot);
49
51
  registerNativePropsHandler(backgroundRpc, shadowRoot);
50
52
  registerTriggerComponentEventHandler(backgroundRpc, shadowRoot);
51
53
  registerSelectComponentHandler(backgroundRpc, shadowRoot);
52
- createExposureService(backgroundRpc, shadowRoot);
53
54
  uiThreadFpReady();
54
55
  }
55
- sendTimingResult(pipelineId, timingFlags, isFP);
56
- }, markTimingInternal);
56
+ });
57
57
  });
58
58
  registerNativeModulesCallHandler(backgroundRpc, callbacks.nativeModulesCall);
59
59
  registerNapiModulesCallHandler(backgroundRpc, callbacks.napiModulesCall);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lynx-js/web-core",
3
- "version": "0.9.1",
3
+ "version": "0.10.0",
4
4
  "private": false,
5
5
  "description": "",
6
6
  "keywords": [],
@@ -24,13 +24,14 @@
24
24
  "**/*.css"
25
25
  ],
26
26
  "dependencies": {
27
- "@lynx-js/web-constants": "0.9.1",
28
- "@lynx-js/web-worker-rpc": "0.9.1",
29
- "@lynx-js/web-worker-runtime": "0.9.1"
27
+ "@lynx-js/offscreen-document": "0.0.0",
28
+ "@lynx-js/web-constants": "0.10.0",
29
+ "@lynx-js/web-worker-rpc": "0.10.0",
30
+ "@lynx-js/web-worker-runtime": "0.10.0"
30
31
  },
31
32
  "devDependencies": {
32
33
  "@lynx-js/lynx-core": "0.1.0",
33
- "@lynx-js/web-elements": "0.5.1"
34
+ "@lynx-js/web-elements": "0.5.2"
34
35
  },
35
36
  "peerDependencies": {
36
37
  "@lynx-js/lynx-core": "0.1.0",
@@ -1,17 +0,0 @@
1
- import type { LynxEventType, Cloneable } from '@lynx-js/web-constants';
2
- import { lynxRuntimeValue } from '@lynx-js/web-constants';
3
- type RuntimeValue = {
4
- dataset: {
5
- [key: string]: Cloneable;
6
- };
7
- eventHandler: Record<string, {
8
- type: LynxEventType;
9
- handler: (ev: Event) => void;
10
- hname: string;
11
- } | undefined>;
12
- [key: string]: any;
13
- };
14
- export type RuntimePropertyOnElement = {
15
- [lynxRuntimeValue]: RuntimeValue;
16
- };
17
- export {};
@@ -1,2 +0,0 @@
1
- import { lynxRuntimeValue } from '@lynx-js/web-constants';
2
- //# sourceMappingURL=RuntimePropertyOnElement.js.map
@@ -1,5 +0,0 @@
1
- import type { Rpc } from '@lynx-js/web-worker-rpc';
2
- export declare function bootTimingSystem(mainThreadRpc: Rpc, backgroundThreadRpc: Rpc, shadowRoot: ShadowRoot): {
3
- markTimingInternal: (timingKey: string, pipelineId?: string, timeStamp?: number) => void;
4
- sendTimingResult: (pipelineId: string | undefined, timingFlags: string[], isFp: boolean) => void;
5
- };
@@ -1,51 +0,0 @@
1
- // Copyright 2023 The Lynx Authors. All rights reserved.
2
- // Licensed under the Apache License Version 2.0 that can be found in the
3
- // LICENSE file in the root directory of this source tree.
4
- import { postTimingInfoFromBackgroundThread, postTimingInfoFromMainThread, postTimingResult, } from '@lynx-js/web-constants';
5
- export function bootTimingSystem(mainThreadRpc, backgroundThreadRpc, shadowRoot) {
6
- const setupTiming = {};
7
- const pipelineIdToTiming = new Map();
8
- let commonTimingFlags = [];
9
- function markTimingInternal(timingKey, pipelineId, timeStamp) {
10
- if (!timeStamp)
11
- timeStamp = performance.now() + performance.timeOrigin;
12
- if (!pipelineId) {
13
- setupTiming[timingKey] = timeStamp;
14
- return;
15
- }
16
- if (!pipelineIdToTiming.has(pipelineId)) {
17
- pipelineIdToTiming.set(pipelineId, {});
18
- }
19
- const timingInfo = pipelineIdToTiming.get(pipelineId);
20
- timingInfo[timingKey] = timeStamp;
21
- }
22
- function sendTimingResult(pipelineId, timingFlags, isFp) {
23
- const timingInfo = (pipelineId ? pipelineIdToTiming.get(pipelineId) : undefined) ?? {};
24
- if (!pipelineId)
25
- commonTimingFlags = commonTimingFlags.concat(timingFlags);
26
- else
27
- timingFlags = timingFlags.concat(commonTimingFlags);
28
- backgroundThreadRpc.invoke(postTimingResult, [
29
- pipelineId,
30
- timingInfo,
31
- timingFlags,
32
- isFp ? setupTiming : undefined,
33
- ]);
34
- shadowRoot.dispatchEvent(new CustomEvent('timing', {
35
- detail: isFp ? setupTiming : timingInfo,
36
- bubbles: true,
37
- cancelable: true,
38
- composed: true,
39
- }));
40
- if (pipelineId) {
41
- pipelineIdToTiming.delete(pipelineId);
42
- }
43
- }
44
- mainThreadRpc.registerHandler(postTimingInfoFromMainThread, markTimingInternal);
45
- backgroundThreadRpc.registerHandler(postTimingInfoFromBackgroundThread, markTimingInternal);
46
- return {
47
- markTimingInternal,
48
- sendTimingResult,
49
- };
50
- }
51
- //# sourceMappingURL=bootTimingSystem.js.map
@@ -1,2 +0,0 @@
1
- import type { Rpc } from '@lynx-js/web-worker-rpc';
2
- export declare function createExposureService(rpc: Rpc, shadowRoot: ShadowRoot): void;
@@ -1,57 +0,0 @@
1
- import { lynxUniqueIdAttribute, postExposureEndpoint, switchExposureService, } from '@lynx-js/web-constants';
2
- import { createCrossThreadEvent } from '../../utils/createCrossThreadEvent.js';
3
- export function createExposureService(rpc, shadowRoot) {
4
- let working = true;
5
- let exposureCache = [];
6
- let disexposureCache = [];
7
- const onScreen = new Map();
8
- async function exposureEventHandler(ev) {
9
- const exposureEvent = createCrossThreadEvent(ev);
10
- exposureEvent.detail['unique-id'] = parseFloat(ev.target.getAttribute(lynxUniqueIdAttribute));
11
- const exposureID = exposureEvent.exposureID;
12
- if (ev.type === 'exposure') {
13
- exposureCache.push(exposureEvent);
14
- onScreen.set(exposureID, exposureEvent);
15
- }
16
- else {
17
- disexposureCache.push(exposureEvent);
18
- onScreen.delete(exposureID);
19
- }
20
- }
21
- setInterval(() => {
22
- if (exposureCache.length > 0 || disexposureCache.length > 0) {
23
- const currentExposure = exposureCache;
24
- const currentDisexposure = disexposureCache;
25
- exposureCache = [];
26
- disexposureCache = [];
27
- rpc.invoke(postExposureEndpoint, [{
28
- exposures: currentExposure,
29
- disExposures: currentDisexposure,
30
- }]);
31
- }
32
- }, 1000 / 20);
33
- shadowRoot.addEventListener('exposure', exposureEventHandler, {
34
- passive: true,
35
- });
36
- shadowRoot.addEventListener('disexposure', exposureEventHandler, {
37
- passive: true,
38
- });
39
- rpc.registerHandler(switchExposureService, async (enable, sendEvent) => {
40
- if (enable && !working) {
41
- // send all onScreen info
42
- rpc.invoke(postExposureEndpoint, [{
43
- exposures: [...onScreen.values()],
44
- disExposures: [],
45
- }]);
46
- }
47
- else if (!enable && working) {
48
- if (sendEvent) {
49
- rpc.invoke(postExposureEndpoint, [{
50
- exposures: [],
51
- disExposures: [...onScreen.values()],
52
- }]);
53
- }
54
- }
55
- });
56
- }
57
- //# sourceMappingURL=createExposureService.js.map
@@ -1,12 +0,0 @@
1
- import type { ElementOperation } from '@lynx-js/web-constants';
2
- import type { RuntimePropertyOnElement } from '../types/RuntimePropertyOnElement.js';
3
- export declare function decodeElementOperation<T extends HTMLElement & RuntimePropertyOnElement>(operations: ElementOperation[], options: {
4
- uniqueIdToElement: (WeakRef<T> | undefined)[];
5
- uniqueIdToCssInJsRule: (WeakRef<CSSStyleRule> | undefined)[];
6
- createElementImpl: (tag: string) => T;
7
- createStyleRuleImpl: (uniqueId: number, initialStyle: string) => CSSStyleRule;
8
- eventHandler: {
9
- mtsHandler: (event: Event) => void;
10
- btsHandler: (event: Event) => void;
11
- };
12
- }): T | undefined;
@@ -1,175 +0,0 @@
1
- // Copyright 2023 The Lynx Authors. All rights reserved.
2
- // Licensed under the Apache License Version 2.0 that can be found in the
3
- // LICENSE file in the root directory of this source tree.
4
- import { cssIdAttribute, lynxUniqueIdAttribute, OperationType, lynxRuntimeValue, LynxEventNameToW3cByTagName, LynxEventNameToW3cCommon, W3cEventNameToLynx, __lynx_timing_flag, lynxTagAttribute, } from '@lynx-js/web-constants';
5
- function getElement(uniqueId, uniqueIdToElement) {
6
- const element = uniqueIdToElement[uniqueId]?.deref();
7
- if (element) {
8
- return element;
9
- }
10
- else {
11
- throw new Error(`[lynx-web] cannot find element with uniqueId: ${uniqueId}`);
12
- }
13
- }
14
- function createElement(tag, uniqueId, uniqueIdToElement, createElementImpl) {
15
- const current = uniqueIdToElement[uniqueId]?.deref();
16
- if (current) {
17
- throw new Error(`[lynx-web] uniqueid is occupied: cannot create new element ${tag} with uniqueId: ${uniqueId}`);
18
- }
19
- const element = createElementImpl(tag);
20
- element.setAttribute(lynxUniqueIdAttribute, uniqueId.toString());
21
- uniqueIdToElement[uniqueId] = new WeakRef(element);
22
- return element;
23
- }
24
- function handleHtmlEvent(event) {
25
- const currentTarget = event.currentTarget;
26
- if (!currentTarget)
27
- return;
28
- const { eventHandler, } = currentTarget[lynxRuntimeValue];
29
- const lynxEventName = W3cEventNameToLynx[event.type] ?? event.type;
30
- const eventHandlerInfo = eventHandler[lynxEventName];
31
- if (eventHandlerInfo) {
32
- const { type: eventType, handler } = eventHandlerInfo;
33
- const isCatch = eventType === 'catchEvent' || eventType === 'capture-catch';
34
- handler(event);
35
- if (isCatch) {
36
- event.stopPropagation();
37
- }
38
- }
39
- }
40
- export function decodeElementOperation(operations, options) {
41
- const { uniqueIdToElement, uniqueIdToCssInJsRule, createElementImpl, createStyleRuleImpl, eventHandler, } = options;
42
- let pageElement;
43
- for (const op of operations) {
44
- if (op.type === OperationType.Create) {
45
- const element = createElement(op.tag, op.uid, uniqueIdToElement, createElementImpl);
46
- if (typeof op.cssId === 'number') {
47
- element.setAttribute(cssIdAttribute, op.cssId.toString());
48
- }
49
- }
50
- else {
51
- const target = getElement(op.uid, uniqueIdToElement);
52
- switch (op.type) {
53
- case OperationType.Append:
54
- {
55
- const children = op.cid.map(id => getElement(id, uniqueIdToElement));
56
- target.append(...children);
57
- }
58
- break;
59
- case OperationType.InsertBefore:
60
- {
61
- const child = getElement(op.cid, uniqueIdToElement);
62
- const ref = op.ref ? getElement(op.ref, uniqueIdToElement) : null;
63
- target.insertBefore(child, ref);
64
- }
65
- break;
66
- case OperationType.Remove:
67
- {
68
- for (const kidId of op.cid) {
69
- const kid = getElement(kidId, uniqueIdToElement);
70
- target.removeChild(kid);
71
- }
72
- }
73
- break;
74
- case OperationType.Replace:
75
- {
76
- const newElements = op.nid.map(id => getElement(id, uniqueIdToElement));
77
- target.replaceWith(...newElements);
78
- }
79
- break;
80
- case OperationType.SetAttribute:
81
- {
82
- if (op.value === null) {
83
- target.removeAttribute(op.key);
84
- }
85
- else {
86
- target.setAttribute(op.key, op.value);
87
- if (op.key === lynxTagAttribute && op.value === 'page') {
88
- pageElement = target;
89
- }
90
- }
91
- }
92
- break;
93
- case OperationType.SwapElement:
94
- {
95
- const targetB = getElement(op.tid, uniqueIdToElement);
96
- const temp = document.createElement('div');
97
- target.replaceWith(temp);
98
- targetB.replaceWith(target);
99
- temp.replaceWith(targetB);
100
- }
101
- break;
102
- case OperationType.SetProperty:
103
- target[lynxRuntimeValue][op.key] = op.value;
104
- if (op.key === 'dataset') {
105
- if (op.value) {
106
- for (const [key, value] of Object.entries(op.value)) {
107
- if (value) {
108
- target.setAttribute(`data-${key}`, value.toString());
109
- }
110
- else {
111
- target.removeAttribute(`data-${key}`);
112
- }
113
- }
114
- }
115
- else {
116
- target[lynxRuntimeValue]['dataset'] = {};
117
- }
118
- }
119
- break;
120
- case OperationType.SetDatasetProperty:
121
- target[lynxRuntimeValue].dataset[op.key] = op.value;
122
- if (op.value) {
123
- target.setAttribute(`data-${op.key}`, op.value.toString());
124
- }
125
- else {
126
- target.removeAttribute(`data-${op.key}`);
127
- }
128
- break;
129
- case OperationType.RegisterEventHandler:
130
- const isMtsHandler = op.hname === null;
131
- const lynxEventName = op.ename.toLowerCase();
132
- const htmlEventName = LynxEventNameToW3cByTagName[target.tagName]?.[lynxEventName]
133
- ?? LynxEventNameToW3cCommon[lynxEventName] ?? lynxEventName;
134
- const currentHandlerInfo = target[lynxRuntimeValue].eventHandler[lynxEventName];
135
- if (currentHandlerInfo) {
136
- target.removeEventListener(htmlEventName, handleHtmlEvent);
137
- }
138
- const isCaptureEvent = op.eventType === 'capture-bind'
139
- || op.eventType === 'capture-catch';
140
- if (op.hname === undefined) {
141
- target[lynxRuntimeValue].eventHandler[lynxEventName] = undefined;
142
- }
143
- else {
144
- target.addEventListener(htmlEventName, handleHtmlEvent, {
145
- passive: true,
146
- capture: isCaptureEvent,
147
- });
148
- target[lynxRuntimeValue].eventHandler[lynxEventName] = {
149
- type: op.eventType,
150
- handler: isMtsHandler
151
- ? eventHandler.mtsHandler
152
- : eventHandler.btsHandler,
153
- hname: op.hname ?? '',
154
- };
155
- }
156
- break;
157
- case OperationType.SetStyleProperty:
158
- target.style.setProperty(op.key, op.value, op.im ? '!important' : undefined);
159
- break;
160
- case OperationType.UpdateCssInJs:
161
- let rule = uniqueIdToCssInJsRule[op.uid]?.deref();
162
- if (rule) {
163
- rule.style.cssText = op.classStyleStr;
164
- }
165
- else {
166
- rule = createStyleRuleImpl(op.uid, op.classStyleStr);
167
- uniqueIdToCssInJsRule[op.uid] = new WeakRef(rule);
168
- }
169
- break;
170
- }
171
- }
172
- }
173
- return pageElement;
174
- }
175
- //# sourceMappingURL=decodeElementOperation.js.map
@@ -1,2 +0,0 @@
1
- import { type LynxCrossThreadEvent } from '@lynx-js/web-constants';
2
- export declare function createCrossThreadEvent(domEvent: Event): LynxCrossThreadEvent;
@@ -1,43 +0,0 @@
1
- // Copyright 2023 The Lynx Authors. All rights reserved.
2
- // Licensed under the Apache License Version 2.0 that can be found in the
3
- // LICENSE file in the root directory of this source tree.
4
- import { lynxRuntimeValue, lynxUniqueIdAttribute, W3cEventNameToLynx, } from '@lynx-js/web-constants';
5
- export function createCrossThreadEvent(domEvent) {
6
- const targetElement = domEvent.target;
7
- const currentTargetElement = domEvent
8
- .currentTarget;
9
- const type = domEvent.type;
10
- const params = {};
11
- if (type.match(/^transition/)) {
12
- Object.assign(params, {
13
- 'animation_type': 'keyframe-animation',
14
- 'animation_name': domEvent.propertyName,
15
- new_animator: true, // we support the new_animator only
16
- });
17
- }
18
- else if (type.match(/animation/)) {
19
- Object.assign(params, {
20
- 'animation_type': 'keyframe-animation',
21
- 'animation_name': domEvent.animationName,
22
- new_animator: true, // we support the new_animator only
23
- });
24
- }
25
- return {
26
- type: W3cEventNameToLynx[type] ?? type,
27
- timestamp: domEvent.timeStamp,
28
- target: {
29
- id: targetElement.id,
30
- dataset: targetElement[lynxRuntimeValue].dataset,
31
- uniqueId: parseFloat(targetElement.getAttribute(lynxUniqueIdAttribute)),
32
- },
33
- currentTarget: {
34
- id: currentTargetElement.id,
35
- dataset: currentTargetElement[lynxRuntimeValue]?.dataset ?? {},
36
- uniqueId: parseFloat(currentTargetElement.getAttribute?.(lynxUniqueIdAttribute)),
37
- },
38
- // @ts-expect-error
39
- detail: domEvent.detail ?? {},
40
- params,
41
- };
42
- }
43
- //# sourceMappingURL=createCrossThreadEvent.js.map