@rpascene/shared 0.30.8
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/README.md +9 -0
- package/dist/es/baseDB.mjs +109 -0
- package/dist/es/build/copy-static.mjs +29 -0
- package/dist/es/common.mjs +37 -0
- package/dist/es/constants/example-code.mjs +202 -0
- package/dist/es/constants/index.mjs +74 -0
- package/dist/es/env/basic.mjs +6 -0
- package/dist/es/env/constants.mjs +97 -0
- package/dist/es/env/decide-model-config.mjs +172 -0
- package/dist/es/env/global-config-manager.mjs +82 -0
- package/dist/es/env/helper.mjs +45 -0
- package/dist/es/env/index.mjs +5 -0
- package/dist/es/env/init-debug.mjs +18 -0
- package/dist/es/env/model-config-manager.mjs +99 -0
- package/dist/es/env/parse.mjs +69 -0
- package/dist/es/env/types.mjs +265 -0
- package/dist/es/env/utils.mjs +18 -0
- package/dist/es/extractor/constants.mjs +2 -0
- package/dist/es/extractor/cs_postmessage.mjs +61 -0
- package/dist/es/extractor/customLocator.mjs +646 -0
- package/dist/es/extractor/debug.mjs +6 -0
- package/dist/es/extractor/dom-util.mjs +92 -0
- package/dist/es/extractor/index.mjs +7 -0
- package/dist/es/extractor/locator.mjs +95 -0
- package/dist/es/extractor/tree.mjs +81 -0
- package/dist/es/extractor/util.mjs +244 -0
- package/dist/es/extractor/web-extractor.mjs +361 -0
- package/dist/es/img/box-select.mjs +184 -0
- package/dist/es/img/draw-box.mjs +42 -0
- package/dist/es/img/get-jimp.mjs +10 -0
- package/dist/es/img/get-photon.mjs +19 -0
- package/dist/es/img/get-sharp.mjs +11 -0
- package/dist/es/img/index.mjs +5 -0
- package/dist/es/img/info.mjs +32 -0
- package/dist/es/img/transform.mjs +192 -0
- package/dist/es/index.mjs +3 -0
- package/dist/es/logger.mjs +61 -0
- package/dist/es/node/fs.mjs +44 -0
- package/dist/es/node/index.mjs +1 -0
- package/dist/es/polyfills/async-hooks.mjs +2 -0
- package/dist/es/polyfills/index.mjs +1 -0
- package/dist/es/types/index.mjs +3 -0
- package/dist/es/us-keyboard-layout.mjs +1414 -0
- package/dist/es/us-keyboard-layout.mjs.LICENSE.txt +5 -0
- package/dist/es/utils.mjs +66 -0
- package/dist/lib/baseDB.js +149 -0
- package/dist/lib/build/copy-static.js +77 -0
- package/dist/lib/common.js +93 -0
- package/dist/lib/constants/example-code.js +239 -0
- package/dist/lib/constants/index.js +153 -0
- package/dist/lib/env/basic.js +40 -0
- package/dist/lib/env/constants.js +143 -0
- package/dist/lib/env/decide-model-config.js +212 -0
- package/dist/lib/env/global-config-manager.js +116 -0
- package/dist/lib/env/helper.js +85 -0
- package/dist/lib/env/index.js +94 -0
- package/dist/lib/env/init-debug.js +52 -0
- package/dist/lib/env/model-config-manager.js +133 -0
- package/dist/lib/env/parse.js +106 -0
- package/dist/lib/env/types.js +650 -0
- package/dist/lib/env/utils.js +61 -0
- package/dist/lib/extractor/constants.js +42 -0
- package/dist/lib/extractor/cs_postmessage.js +98 -0
- package/dist/lib/extractor/customLocator.js +698 -0
- package/dist/lib/extractor/debug.js +12 -0
- package/dist/lib/extractor/dom-util.js +150 -0
- package/dist/lib/extractor/index.js +153 -0
- package/dist/lib/extractor/locator.js +141 -0
- package/dist/lib/extractor/tree.js +127 -0
- package/dist/lib/extractor/util.js +335 -0
- package/dist/lib/extractor/web-extractor.js +407 -0
- package/dist/lib/img/box-select.js +232 -0
- package/dist/lib/img/draw-box.js +89 -0
- package/dist/lib/img/get-jimp.js +72 -0
- package/dist/lib/img/get-photon.js +76 -0
- package/dist/lib/img/get-sharp.js +63 -0
- package/dist/lib/img/index.js +102 -0
- package/dist/lib/img/info.js +86 -0
- package/dist/lib/img/transform.js +279 -0
- package/dist/lib/index.js +43 -0
- package/dist/lib/logger.js +114 -0
- package/dist/lib/node/fs.js +97 -0
- package/dist/lib/node/index.js +60 -0
- package/dist/lib/polyfills/async-hooks.js +36 -0
- package/dist/lib/polyfills/index.js +60 -0
- package/dist/lib/types/index.js +37 -0
- package/dist/lib/us-keyboard-layout.js +1457 -0
- package/dist/lib/us-keyboard-layout.js.LICENSE.txt +5 -0
- package/dist/lib/utils.js +136 -0
- package/dist/types/baseDB.d.ts +25 -0
- package/dist/types/build/copy-static.d.ts +31 -0
- package/dist/types/common.d.ts +12 -0
- package/dist/types/constants/example-code.d.ts +2 -0
- package/dist/types/constants/index.d.ts +23 -0
- package/dist/types/env/basic.d.ts +6 -0
- package/dist/types/env/constants.d.ts +40 -0
- package/dist/types/env/decide-model-config.d.ts +14 -0
- package/dist/types/env/global-config-manager.d.ts +32 -0
- package/dist/types/env/helper.d.ts +6 -0
- package/dist/types/env/index.d.ts +4 -0
- package/dist/types/env/init-debug.d.ts +1 -0
- package/dist/types/env/model-config-manager.d.ts +24 -0
- package/dist/types/env/parse.d.ts +12 -0
- package/dist/types/env/types.d.ts +295 -0
- package/dist/types/env/utils.d.ts +7 -0
- package/dist/types/extractor/constants.d.ts +1 -0
- package/dist/types/extractor/cs_postmessage.d.ts +2 -0
- package/dist/types/extractor/customLocator.d.ts +69 -0
- package/dist/types/extractor/debug.d.ts +1 -0
- package/dist/types/extractor/dom-util.d.ts +26 -0
- package/dist/types/extractor/index.d.ts +36 -0
- package/dist/types/extractor/locator.d.ts +7 -0
- package/dist/types/extractor/tree.d.ts +9 -0
- package/dist/types/extractor/util.d.ts +43 -0
- package/dist/types/extractor/web-extractor.d.ts +19 -0
- package/dist/types/img/box-select.d.ts +25 -0
- package/dist/types/img/draw-box.d.ts +15 -0
- package/dist/types/img/get-jimp.d.ts +2 -0
- package/dist/types/img/get-photon.d.ts +8 -0
- package/dist/types/img/get-sharp.d.ts +3 -0
- package/dist/types/img/index.d.ts +4 -0
- package/dist/types/img/info.d.ts +29 -0
- package/dist/types/img/transform.d.ts +88 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/logger.d.ts +4 -0
- package/dist/types/node/fs.d.ts +15 -0
- package/dist/types/node/index.d.ts +1 -0
- package/dist/types/polyfills/async-hooks.d.ts +6 -0
- package/dist/types/polyfills/index.d.ts +4 -0
- package/dist/types/types/index.d.ts +37 -0
- package/dist/types/us-keyboard-layout.d.ts +32 -0
- package/dist/types/utils.d.ts +22 -0
- package/package.json +102 -0
- package/src/baseDB.ts +158 -0
- package/src/build/copy-static.ts +62 -0
- package/src/common.ts +67 -0
- package/src/constants/example-code.ts +202 -0
- package/src/constants/index.ts +81 -0
- package/src/env/basic.ts +12 -0
- package/src/env/constants.ts +291 -0
- package/src/env/decide-model-config.ts +319 -0
- package/src/env/global-config-manager.ts +174 -0
- package/src/env/helper.ts +80 -0
- package/src/env/index.ts +4 -0
- package/src/env/init-debug.ts +29 -0
- package/src/env/model-config-manager.ts +145 -0
- package/src/env/parse.ts +131 -0
- package/src/env/types.ts +573 -0
- package/src/env/utils.ts +39 -0
- package/src/extractor/constants.ts +5 -0
- package/src/extractor/cs_postmessage.ts +101 -0
- package/src/extractor/customLocator.ts +1138 -0
- package/src/extractor/debug.ts +10 -0
- package/src/extractor/dom-util.ts +141 -0
- package/src/extractor/index.ts +54 -0
- package/src/extractor/locator.ts +179 -0
- package/src/extractor/tree.ts +179 -0
- package/src/extractor/util.ts +468 -0
- package/src/extractor/web-extractor.ts +559 -0
- package/src/img/box-select.ts +346 -0
- package/src/img/draw-box.ts +60 -0
- package/src/img/get-jimp.ts +12 -0
- package/src/img/get-photon.ts +48 -0
- package/src/img/get-sharp.ts +18 -0
- package/src/img/index.ts +24 -0
- package/src/img/info.ts +79 -0
- package/src/img/jimp.d.ts +4 -0
- package/src/img/transform.ts +396 -0
- package/src/index.ts +6 -0
- package/src/logger.ts +93 -0
- package/src/node/fs.ts +84 -0
- package/src/node/index.ts +1 -0
- package/src/polyfills/async-hooks.ts +6 -0
- package/src/polyfills/index.ts +4 -0
- package/src/types/index.ts +53 -0
- package/src/us-keyboard-layout.ts +723 -0
- package/src/utils.ts +127 -0
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CONTAINER_MINI_HEIGHT,
|
|
3
|
+
CONTAINER_MINI_WIDTH,
|
|
4
|
+
NodeType,
|
|
5
|
+
} from '../constants/index';
|
|
6
|
+
import type { WebElementInfo } from '../types';
|
|
7
|
+
import type { Point } from '../types';
|
|
8
|
+
import {
|
|
9
|
+
isAElement,
|
|
10
|
+
isButtonElement,
|
|
11
|
+
isContainerElement,
|
|
12
|
+
isFormElement,
|
|
13
|
+
isImgElement,
|
|
14
|
+
isTextElement,
|
|
15
|
+
} from './dom-util';
|
|
16
|
+
import { descriptionOfTree } from './tree';
|
|
17
|
+
import {
|
|
18
|
+
elementRect,
|
|
19
|
+
getNodeAttributes,
|
|
20
|
+
getPseudoElementContent,
|
|
21
|
+
getRect,
|
|
22
|
+
getTopDocument,
|
|
23
|
+
logger,
|
|
24
|
+
rpasceneGenerateHash,
|
|
25
|
+
setDebugMode,
|
|
26
|
+
} from './util';
|
|
27
|
+
import { postWindowMessage, onWindowMessage } from './cs_postmessage';
|
|
28
|
+
|
|
29
|
+
import {
|
|
30
|
+
getContainerPath,
|
|
31
|
+
getLocators
|
|
32
|
+
} from './customLocator';
|
|
33
|
+
|
|
34
|
+
let indexId = 0;
|
|
35
|
+
|
|
36
|
+
function tagNameOfNode(node: globalThis.Node, currentWindow = globalThis): string {
|
|
37
|
+
let tagName = '';
|
|
38
|
+
if (node instanceof currentWindow.HTMLElement) {
|
|
39
|
+
tagName = node.tagName?.toLowerCase();
|
|
40
|
+
} else {
|
|
41
|
+
const parentElement = node.parentElement;
|
|
42
|
+
if (parentElement && parentElement instanceof currentWindow.HTMLElement) {
|
|
43
|
+
tagName = parentElement.tagName?.toLowerCase();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return tagName ? `<${tagName}>` : '';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function collectElementInfo(
|
|
51
|
+
node: Node,
|
|
52
|
+
currentWindow: typeof window,
|
|
53
|
+
currentDocument: typeof document,
|
|
54
|
+
baseZoom = 1,
|
|
55
|
+
basePoint: Point = { left: 0, top: 0 },
|
|
56
|
+
isContainer = false, // if true, the element will be considered as a container
|
|
57
|
+
): WebElementInfo | null | any {
|
|
58
|
+
const rect = elementRect(node, currentWindow, currentDocument, baseZoom);
|
|
59
|
+
|
|
60
|
+
if (!rect) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (
|
|
65
|
+
rect.width < CONTAINER_MINI_WIDTH ||
|
|
66
|
+
rect.height < CONTAINER_MINI_HEIGHT
|
|
67
|
+
) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (basePoint.left !== 0 || basePoint.top !== 0) {
|
|
72
|
+
rect.left += basePoint.left;
|
|
73
|
+
rect.top += basePoint.top;
|
|
74
|
+
}
|
|
75
|
+
// Skip elements that cover the entire viewport, as they are likely background containers
|
|
76
|
+
// rather than meaningful interactive elements
|
|
77
|
+
if (rect.height >= window.innerHeight && rect.width >= window.innerWidth) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
if (isFormElement(node, currentWindow)) {
|
|
83
|
+
const attributes = getNodeAttributes(node, currentWindow);
|
|
84
|
+
let valueContent =
|
|
85
|
+
attributes.value || attributes.placeholder || node.textContent || '';
|
|
86
|
+
const nodeHashId = rpasceneGenerateHash(node, valueContent, rect);
|
|
87
|
+
const tagName = (node as HTMLElement).tagName.toLowerCase();
|
|
88
|
+
if ((node as HTMLElement).tagName.toLowerCase() === 'select') {
|
|
89
|
+
// Get the selected option using the selectedIndex property
|
|
90
|
+
const selectedOption = (node as HTMLSelectElement).options[
|
|
91
|
+
(node as HTMLSelectElement).selectedIndex
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
// Retrieve the text content of the selected option
|
|
95
|
+
valueContent = selectedOption?.textContent || '';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (
|
|
99
|
+
((node as HTMLElement).tagName.toLowerCase() === 'input' ||
|
|
100
|
+
(node as HTMLElement).tagName.toLowerCase() === 'textarea') &&
|
|
101
|
+
(node as HTMLInputElement).value
|
|
102
|
+
) {
|
|
103
|
+
valueContent = (node as HTMLInputElement).value;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const elementInfo: WebElementInfo = {
|
|
107
|
+
id: nodeHashId,
|
|
108
|
+
nodeHashId,
|
|
109
|
+
nodeType: NodeType.FORM_ITEM,
|
|
110
|
+
indexId: indexId++,
|
|
111
|
+
attributes: {
|
|
112
|
+
...attributes,
|
|
113
|
+
htmlTagName: `<${tagName}>`,
|
|
114
|
+
nodeType: NodeType.FORM_ITEM,
|
|
115
|
+
},
|
|
116
|
+
content: valueContent.trim(),
|
|
117
|
+
rect,
|
|
118
|
+
center: [
|
|
119
|
+
Math.round(rect.left + rect.width / 2),
|
|
120
|
+
Math.round(rect.top + rect.height / 2),
|
|
121
|
+
],
|
|
122
|
+
zoom: rect.zoom,
|
|
123
|
+
isVisible: rect.isVisible
|
|
124
|
+
};
|
|
125
|
+
return elementInfo;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (isButtonElement(node, currentWindow)) {
|
|
129
|
+
const rect = mergeElementAndChildrenRects(
|
|
130
|
+
node,
|
|
131
|
+
currentWindow,
|
|
132
|
+
currentDocument,
|
|
133
|
+
baseZoom,
|
|
134
|
+
);
|
|
135
|
+
if (!rect) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
const attributes = getNodeAttributes(node, currentWindow);
|
|
139
|
+
const pseudo = getPseudoElementContent(node, currentWindow);
|
|
140
|
+
const content = node.innerText || pseudo.before || pseudo.after || '';
|
|
141
|
+
const nodeHashId = rpasceneGenerateHash(node, content, rect);
|
|
142
|
+
const elementInfo: WebElementInfo = {
|
|
143
|
+
id: nodeHashId,
|
|
144
|
+
indexId: indexId++,
|
|
145
|
+
nodeHashId,
|
|
146
|
+
nodeType: NodeType.BUTTON,
|
|
147
|
+
attributes: {
|
|
148
|
+
...attributes,
|
|
149
|
+
htmlTagName: tagNameOfNode(node, currentWindow),
|
|
150
|
+
nodeType: NodeType.BUTTON,
|
|
151
|
+
},
|
|
152
|
+
content,
|
|
153
|
+
rect,
|
|
154
|
+
center: [
|
|
155
|
+
Math.round(rect.left + rect.width / 2),
|
|
156
|
+
Math.round(rect.top + rect.height / 2),
|
|
157
|
+
],
|
|
158
|
+
zoom: rect.zoom,
|
|
159
|
+
isVisible: rect.isVisible
|
|
160
|
+
};
|
|
161
|
+
return elementInfo;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (isImgElement(node, currentWindow)) {
|
|
165
|
+
const attributes = getNodeAttributes(node, currentWindow);
|
|
166
|
+
const nodeHashId = rpasceneGenerateHash(node, '', rect);
|
|
167
|
+
const elementInfo: WebElementInfo = {
|
|
168
|
+
id: nodeHashId,
|
|
169
|
+
indexId: indexId++,
|
|
170
|
+
nodeHashId,
|
|
171
|
+
attributes: {
|
|
172
|
+
...attributes,
|
|
173
|
+
...(node.nodeName?.toLowerCase() === 'svg'
|
|
174
|
+
? {
|
|
175
|
+
svgContent: 'true',
|
|
176
|
+
}
|
|
177
|
+
: {}),
|
|
178
|
+
nodeType: NodeType.IMG,
|
|
179
|
+
htmlTagName: tagNameOfNode(node, currentWindow),
|
|
180
|
+
},
|
|
181
|
+
nodeType: NodeType.IMG,
|
|
182
|
+
content: '',
|
|
183
|
+
rect,
|
|
184
|
+
center: [
|
|
185
|
+
Math.round(rect.left + rect.width / 2),
|
|
186
|
+
Math.round(rect.top + rect.height / 2),
|
|
187
|
+
],
|
|
188
|
+
zoom: rect.zoom,
|
|
189
|
+
isVisible: rect.isVisible
|
|
190
|
+
};
|
|
191
|
+
return elementInfo;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (isTextElement(node, currentWindow)) {
|
|
195
|
+
const text = node.textContent?.trim().replace(/\n+/g, ' ');
|
|
196
|
+
if (!text) {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
const attributes = getNodeAttributes(node, currentWindow);
|
|
200
|
+
const attributeKeys = Object.keys(attributes);
|
|
201
|
+
if (!text.trim() && attributeKeys.length === 0) {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
const nodeHashId = rpasceneGenerateHash(node, text, rect);
|
|
205
|
+
const elementInfo: WebElementInfo = {
|
|
206
|
+
id: nodeHashId,
|
|
207
|
+
indexId: indexId++,
|
|
208
|
+
nodeHashId,
|
|
209
|
+
nodeType: NodeType.TEXT,
|
|
210
|
+
attributes: {
|
|
211
|
+
...attributes,
|
|
212
|
+
nodeType: NodeType.TEXT,
|
|
213
|
+
htmlTagName: tagNameOfNode(node, currentWindow),
|
|
214
|
+
},
|
|
215
|
+
center: [
|
|
216
|
+
Math.round(rect.left + rect.width / 2),
|
|
217
|
+
Math.round(rect.top + rect.height / 2),
|
|
218
|
+
],
|
|
219
|
+
content: text,
|
|
220
|
+
rect,
|
|
221
|
+
zoom: rect.zoom,
|
|
222
|
+
isVisible: rect.isVisible
|
|
223
|
+
};
|
|
224
|
+
return elementInfo;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (isAElement(node, currentWindow)) {
|
|
228
|
+
const attributes = getNodeAttributes(node, currentWindow);
|
|
229
|
+
const pseudo = getPseudoElementContent(node, currentWindow);
|
|
230
|
+
const content = node.innerText || pseudo.before || pseudo.after || '';
|
|
231
|
+
const nodeHashId = rpasceneGenerateHash(node, content, rect);
|
|
232
|
+
const elementInfo: WebElementInfo = {
|
|
233
|
+
id: nodeHashId,
|
|
234
|
+
indexId: indexId++,
|
|
235
|
+
nodeHashId,
|
|
236
|
+
nodeType: NodeType.A,
|
|
237
|
+
attributes: {
|
|
238
|
+
...attributes,
|
|
239
|
+
htmlTagName: tagNameOfNode(node, currentWindow),
|
|
240
|
+
nodeType: NodeType.A,
|
|
241
|
+
},
|
|
242
|
+
content,
|
|
243
|
+
rect,
|
|
244
|
+
center: [
|
|
245
|
+
Math.round(rect.left + rect.width / 2),
|
|
246
|
+
Math.round(rect.top + rect.height / 2),
|
|
247
|
+
],
|
|
248
|
+
zoom: rect.zoom,
|
|
249
|
+
isVisible: rect.isVisible
|
|
250
|
+
};
|
|
251
|
+
return elementInfo;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// else, consider as a container
|
|
255
|
+
// 视为容器
|
|
256
|
+
if (isContainerElement(node, currentWindow) || isContainer) {
|
|
257
|
+
const attributes = getNodeAttributes(node, currentWindow);
|
|
258
|
+
const nodeHashId = rpasceneGenerateHash(node, '', rect);
|
|
259
|
+
const elementInfo: WebElementInfo = {
|
|
260
|
+
id: nodeHashId,
|
|
261
|
+
nodeHashId,
|
|
262
|
+
indexId: indexId++,
|
|
263
|
+
nodeType: NodeType.CONTAINER,
|
|
264
|
+
attributes: {
|
|
265
|
+
...attributes,
|
|
266
|
+
nodeType: NodeType.CONTAINER,
|
|
267
|
+
htmlTagName: tagNameOfNode(node, currentWindow),
|
|
268
|
+
},
|
|
269
|
+
content: '',
|
|
270
|
+
rect,
|
|
271
|
+
center: [
|
|
272
|
+
Math.round(rect.left + rect.width / 2),
|
|
273
|
+
Math.round(rect.top + rect.height / 2),
|
|
274
|
+
],
|
|
275
|
+
zoom: rect.zoom,
|
|
276
|
+
isVisible: rect.isVisible
|
|
277
|
+
};
|
|
278
|
+
return elementInfo;
|
|
279
|
+
}
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
interface WebElementNode {
|
|
284
|
+
node: WebElementInfo | null;
|
|
285
|
+
children: WebElementNode[];
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// 提取带位置的文本
|
|
289
|
+
// @deprecated
|
|
290
|
+
export async function extractTextWithPosition(
|
|
291
|
+
initNode: globalThis.Node,
|
|
292
|
+
debugMode = false,
|
|
293
|
+
): Promise<WebElementInfo[]> {
|
|
294
|
+
const elementNode = await extractTreeNode(initNode, debugMode);
|
|
295
|
+
|
|
296
|
+
// dfs topChildren
|
|
297
|
+
const elementInfoArray: WebElementInfo[] = [];
|
|
298
|
+
function dfsTopChildren(node: WebElementNode) {
|
|
299
|
+
if (node.node) {
|
|
300
|
+
elementInfoArray.push(node.node);
|
|
301
|
+
}
|
|
302
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
303
|
+
dfsTopChildren(node.children[i]);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
dfsTopChildren({ children: elementNode.children, node: elementNode.node });
|
|
307
|
+
|
|
308
|
+
console.log(elementInfoArray, 'elementInfoArray')
|
|
309
|
+
return elementInfoArray;
|
|
310
|
+
}
|
|
311
|
+
// 提取树节点为字符串
|
|
312
|
+
export async function extractTreeNodeAsString(
|
|
313
|
+
initNode: globalThis.Node,
|
|
314
|
+
visibleOnly = false,
|
|
315
|
+
debugMode = false,
|
|
316
|
+
): Promise<string> {
|
|
317
|
+
const elementNode = await extractTreeNode(initNode, debugMode);
|
|
318
|
+
return descriptionOfTree(elementNode, undefined, false, visibleOnly);
|
|
319
|
+
}
|
|
320
|
+
// 提取树节点
|
|
321
|
+
export async function extractTreeNode(
|
|
322
|
+
initNode: globalThis.Node,
|
|
323
|
+
debugMode: boolean = false,
|
|
324
|
+
basePoint: Point = { left: 0, top: 0 },
|
|
325
|
+
baseZoom: number = 1,
|
|
326
|
+
containerPaths: any[] = []
|
|
327
|
+
): Promise<WebElementNode> {
|
|
328
|
+
setDebugMode(debugMode);
|
|
329
|
+
indexId = 0;
|
|
330
|
+
let containerPathClone = [...containerPaths]
|
|
331
|
+
const topDocument = getTopDocument();
|
|
332
|
+
const startNode = initNode || topDocument;
|
|
333
|
+
const topChildren: WebElementNode[] = [];
|
|
334
|
+
|
|
335
|
+
// 递归函数dfs
|
|
336
|
+
function dfs(
|
|
337
|
+
node: globalThis.Node,
|
|
338
|
+
currentWindow: typeof globalThis.window,
|
|
339
|
+
currentDocument: typeof globalThis.document,
|
|
340
|
+
baseZoom = 1,
|
|
341
|
+
basePoint: Point = { left: 0, top: 0 },
|
|
342
|
+
): WebElementNode | WebElementNode[] | null {
|
|
343
|
+
if (!node) {
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (node.nodeType && node.nodeType === 10) {
|
|
348
|
+
// Doctype node
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const elementInfo = collectElementInfo(
|
|
353
|
+
node,
|
|
354
|
+
currentWindow,
|
|
355
|
+
currentDocument,
|
|
356
|
+
baseZoom,
|
|
357
|
+
basePoint,
|
|
358
|
+
false
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
if (elementInfo) {
|
|
362
|
+
const allPaths = getLocators(node);
|
|
363
|
+
elementInfo.allPaths = allPaths;
|
|
364
|
+
elementInfo.containerPaths = containerPathClone;
|
|
365
|
+
}
|
|
366
|
+
if (node instanceof currentWindow.HTMLIFrameElement) {
|
|
367
|
+
if (
|
|
368
|
+
(node as HTMLIFrameElement).contentWindow &&
|
|
369
|
+
(node as HTMLIFrameElement).contentWindow
|
|
370
|
+
) {
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const nodeInfo: WebElementNode = {
|
|
376
|
+
node: elementInfo,
|
|
377
|
+
children: [],
|
|
378
|
+
};
|
|
379
|
+
// stop collecting if the node is a Button/Image/Text/FormItem/Container
|
|
380
|
+
if (
|
|
381
|
+
elementInfo?.nodeType === NodeType.BUTTON ||
|
|
382
|
+
elementInfo?.nodeType === NodeType.IMG ||
|
|
383
|
+
elementInfo?.nodeType === NodeType.TEXT ||
|
|
384
|
+
elementInfo?.nodeType === NodeType.FORM_ITEM ||
|
|
385
|
+
elementInfo?.nodeType === NodeType.CONTAINER // TODO: need return the container node?
|
|
386
|
+
) {
|
|
387
|
+
return nodeInfo;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const rect = getRect(node, baseZoom, currentWindow);
|
|
391
|
+
for (let i = 0; i < node.childNodes.length; i++) {
|
|
392
|
+
logger('will dfs', node.childNodes[i]);
|
|
393
|
+
const childNodeInfo = dfs(
|
|
394
|
+
node.childNodes[i],
|
|
395
|
+
currentWindow,
|
|
396
|
+
currentDocument,
|
|
397
|
+
rect.zoom,
|
|
398
|
+
basePoint,
|
|
399
|
+
);
|
|
400
|
+
if (Array.isArray(childNodeInfo)) {
|
|
401
|
+
// if the recursive return is an array, expand and merge it into children
|
|
402
|
+
nodeInfo.children.push(...childNodeInfo);
|
|
403
|
+
} else if (childNodeInfo) {
|
|
404
|
+
nodeInfo.children.push(childNodeInfo);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// if nodeInfo.node is null
|
|
409
|
+
if (nodeInfo.node === null) {
|
|
410
|
+
if (nodeInfo.children.length === 0) {
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
// promote children to the upper layer
|
|
414
|
+
return nodeInfo.children;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
return nodeInfo;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* 获取css样式中元素的transform: scale(.8);
|
|
422
|
+
* @param {*} ele
|
|
423
|
+
* @returns
|
|
424
|
+
*/
|
|
425
|
+
function getCssScale(ele: any) {
|
|
426
|
+
try {
|
|
427
|
+
const { width, height } = ele.getBoundingClientRect();
|
|
428
|
+
return {
|
|
429
|
+
cssScaleX: Number((width / ele.offsetWidth).toFixed(1)),
|
|
430
|
+
cssScaleY: Number((height / ele.offsetHeight).toFixed(1)),
|
|
431
|
+
};
|
|
432
|
+
} catch (error) {
|
|
433
|
+
return {
|
|
434
|
+
cssScaleX: 1,
|
|
435
|
+
cssScaleY: 1,
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const rootNodeInfo = dfs(startNode, window, document, baseZoom, basePoint);
|
|
441
|
+
if (Array.isArray(rootNodeInfo)) {
|
|
442
|
+
topChildren.push(...rootNodeInfo);
|
|
443
|
+
} else if (rootNodeInfo) {
|
|
444
|
+
topChildren.push(rootNodeInfo);
|
|
445
|
+
}
|
|
446
|
+
if (startNode === topDocument) {
|
|
447
|
+
// find all the same-origin iframes
|
|
448
|
+
const iframes = document.querySelectorAll('iframe');
|
|
449
|
+
for (let i = 0; i < iframes.length; i++) {
|
|
450
|
+
const iframe = iframes[i];
|
|
451
|
+
const iframeInfo = collectElementInfo(iframe, window, document, 1);
|
|
452
|
+
const baseZoom = getCssScale(iframe).cssScaleX;
|
|
453
|
+
const containerPath = getContainerPath(iframe);
|
|
454
|
+
containerPathClone = containerPathClone.concat(containerPath)
|
|
455
|
+
if (iframeInfo) {
|
|
456
|
+
if (iframe.contentDocument && iframe.contentWindow) {
|
|
457
|
+
console.log('iframe同域')
|
|
458
|
+
// when the iframe is in the viewport, we need to collect its children
|
|
459
|
+
const iframeChildren = dfs(
|
|
460
|
+
iframe.contentDocument.body,
|
|
461
|
+
iframe.contentWindow as any,
|
|
462
|
+
iframe.contentDocument,
|
|
463
|
+
baseZoom,
|
|
464
|
+
{
|
|
465
|
+
left: iframeInfo.rect.left,
|
|
466
|
+
top: iframeInfo.rect.top,
|
|
467
|
+
},
|
|
468
|
+
);
|
|
469
|
+
if (Array.isArray(iframeChildren)) {
|
|
470
|
+
topChildren.push(...iframeChildren);
|
|
471
|
+
} else if (iframeChildren) {
|
|
472
|
+
topChildren.push(iframeChildren);
|
|
473
|
+
}
|
|
474
|
+
} else {
|
|
475
|
+
console.log(iframeInfo, 'iframe跨域了')
|
|
476
|
+
|
|
477
|
+
// 跨域了,请求获取tree
|
|
478
|
+
const iframeChildren = await postWindowMessage(iframe.contentWindow, window, {
|
|
479
|
+
action: 'extractTreeNode',
|
|
480
|
+
data: {
|
|
481
|
+
basePoint: {
|
|
482
|
+
left: iframeInfo.rect.left,
|
|
483
|
+
top: iframeInfo.rect.top,
|
|
484
|
+
},
|
|
485
|
+
baseZoom,
|
|
486
|
+
containerPaths: containerPathClone
|
|
487
|
+
},
|
|
488
|
+
})
|
|
489
|
+
if (Array.isArray(iframeChildren)) {
|
|
490
|
+
topChildren.push(...iframeChildren);
|
|
491
|
+
} else if (iframeChildren) {
|
|
492
|
+
topChildren.push(iframeChildren);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
return {
|
|
501
|
+
node: null,
|
|
502
|
+
children: topChildren,
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
// 合并元素及其子元素矩形
|
|
506
|
+
export function mergeElementAndChildrenRects(
|
|
507
|
+
node: Node,
|
|
508
|
+
currentWindow: typeof window,
|
|
509
|
+
currentDocument: typeof document,
|
|
510
|
+
baseZoom = 1,
|
|
511
|
+
) {
|
|
512
|
+
const selfRect = elementRect(node, currentWindow, currentDocument, baseZoom);
|
|
513
|
+
if (!selfRect) return null;
|
|
514
|
+
|
|
515
|
+
let minLeft = selfRect.left;
|
|
516
|
+
let minTop = selfRect.top;
|
|
517
|
+
let maxRight = selfRect.left + selfRect.width;
|
|
518
|
+
let maxBottom = selfRect.top + selfRect.height;
|
|
519
|
+
|
|
520
|
+
function traverse(child: Node) {
|
|
521
|
+
for (let i = 0; i < child.childNodes.length; i++) {
|
|
522
|
+
const sub = child.childNodes[i];
|
|
523
|
+
if (sub.nodeType === 1) {
|
|
524
|
+
const rect = elementRect(sub, currentWindow, currentDocument, baseZoom);
|
|
525
|
+
if (rect) {
|
|
526
|
+
minLeft = Math.min(minLeft, rect.left);
|
|
527
|
+
minTop = Math.min(minTop, rect.top);
|
|
528
|
+
maxRight = Math.max(maxRight, rect.left + rect.width);
|
|
529
|
+
maxBottom = Math.max(maxBottom, rect.top + rect.height);
|
|
530
|
+
}
|
|
531
|
+
traverse(sub);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
traverse(node);
|
|
536
|
+
|
|
537
|
+
return {
|
|
538
|
+
...selfRect,
|
|
539
|
+
left: minLeft,
|
|
540
|
+
top: minTop,
|
|
541
|
+
width: maxRight - minLeft,
|
|
542
|
+
height: maxBottom - minTop,
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
if (typeof window !== 'undefined') {
|
|
547
|
+
onWindowMessage(window, async ({ action, data }: any, { source }: any) => {
|
|
548
|
+
if (action === 'extractTreeNode') {
|
|
549
|
+
// @ts-ignore
|
|
550
|
+
try {
|
|
551
|
+
return await extractTreeNode(document.body, false, data.basePoint, data.baseZoom, data.containerPaths)
|
|
552
|
+
} catch (error) {
|
|
553
|
+
return {
|
|
554
|
+
error: 'error message'
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
})
|
|
559
|
+
}
|