glass-easel-devtools-agent 0.9.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/.eslintignore +2 -0
- package/dist/backend.d.ts +21 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.js +2 -0
- package/dist/index.js.LICENSE.txt +1 -0
- package/dist/mount_point.d.ts +160 -0
- package/dist/overlay.d.ts +37 -0
- package/dist/protocol/css.d.ts +240 -0
- package/dist/protocol/dom.d.ts +463 -0
- package/dist/protocol/index.d.ts +48 -0
- package/dist/protocol/overlay.d.ts +64 -0
- package/dist/protocol/var.d.ts +15 -0
- package/dist/utils.d.ts +5 -0
- package/package.json +21 -0
- package/src/backend.ts +233 -0
- package/src/index.ts +115 -0
- package/src/mount_point.ts +899 -0
- package/src/overlay.ts +207 -0
- package/src/overlay.wxml +40 -0
- package/src/protocol/css.ts +222 -0
- package/src/protocol/dom.ts +392 -0
- package/src/protocol/index.ts +52 -0
- package/src/protocol/overlay.ts +62 -0
- package/src/protocol/var.ts +37 -0
- package/src/utils.ts +20 -0
- package/tsconfig.json +12 -0
- package/webpack.config.js +49 -0
- package/webpack.dev.config.js +12 -0
- package/wxml_loader.js +14 -0
|
@@ -0,0 +1,899 @@
|
|
|
1
|
+
import * as glassEasel from 'glass-easel'
|
|
2
|
+
import { type protocol, type Connection } from '.'
|
|
3
|
+
import {
|
|
4
|
+
type GlassEaselVar,
|
|
5
|
+
glassEaselVarToString,
|
|
6
|
+
toGlassEaselVar,
|
|
7
|
+
type NodeId,
|
|
8
|
+
type dom,
|
|
9
|
+
} from './protocol'
|
|
10
|
+
import { GlassEaselNodeType, glassEaselNodeTypeToCDP } from './protocol/dom'
|
|
11
|
+
import * as backendUtils from './backend'
|
|
12
|
+
import { warn } from './utils'
|
|
13
|
+
|
|
14
|
+
export type NodeMeta = {
|
|
15
|
+
node: glassEasel.Node
|
|
16
|
+
nodeId: NodeId
|
|
17
|
+
childNodesSent: boolean
|
|
18
|
+
observer: glassEasel.MutationObserver
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const enum StaticNodeName {
|
|
22
|
+
Document = '#document',
|
|
23
|
+
TextNode = '#text',
|
|
24
|
+
ShadowRoot = '#shadow-root',
|
|
25
|
+
Slot = 'SLOT',
|
|
26
|
+
Unknown = 'UNKNOWN',
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const getNodeType = (node: glassEasel.Node): GlassEaselNodeType => {
|
|
30
|
+
if (node.asTextNode()) return GlassEaselNodeType.TextNode
|
|
31
|
+
if (node.asNativeNode()) return GlassEaselNodeType.NativeNode
|
|
32
|
+
if (node.asVirtualNode()) return GlassEaselNodeType.VirtualNode
|
|
33
|
+
if (node.asGeneralComponent()) return GlassEaselNodeType.Component
|
|
34
|
+
return GlassEaselNodeType.Unknown
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const getNodeName = (
|
|
38
|
+
node: glassEasel.Node,
|
|
39
|
+
nodeType: GlassEaselNodeType,
|
|
40
|
+
local: boolean,
|
|
41
|
+
): string => {
|
|
42
|
+
if (nodeType === GlassEaselNodeType.TextNode) return StaticNodeName.TextNode
|
|
43
|
+
if (nodeType === GlassEaselNodeType.NativeNode) return node.asNativeNode()!.is
|
|
44
|
+
if (nodeType === GlassEaselNodeType.Component) {
|
|
45
|
+
const comp = node.asGeneralComponent()!
|
|
46
|
+
return local ? comp.is : comp.tagName
|
|
47
|
+
}
|
|
48
|
+
if (nodeType === GlassEaselNodeType.VirtualNode) return node.asVirtualNode()!.is
|
|
49
|
+
return StaticNodeName.Unknown
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export class MountPointsManager {
|
|
53
|
+
private conn: Connection
|
|
54
|
+
private nodeIdMap = new WeakMap<glassEasel.Node, NodeId>()
|
|
55
|
+
private activeNodes = Object.create(null) as Record<NodeId, NodeMeta>
|
|
56
|
+
private activeBackendNodes = Object.create(null) as Record<NodeId, WeakRef<glassEasel.Node>>
|
|
57
|
+
readonly documentNodeId = 1
|
|
58
|
+
private nodeIdInc = 2
|
|
59
|
+
private mountPoints: { nodeMeta: NodeMeta; env: glassEasel.MountPointEnv }[] = []
|
|
60
|
+
private selectedNodeId = 0
|
|
61
|
+
|
|
62
|
+
constructor(conn: Connection) {
|
|
63
|
+
this.conn = conn
|
|
64
|
+
this.init()
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
init() {
|
|
68
|
+
this.conn.setRequestHandler('DOM.describeNode', async (args) => {
|
|
69
|
+
let node: dom.Node
|
|
70
|
+
if (args.nodeId !== undefined) {
|
|
71
|
+
const nodeMeta = this.queryActiveNode(args.nodeId)
|
|
72
|
+
node = this.collectNodeDetails(nodeMeta, args.depth ?? 0, false)
|
|
73
|
+
} else if (args.backendNodeId !== undefined) {
|
|
74
|
+
const nodeMeta = this.activateBackendNodeIfNeeded(args.backendNodeId)
|
|
75
|
+
if (!nodeMeta) throw new Error('no such node found')
|
|
76
|
+
node = this.collectNodeDetails(nodeMeta, args.depth ?? 0, false)
|
|
77
|
+
} else {
|
|
78
|
+
throw new Error('missing (backend) node id')
|
|
79
|
+
}
|
|
80
|
+
return { node }
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
this.conn.setRequestHandler('DOM.getDocument', async ({ depth }) => {
|
|
84
|
+
let children: dom.Node[] | undefined
|
|
85
|
+
if (depth && depth > 1) {
|
|
86
|
+
children = this.mountPoints.map(({ nodeMeta }) =>
|
|
87
|
+
this.collectNodeDetails(nodeMeta, depth - 1, true),
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
const ty = GlassEaselNodeType.Unknown
|
|
91
|
+
const root: dom.Node = {
|
|
92
|
+
backendNodeId: this.documentNodeId,
|
|
93
|
+
nodeType: glassEaselNodeTypeToCDP(ty),
|
|
94
|
+
glassEaselNodeType: ty,
|
|
95
|
+
nodeName: StaticNodeName.Document,
|
|
96
|
+
virtual: true,
|
|
97
|
+
inheritSlots: false,
|
|
98
|
+
nodeId: this.documentNodeId,
|
|
99
|
+
localName: StaticNodeName.Document,
|
|
100
|
+
nodeValue: '',
|
|
101
|
+
attributes: [],
|
|
102
|
+
glassEaselAttributeCount: 0,
|
|
103
|
+
children,
|
|
104
|
+
}
|
|
105
|
+
return { root }
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
this.conn.setRequestHandler('DOM.setInspectedNode', async ({ nodeId }) => {
|
|
109
|
+
const { node } = this.queryActiveNode(nodeId)
|
|
110
|
+
const elem = node.asElement()
|
|
111
|
+
if (!elem) return
|
|
112
|
+
this.selectedNodeId = nodeId
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
this.conn.setRequestHandler('DOM.requestChildNodes', async ({ nodeId }) => {
|
|
116
|
+
const nodeMeta = this.queryActiveNode(nodeId)
|
|
117
|
+
this.sendChildNodes(nodeMeta)
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
this.conn.setRequestHandler('DOM.getGlassEaselAttributes', async ({ nodeId }) => {
|
|
121
|
+
const { node } = this.queryActiveNode(nodeId)
|
|
122
|
+
const elem = node.asElement()
|
|
123
|
+
if (!elem) {
|
|
124
|
+
return {
|
|
125
|
+
glassEaselNodeType: GlassEaselNodeType.TextNode,
|
|
126
|
+
virtual: false,
|
|
127
|
+
is: '',
|
|
128
|
+
id: '',
|
|
129
|
+
class: '',
|
|
130
|
+
slot: '',
|
|
131
|
+
slotName: undefined,
|
|
132
|
+
slotValues: undefined,
|
|
133
|
+
eventBindings: [],
|
|
134
|
+
dataset: [],
|
|
135
|
+
marks: [],
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// element types
|
|
140
|
+
const comp = elem.asGeneralComponent()
|
|
141
|
+
const nativeNode = elem.asNativeNode()
|
|
142
|
+
const virtualNode = elem.asVirtualNode()
|
|
143
|
+
let glassEaselNodeType = GlassEaselNodeType.Unknown
|
|
144
|
+
if (comp) glassEaselNodeType = GlassEaselNodeType.Component
|
|
145
|
+
if (nativeNode) glassEaselNodeType = GlassEaselNodeType.NativeNode
|
|
146
|
+
if (virtualNode) glassEaselNodeType = GlassEaselNodeType.VirtualNode
|
|
147
|
+
|
|
148
|
+
// collect basic attributes
|
|
149
|
+
const virtual = elem.isVirtual()
|
|
150
|
+
let is = ''
|
|
151
|
+
if (comp) is = comp.is
|
|
152
|
+
if (nativeNode) is = nativeNode.is
|
|
153
|
+
if (virtualNode) is = virtualNode.is
|
|
154
|
+
const id = elem.id
|
|
155
|
+
const nodeClass = elem.class
|
|
156
|
+
const slot = elem.slot
|
|
157
|
+
let slotName
|
|
158
|
+
const maybeSlotName = Reflect.get(elem, '_$slotName') as unknown
|
|
159
|
+
if (typeof maybeSlotName === 'string') slotName = maybeSlotName
|
|
160
|
+
let slotValues: { name: string; value: GlassEaselVar }[] | undefined
|
|
161
|
+
const maybeSlotValues = Reflect.get(elem, '_$slotValues') as unknown
|
|
162
|
+
if (typeof maybeSlotValues === 'object' && maybeSlotValues !== null) {
|
|
163
|
+
slotValues = []
|
|
164
|
+
Object.entries(maybeSlotValues).forEach(([name, value]) => {
|
|
165
|
+
slotValues!.push({ name, value: toGlassEaselVar(value) })
|
|
166
|
+
})
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// collect event bindings
|
|
170
|
+
const eventBindings: {
|
|
171
|
+
name: string
|
|
172
|
+
capture: boolean
|
|
173
|
+
count: number
|
|
174
|
+
hasCatch: boolean
|
|
175
|
+
hasMutBind: boolean
|
|
176
|
+
}[] = []
|
|
177
|
+
type EventPoint = {
|
|
178
|
+
mutCount?: number
|
|
179
|
+
finalCount?: number
|
|
180
|
+
funcArr?: { _$arr?: { f: unknown }[] | null }
|
|
181
|
+
}
|
|
182
|
+
const maybeEventTarget = Reflect.get(elem, '_$eventTarget') as
|
|
183
|
+
| {
|
|
184
|
+
listeners?: { [name: string]: EventPoint }
|
|
185
|
+
captureListeners?: { [name: string]: EventPoint }
|
|
186
|
+
}
|
|
187
|
+
| null
|
|
188
|
+
| undefined
|
|
189
|
+
if (typeof maybeEventTarget === 'object' && maybeEventTarget !== null) {
|
|
190
|
+
const processListeners = (capture: boolean, listeners?: { [name: string]: EventPoint }) => {
|
|
191
|
+
if (typeof listeners === 'object' && listeners !== null) {
|
|
192
|
+
Object.entries(listeners).forEach(([name, value]) => {
|
|
193
|
+
const count = value?.funcArr?._$arr?.length ?? 0
|
|
194
|
+
if (count > 0) {
|
|
195
|
+
const hasCatch = (value?.finalCount ?? 0) > 0
|
|
196
|
+
const hasMutBind = (value?.finalCount ?? 0) > 0
|
|
197
|
+
eventBindings.push({ name, capture, count, hasCatch, hasMutBind })
|
|
198
|
+
}
|
|
199
|
+
})
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
processListeners(true, maybeEventTarget.captureListeners)
|
|
203
|
+
processListeners(false, maybeEventTarget.listeners)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// collect attributes, properties, and external classes
|
|
207
|
+
let normalAttributes: { name: string; value: GlassEaselVar }[] | undefined
|
|
208
|
+
let properties: { name: string; value: GlassEaselVar }[] | undefined
|
|
209
|
+
let externalClasses: { name: string; value: string }[] | undefined
|
|
210
|
+
if (nativeNode) {
|
|
211
|
+
normalAttributes = []
|
|
212
|
+
elem.attributes.forEach(({ name, value }) => {
|
|
213
|
+
normalAttributes!.push({ name, value: toGlassEaselVar(value) })
|
|
214
|
+
})
|
|
215
|
+
}
|
|
216
|
+
if (comp) {
|
|
217
|
+
properties = []
|
|
218
|
+
const beh = comp.getComponentDefinition().behavior
|
|
219
|
+
const names = beh.listProperties()
|
|
220
|
+
names.forEach((name) => {
|
|
221
|
+
properties!.push({ name, value: toGlassEaselVar(comp.data[name]) })
|
|
222
|
+
})
|
|
223
|
+
const ec = comp.getExternalClasses()
|
|
224
|
+
if (ec) {
|
|
225
|
+
externalClasses = Object.entries(ec).map(([name, value]) => ({
|
|
226
|
+
name,
|
|
227
|
+
value: value?.join(' ') ?? '',
|
|
228
|
+
}))
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// collect dataset
|
|
233
|
+
const dataset: { name: string; value: GlassEaselVar }[] = []
|
|
234
|
+
Object.entries(elem.dataset ?? {}).forEach(([name, value]) => {
|
|
235
|
+
dataset.push({ name, value: toGlassEaselVar(value) })
|
|
236
|
+
})
|
|
237
|
+
const marks: { name: string; value: GlassEaselVar }[] = []
|
|
238
|
+
const maybeMarks = Reflect.get(elem, '_$marks') as { [key: string]: unknown } | undefined
|
|
239
|
+
Object.entries(maybeMarks ?? {}).forEach(([name, value]) => {
|
|
240
|
+
marks.push({ name, value: toGlassEaselVar(value) })
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
glassEaselNodeType,
|
|
245
|
+
virtual,
|
|
246
|
+
is,
|
|
247
|
+
id,
|
|
248
|
+
class: nodeClass,
|
|
249
|
+
slot,
|
|
250
|
+
slotName,
|
|
251
|
+
slotValues,
|
|
252
|
+
eventBindings,
|
|
253
|
+
normalAttributes,
|
|
254
|
+
properties,
|
|
255
|
+
externalClasses,
|
|
256
|
+
dataset,
|
|
257
|
+
marks,
|
|
258
|
+
}
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
this.conn.setRequestHandler('DOM.getGlassEaselComposedChildren', async ({ nodeId }) => {
|
|
262
|
+
const { node } = this.queryActiveNode(nodeId)
|
|
263
|
+
const elem = node.asElement()
|
|
264
|
+
if (!elem) return { nodes: [] }
|
|
265
|
+
const nodes: dom.Node[] = []
|
|
266
|
+
elem.forEachComposedChild((child) => {
|
|
267
|
+
const nodeMeta = this.activateNode(child)
|
|
268
|
+
nodes.push(this.collectNodeDetails(nodeMeta, 0, false))
|
|
269
|
+
})
|
|
270
|
+
return { nodes }
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
this.conn.setRequestHandler(
|
|
274
|
+
'DOM.pushNodesByBackendIdsToFrontend',
|
|
275
|
+
async ({ backendNodeIds }) => {
|
|
276
|
+
backendNodeIds.forEach((backendNodeId) => {
|
|
277
|
+
this.activateBackendNodeIfNeeded(backendNodeId)
|
|
278
|
+
})
|
|
279
|
+
return { nodeIds: backendNodeIds }
|
|
280
|
+
},
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
this.conn.setRequestHandler('DOM.getBoxModel', async (args) => {
|
|
284
|
+
let node: glassEasel.Node | null
|
|
285
|
+
if ('nodeId' in args) {
|
|
286
|
+
node = this.queryActiveNode(args.nodeId).node
|
|
287
|
+
} else {
|
|
288
|
+
node = this.getMaybeBackendNode(args.backendNodeId)
|
|
289
|
+
}
|
|
290
|
+
const ctx = node?.getBackendContext()
|
|
291
|
+
const elem = node?.getBackendElement()
|
|
292
|
+
if (!ctx || !elem) {
|
|
293
|
+
throw new Error('no such backend node found')
|
|
294
|
+
}
|
|
295
|
+
const { margin, border, padding, content } = await backendUtils.getBoxModel(ctx, elem)
|
|
296
|
+
const toQuad = (x: backendUtils.BoundingClientRect) => {
|
|
297
|
+
const lt = [x.left, x.top]
|
|
298
|
+
const rt = [x.left + x.width, x.top]
|
|
299
|
+
const lb = [x.left, x.top + x.height]
|
|
300
|
+
const rb = [x.left + x.width, x.top + x.height]
|
|
301
|
+
return [...lt, ...rt, ...rb, ...lb] as protocol.dom.Quad
|
|
302
|
+
}
|
|
303
|
+
return {
|
|
304
|
+
margin: toQuad(margin),
|
|
305
|
+
border: toQuad(border),
|
|
306
|
+
padding: toQuad(padding),
|
|
307
|
+
content: toQuad(content),
|
|
308
|
+
width: border.width,
|
|
309
|
+
height: border.height,
|
|
310
|
+
}
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
this.conn.setRequestHandler('DOM.useGlassEaselElementInConsole', async ({ nodeId }) => {
|
|
314
|
+
const { node } = this.queryActiveNode(nodeId)
|
|
315
|
+
const varName = this.useInConsole(node)
|
|
316
|
+
return { varName }
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
this.conn.setRequestHandler(
|
|
320
|
+
'DOM.useGlassEaselAttributeInConsole',
|
|
321
|
+
async ({ nodeId, attribute }) => {
|
|
322
|
+
const { node } = this.queryActiveNode(nodeId)
|
|
323
|
+
let attr: unknown
|
|
324
|
+
const elem = node.asElement()
|
|
325
|
+
if (!elem) throw new Error('not an element')
|
|
326
|
+
if (attribute.startsWith('data:')) {
|
|
327
|
+
attr = elem.dataset[attribute.slice(5)]
|
|
328
|
+
} else if (attribute.startsWith('mark:')) {
|
|
329
|
+
const maybeMarks = Reflect.get(elem, '_$marks') as { [key: string]: unknown } | undefined
|
|
330
|
+
attr = maybeMarks?.[attribute.slice(5)]
|
|
331
|
+
} else {
|
|
332
|
+
const comp = elem.asGeneralComponent()
|
|
333
|
+
if (comp) {
|
|
334
|
+
attr = comp.data[attribute]
|
|
335
|
+
} else {
|
|
336
|
+
attr = elem.attributes.find(({ name }) => name === attribute)?.value
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
const varName = this.useInConsole(attr)
|
|
340
|
+
return { varName }
|
|
341
|
+
},
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
this.conn.setRequestHandler('CSS.getComputedStyleForNode', async ({ nodeId }) => {
|
|
345
|
+
const { node } = this.queryActiveNode(nodeId)
|
|
346
|
+
const ctx = node?.getBackendContext()
|
|
347
|
+
const elem = node?.getBackendElement()
|
|
348
|
+
if (!ctx || !elem) {
|
|
349
|
+
throw new Error('no such backend node found')
|
|
350
|
+
}
|
|
351
|
+
const computedStyle = (await backendUtils.getAllComputedStyles(ctx, elem)).properties
|
|
352
|
+
return { computedStyle }
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
this.conn.setRequestHandler('CSS.getMatchedStylesForNode', async ({ nodeId }) => {
|
|
356
|
+
const { node } = this.queryActiveNode(nodeId)
|
|
357
|
+
const ctx = node?.getBackendContext()
|
|
358
|
+
const elem = node?.getBackendElement()
|
|
359
|
+
if (!ctx || !elem) {
|
|
360
|
+
throw new Error('no such backend node found')
|
|
361
|
+
}
|
|
362
|
+
const { inline, inlineText, rules, crossOriginFailing } = await backendUtils.getMatchedRules(
|
|
363
|
+
ctx,
|
|
364
|
+
elem,
|
|
365
|
+
)
|
|
366
|
+
const inlineStyle = { cssProperties: inline, cssText: inlineText }
|
|
367
|
+
const matchedCSSRules = rules.map((rule) => ({
|
|
368
|
+
rule: {
|
|
369
|
+
selectorList: { selectors: [{ text: rule.selector }], text: rule.selector },
|
|
370
|
+
style: { cssProperties: rule.properties, cssText: rule.propertyText },
|
|
371
|
+
media: rule.mediaQueries.map((x) => ({ text: x })),
|
|
372
|
+
inactive: rule.inactive || false,
|
|
373
|
+
},
|
|
374
|
+
}))
|
|
375
|
+
return { inlineStyle, matchedCSSRules, inherited: [], crossOriginFailing }
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
this.conn.setRequestHandler('Overlay.setInspectMode', async ({ mode }) => {
|
|
379
|
+
if (mode === 'searchForNode') {
|
|
380
|
+
let prevHighlight = 0
|
|
381
|
+
this.listOverlayComponents().forEach((x) =>
|
|
382
|
+
x.startNodeSelect((node, isFinal) => {
|
|
383
|
+
if (isFinal) {
|
|
384
|
+
this.listOverlayComponents().forEach((x) => x.endNodeSelect())
|
|
385
|
+
if (node) {
|
|
386
|
+
const backendNodeId = this.addBackendNode(node)
|
|
387
|
+
this.conn.sendEvent('Overlay.inspectNodeRequested', {
|
|
388
|
+
backendNodeId,
|
|
389
|
+
})
|
|
390
|
+
}
|
|
391
|
+
this.conn.sendEvent('Overlay.inspectModeCanceled', {})
|
|
392
|
+
} else {
|
|
393
|
+
let nodeId = node ? this.getNodeId(node) : 0
|
|
394
|
+
if (!this.activeNodes[nodeId]) nodeId = 0
|
|
395
|
+
if (prevHighlight !== nodeId) {
|
|
396
|
+
prevHighlight = nodeId
|
|
397
|
+
this.conn.sendEvent('Overlay.nodeHighlightRequested', { nodeId })
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}),
|
|
401
|
+
)
|
|
402
|
+
} else {
|
|
403
|
+
this.listOverlayComponents().forEach((x) => x.endNodeSelect())
|
|
404
|
+
}
|
|
405
|
+
})
|
|
406
|
+
|
|
407
|
+
this.conn.setRequestHandler('Overlay.highlightNode', async (args) => {
|
|
408
|
+
let node: glassEasel.Node
|
|
409
|
+
if ('nodeId' in args) {
|
|
410
|
+
node = this.queryActiveNode(args.nodeId).node
|
|
411
|
+
} else {
|
|
412
|
+
const n = this.getMaybeBackendNode(args.backendNodeId)
|
|
413
|
+
if (!n) throw new Error('no such node found')
|
|
414
|
+
node = n
|
|
415
|
+
}
|
|
416
|
+
this.listOverlayComponents().forEach((x) => {
|
|
417
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
418
|
+
x.highlight(null)
|
|
419
|
+
})
|
|
420
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
421
|
+
this.getOverlayComponent(node).highlight(node)
|
|
422
|
+
})
|
|
423
|
+
|
|
424
|
+
this.conn.setRequestHandler('Overlay.hideHighlight', async () => {
|
|
425
|
+
this.listOverlayComponents().forEach((x) => {
|
|
426
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
427
|
+
x.highlight(null)
|
|
428
|
+
})
|
|
429
|
+
})
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
attach(root: glassEasel.Element, env: glassEasel.MountPointEnv) {
|
|
433
|
+
const nodeMeta = this.activateNode(root)
|
|
434
|
+
const previousNode = this.mountPoints[this.mountPoints.length - 1]
|
|
435
|
+
const previousNodeId = previousNode ? previousNode.nodeMeta.nodeId : undefined
|
|
436
|
+
this.mountPoints.push({ nodeMeta, env })
|
|
437
|
+
this.conn.sendEvent('DOM.childNodeInserted', {
|
|
438
|
+
parentNodeId: this.documentNodeId,
|
|
439
|
+
previousNodeId: previousNodeId ?? 0,
|
|
440
|
+
node: this.collectNodeDetails(nodeMeta, 0, true),
|
|
441
|
+
})
|
|
442
|
+
this.conn.sendEvent('DOM.childNodeCountUpdated', {
|
|
443
|
+
nodeId: this.documentNodeId,
|
|
444
|
+
childNodeCount: this.mountPoints.length,
|
|
445
|
+
})
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
detach(root: glassEasel.Element) {
|
|
449
|
+
const index = this.mountPoints.findIndex((x) => x.nodeMeta.node === root)
|
|
450
|
+
if (index < 0) {
|
|
451
|
+
warn('no such mount point to remove')
|
|
452
|
+
return
|
|
453
|
+
}
|
|
454
|
+
this.mountPoints.splice(index, 1)
|
|
455
|
+
const nodeId = this.deactivateNodeTree(root)
|
|
456
|
+
if (!nodeId) return
|
|
457
|
+
this.conn.sendEvent('DOM.childNodeRemoved', {
|
|
458
|
+
parentNodeId: this.documentNodeId,
|
|
459
|
+
nodeId,
|
|
460
|
+
})
|
|
461
|
+
this.conn.sendEvent('DOM.childNodeCountUpdated', {
|
|
462
|
+
nodeId: this.documentNodeId,
|
|
463
|
+
childNodeCount: this.mountPoints.length,
|
|
464
|
+
})
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
getOverlayComponent(node: glassEasel.Node) {
|
|
468
|
+
const ctx = node.getBackendContext()
|
|
469
|
+
if (!ctx) {
|
|
470
|
+
throw new Error('backend context has been released')
|
|
471
|
+
}
|
|
472
|
+
return this.conn.getOverlayComponent(ctx)
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
listOverlayComponents() {
|
|
476
|
+
const ret: ReturnType<Connection['getOverlayComponent']>[] = []
|
|
477
|
+
this.mountPoints.forEach((mp) => {
|
|
478
|
+
const ctx = mp.nodeMeta.node.getBackendContext()
|
|
479
|
+
if (!ctx) return
|
|
480
|
+
const comp = this.conn.getOverlayComponent(ctx)
|
|
481
|
+
if (ret.includes(comp)) return
|
|
482
|
+
ret.push(comp)
|
|
483
|
+
})
|
|
484
|
+
return ret
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// eslint-disable-next-line class-methods-use-this
|
|
488
|
+
private useInConsole(v: unknown): string {
|
|
489
|
+
let i = 0
|
|
490
|
+
while (i <= 0xffffffff) {
|
|
491
|
+
const varName = `temp${i}`
|
|
492
|
+
if (!Object.prototype.hasOwnProperty.call(globalThis, varName)) {
|
|
493
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
494
|
+
;(globalThis as any)[varName] = v
|
|
495
|
+
// eslint-disable-next-line no-console
|
|
496
|
+
console.log(varName, v)
|
|
497
|
+
return varName
|
|
498
|
+
}
|
|
499
|
+
i += 1
|
|
500
|
+
}
|
|
501
|
+
return ''
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
generateNodeId(): NodeId {
|
|
505
|
+
const ret = this.nodeIdInc
|
|
506
|
+
this.nodeIdInc += 1
|
|
507
|
+
return ret
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
private getNodeId(node: glassEasel.Node): NodeId {
|
|
511
|
+
const nodeId = this.nodeIdMap.get(node)
|
|
512
|
+
if (nodeId !== undefined) {
|
|
513
|
+
return nodeId
|
|
514
|
+
}
|
|
515
|
+
const newNodeId = this.generateNodeId()
|
|
516
|
+
this.nodeIdMap.set(node, newNodeId)
|
|
517
|
+
return newNodeId
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
private queryActiveNode(nodeId: NodeId): NodeMeta {
|
|
521
|
+
const nodeMeta = this.activeNodes[nodeId]
|
|
522
|
+
if (!nodeMeta) throw new Error(`no active node found for node id ${nodeId}`)
|
|
523
|
+
return nodeMeta
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
private getMaybeBackendNode(backendNodeId: NodeId): glassEasel.Node | null {
|
|
527
|
+
const nodeMeta = this.activeNodes[backendNodeId]
|
|
528
|
+
if (nodeMeta) return nodeMeta?.node
|
|
529
|
+
return this.activeBackendNodes[backendNodeId]?.deref() ?? null
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// eslint-disable-next-line class-methods-use-this
|
|
533
|
+
private startWatch(node: glassEasel.Node) {
|
|
534
|
+
const observer = glassEasel.MutationObserver.create((ev) => {
|
|
535
|
+
const node = ev.target
|
|
536
|
+
const nodeId = this.getNodeId(node)
|
|
537
|
+
const nodeMeta = this.activeNodes[nodeId]
|
|
538
|
+
if (!nodeMeta) return
|
|
539
|
+
if (ev.type === 'properties') {
|
|
540
|
+
const elem = node.asElement()!
|
|
541
|
+
const nameType = ev.nameType
|
|
542
|
+
if (nameType === 'attribute') {
|
|
543
|
+
const name = ev.attributeName ?? ''
|
|
544
|
+
const v = elem.getAttribute(name)
|
|
545
|
+
if (v === null || v === 'undefined') {
|
|
546
|
+
this.conn.sendEvent('DOM.attributeRemoved', { nodeId, name, nameType })
|
|
547
|
+
} else {
|
|
548
|
+
const detail = toGlassEaselVar(v)
|
|
549
|
+
const value = glassEaselVarToString(detail)
|
|
550
|
+
this.conn.sendEvent('DOM.attributeModified', {
|
|
551
|
+
nodeId,
|
|
552
|
+
name,
|
|
553
|
+
value,
|
|
554
|
+
detail,
|
|
555
|
+
nameType,
|
|
556
|
+
})
|
|
557
|
+
}
|
|
558
|
+
} else {
|
|
559
|
+
let name: string | undefined
|
|
560
|
+
let v: unknown
|
|
561
|
+
if (nameType === 'component-property') {
|
|
562
|
+
name = ev.propertyName ?? ''
|
|
563
|
+
v = elem.asGeneralComponent()?.data[name]
|
|
564
|
+
} else if (nameType === 'slot-value') {
|
|
565
|
+
name = ev.propertyName ?? ''
|
|
566
|
+
const maybeSlotValues = Reflect.get(elem, '_$slotValues') as unknown
|
|
567
|
+
if (typeof maybeSlotValues === 'object' && maybeSlotValues !== null) {
|
|
568
|
+
v = (maybeSlotValues as { [name: string]: unknown })[name]
|
|
569
|
+
}
|
|
570
|
+
} else if (nameType === 'dataset' && ev.attributeName?.startsWith('data:')) {
|
|
571
|
+
name = ev.attributeName ?? ''
|
|
572
|
+
v = elem.dataset[name.slice(5)]
|
|
573
|
+
} else if (nameType === 'mark' && ev.attributeName?.startsWith('mark:')) {
|
|
574
|
+
name = ev.attributeName ?? ''
|
|
575
|
+
const marks = Reflect.get(elem, '_$marks') as { [key: string]: unknown } | undefined
|
|
576
|
+
v = marks?.[name.slice(5)]
|
|
577
|
+
} else if (nameType === 'external-class') {
|
|
578
|
+
name = ev.attributeName ?? ''
|
|
579
|
+
v = elem.asGeneralComponent()?.getExternalClasses()?.[name]?.join(' ') ?? ''
|
|
580
|
+
} else if (ev.attributeName === 'slot') {
|
|
581
|
+
name = ev.attributeName
|
|
582
|
+
v = elem.slot
|
|
583
|
+
} else if (ev.attributeName === 'id') {
|
|
584
|
+
name = ev.attributeName
|
|
585
|
+
v = elem.id
|
|
586
|
+
} else if (ev.attributeName === 'class') {
|
|
587
|
+
name = ev.attributeName
|
|
588
|
+
v = elem.class
|
|
589
|
+
} else if (ev.attributeName === 'style') {
|
|
590
|
+
name = ev.attributeName
|
|
591
|
+
v = elem.style
|
|
592
|
+
} else if (ev.attributeName === 'name') {
|
|
593
|
+
name = ev.attributeName
|
|
594
|
+
v = Reflect.get(elem, '_$slotName')
|
|
595
|
+
}
|
|
596
|
+
if (name) {
|
|
597
|
+
const detail = toGlassEaselVar(v)
|
|
598
|
+
const value = glassEaselVarToString(detail)
|
|
599
|
+
this.conn.sendEvent('DOM.attributeModified', {
|
|
600
|
+
nodeId,
|
|
601
|
+
name,
|
|
602
|
+
value,
|
|
603
|
+
detail,
|
|
604
|
+
nameType,
|
|
605
|
+
})
|
|
606
|
+
} else {
|
|
607
|
+
warn('unknown mutation observer event')
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
return
|
|
611
|
+
}
|
|
612
|
+
if (ev.type === 'childList') {
|
|
613
|
+
if (!nodeMeta.childNodesSent) return
|
|
614
|
+
const parent = node.asElement()!
|
|
615
|
+
ev.addedNodes?.forEach((child) => {
|
|
616
|
+
const index = parent.childNodes.indexOf(child)
|
|
617
|
+
if (index < 0) return
|
|
618
|
+
const previousNodeId = index === 0 ? 0 : this.getNodeId(parent.childNodes[index - 1])
|
|
619
|
+
const childMeta = this.activateNode(child)
|
|
620
|
+
this.conn.sendEvent('DOM.childNodeInserted', {
|
|
621
|
+
parentNodeId: nodeId,
|
|
622
|
+
previousNodeId,
|
|
623
|
+
node: this.collectNodeDetails(childMeta, 0, false),
|
|
624
|
+
})
|
|
625
|
+
})
|
|
626
|
+
ev.removedNodes?.forEach((child) => {
|
|
627
|
+
this.deactivateNodeTree(child)
|
|
628
|
+
this.conn.sendEvent('DOM.childNodeRemoved', {
|
|
629
|
+
parentNodeId: nodeId,
|
|
630
|
+
nodeId: this.getNodeId(child),
|
|
631
|
+
})
|
|
632
|
+
})
|
|
633
|
+
return
|
|
634
|
+
}
|
|
635
|
+
if (ev.type === 'characterData') {
|
|
636
|
+
this.conn.sendEvent('DOM.characterDataModified', {
|
|
637
|
+
nodeId,
|
|
638
|
+
characterData: node.asTextNode()!.textContent,
|
|
639
|
+
})
|
|
640
|
+
return
|
|
641
|
+
}
|
|
642
|
+
warn('unknown mutation observer event')
|
|
643
|
+
})
|
|
644
|
+
observer.observe(node, { properties: 'all', characterData: true, childList: true })
|
|
645
|
+
return observer
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// eslint-disable-next-line class-methods-use-this
|
|
649
|
+
private endWatch(observer: glassEasel.MutationObserver) {
|
|
650
|
+
observer.disconnect()
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* Start tracking a node.
|
|
655
|
+
*
|
|
656
|
+
* This will also activate its parent or host (for shadow-root).
|
|
657
|
+
*/
|
|
658
|
+
private activateNode(node: glassEasel.Node): NodeMeta {
|
|
659
|
+
const nodeId = this.getNodeId(node)
|
|
660
|
+
delete this.activeBackendNodes[nodeId]
|
|
661
|
+
if (this.activeNodes[nodeId]) {
|
|
662
|
+
const nodeMeta = this.activeNodes[nodeId]
|
|
663
|
+
return nodeMeta
|
|
664
|
+
}
|
|
665
|
+
const isMountPoint = this.mountPoints.map((x) => x.nodeMeta.node).includes(node)
|
|
666
|
+
if (!isMountPoint) {
|
|
667
|
+
let p: glassEasel.Node | undefined
|
|
668
|
+
if (node.parentNode) p = node.parentNode
|
|
669
|
+
else if (node.asShadowRoot()) p = node.asShadowRoot()!.getHostNode()
|
|
670
|
+
else p = undefined
|
|
671
|
+
if (p) this.activateNode(p)
|
|
672
|
+
}
|
|
673
|
+
const observer = this.startWatch(node)
|
|
674
|
+
const nodeMeta = { node, nodeId, observer, childNodesSent: false }
|
|
675
|
+
this.activeNodes[nodeId] = nodeMeta
|
|
676
|
+
return nodeMeta
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/** Release a node tree (to allow gabbage collection). */
|
|
680
|
+
private deactivateNodeTree(node: glassEasel.Node): NodeId | undefined {
|
|
681
|
+
const nodeId = this.nodeIdMap.get(node)
|
|
682
|
+
if (nodeId === undefined) {
|
|
683
|
+
return undefined
|
|
684
|
+
}
|
|
685
|
+
if (!this.activeNodes[nodeId]) {
|
|
686
|
+
return nodeId
|
|
687
|
+
}
|
|
688
|
+
const { observer } = this.activeNodes[nodeId]
|
|
689
|
+
this.endWatch(observer)
|
|
690
|
+
const shadowRoot = node.asGeneralComponent()?.getShadowRoot?.()
|
|
691
|
+
if (shadowRoot) this.deactivateNodeTree(shadowRoot)
|
|
692
|
+
const childNodes: glassEasel.Node[] | undefined = (node as glassEasel.Element).childNodes
|
|
693
|
+
if (childNodes) {
|
|
694
|
+
childNodes.forEach((node) => this.deactivateNodeTree(node))
|
|
695
|
+
}
|
|
696
|
+
delete this.activeNodes[nodeId]
|
|
697
|
+
return nodeId
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
private addBackendNode(node: glassEasel.Node): NodeId {
|
|
701
|
+
const nodeId = this.getNodeId(node)
|
|
702
|
+
this.activeBackendNodes[nodeId] = new WeakRef(node)
|
|
703
|
+
return nodeId
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
private activateBackendNodeIfNeeded(backendNodeId: NodeId): NodeMeta | null {
|
|
707
|
+
const node = this.activeBackendNodes[backendNodeId]?.deref()
|
|
708
|
+
if (node === undefined) {
|
|
709
|
+
const nodeMeta = this.activeNodes[backendNodeId]
|
|
710
|
+
return nodeMeta ?? null
|
|
711
|
+
}
|
|
712
|
+
return this.activateNode(node)
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
// eslint-disable-next-line class-methods-use-this
|
|
716
|
+
private collectNodeBasicInfomation(
|
|
717
|
+
backendNodeId: NodeId,
|
|
718
|
+
node: glassEasel.Node,
|
|
719
|
+
): dom.BackendNode {
|
|
720
|
+
const ty = getNodeType(node)
|
|
721
|
+
const nodeName = getNodeName(node, ty, false)
|
|
722
|
+
const virtual = node.asElement()?.isVirtual() ?? false
|
|
723
|
+
const inheritSlots = node.asElement()?.isInheritSlots() ?? false
|
|
724
|
+
return {
|
|
725
|
+
backendNodeId,
|
|
726
|
+
nodeType: glassEaselNodeTypeToCDP(ty),
|
|
727
|
+
glassEaselNodeType: ty,
|
|
728
|
+
nodeName,
|
|
729
|
+
virtual,
|
|
730
|
+
inheritSlots,
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
collectNodeDetails(nodeMeta: NodeMeta, depth: number, isMountPoint: boolean): dom.Node {
|
|
735
|
+
const { nodeId, node } = nodeMeta
|
|
736
|
+
const tmplDevAttrs = (
|
|
737
|
+
node as glassEasel.Node & { _$wxTmplDevArgs?: glassEasel.template.TmplDevArgs }
|
|
738
|
+
)._$wxTmplDevArgs
|
|
739
|
+
|
|
740
|
+
// collect node information
|
|
741
|
+
const {
|
|
742
|
+
backendNodeId,
|
|
743
|
+
nodeType,
|
|
744
|
+
glassEaselNodeType: ty,
|
|
745
|
+
nodeName,
|
|
746
|
+
virtual,
|
|
747
|
+
inheritSlots,
|
|
748
|
+
} = this.collectNodeBasicInfomation(nodeId, node)
|
|
749
|
+
let parentId: NodeId | undefined
|
|
750
|
+
if (isMountPoint) parentId = this.documentNodeId
|
|
751
|
+
else if (node.parentNode) parentId = this.getNodeId(node.parentNode)
|
|
752
|
+
else if (node.asShadowRoot()) parentId = this.getNodeId(node.asShadowRoot()!.getHostNode())
|
|
753
|
+
else parentId = undefined
|
|
754
|
+
const localName = getNodeName(node, ty, true)
|
|
755
|
+
const nodeValue = node.asTextNode()?.textContent ?? ''
|
|
756
|
+
|
|
757
|
+
// collect attributes
|
|
758
|
+
const attributes: string[] = []
|
|
759
|
+
let glassEaselAttributeCount = 0
|
|
760
|
+
let slotName: string | undefined
|
|
761
|
+
if (ty !== GlassEaselNodeType.TextNode) {
|
|
762
|
+
const activeAttrs = tmplDevAttrs?.A
|
|
763
|
+
const elem = node.asElement()!
|
|
764
|
+
if (activeAttrs) {
|
|
765
|
+
// show active attributes based on template information
|
|
766
|
+
activeAttrs.forEach((name) => {
|
|
767
|
+
if (name[0] === ':') {
|
|
768
|
+
if (name === ':slot') attributes.push('slot', elem.slot)
|
|
769
|
+
if (name === ':id') attributes.push('id', elem.id)
|
|
770
|
+
if (name === ':class') attributes.push('class', elem.class)
|
|
771
|
+
if (name === ':style') attributes.push('style', elem.style)
|
|
772
|
+
if (name === ':name') attributes.push('style', Reflect.get(elem, '_$slotName'))
|
|
773
|
+
} else if (name.startsWith('data:')) {
|
|
774
|
+
const value = elem.dataset?.[name.slice(5)]
|
|
775
|
+
attributes.push(name, glassEaselVarToString(toGlassEaselVar(value)))
|
|
776
|
+
} else if (name.startsWith('mark:')) {
|
|
777
|
+
const marks = Reflect.get(elem, '_$marks') as { [key: string]: unknown } | undefined
|
|
778
|
+
const value = marks?.[name.slice(5)]
|
|
779
|
+
attributes.push(name, glassEaselVarToString(toGlassEaselVar(value)))
|
|
780
|
+
} else if (name.indexOf(':') < 0) {
|
|
781
|
+
if (elem.asNativeNode()) {
|
|
782
|
+
const value = elem.attributes.find(({ name: n }) => name === n)?.value
|
|
783
|
+
attributes.push(name, glassEaselVarToString(toGlassEaselVar(value)))
|
|
784
|
+
} else if (elem.asGeneralComponent()) {
|
|
785
|
+
const comp = elem.asGeneralComponent()!
|
|
786
|
+
if (comp.getComponentDefinition().behavior.getPropertyType(name) !== undefined) {
|
|
787
|
+
const value = comp.data[name] as unknown
|
|
788
|
+
attributes.push(name, glassEaselVarToString(toGlassEaselVar(value)))
|
|
789
|
+
} else if (comp.hasExternalClass(name)) {
|
|
790
|
+
const value = comp.getExternalClasses()[name]?.join(' ')
|
|
791
|
+
attributes.push(name, glassEaselVarToString(toGlassEaselVar(value)))
|
|
792
|
+
}
|
|
793
|
+
} else if (typeof Reflect.get(elem, '_$slotName') === 'string') {
|
|
794
|
+
const maybeSlotValues = Reflect.get(elem, '_$slotValues') as unknown
|
|
795
|
+
if (typeof maybeSlotValues === 'object' && maybeSlotValues !== null) {
|
|
796
|
+
const value = (maybeSlotValues as { [name: string]: unknown })[name]
|
|
797
|
+
attributes.push(name, glassEaselVarToString(toGlassEaselVar(value)))
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
})
|
|
802
|
+
} else {
|
|
803
|
+
// detect changed attributes
|
|
804
|
+
if (elem.slot) attributes.push('slot', elem.slot)
|
|
805
|
+
if (elem.id) attributes.push('id', elem.id)
|
|
806
|
+
if (elem.class) attributes.push('class', elem.class)
|
|
807
|
+
if (elem.style) attributes.push('style', elem.style)
|
|
808
|
+
const maybeSlotName = Reflect.get(elem, '_$slotName') as unknown
|
|
809
|
+
if (typeof maybeSlotName === 'string') {
|
|
810
|
+
slotName = maybeSlotName
|
|
811
|
+
attributes.push('name', slotName)
|
|
812
|
+
}
|
|
813
|
+
glassEaselAttributeCount = attributes.length / 2
|
|
814
|
+
if (elem.asNativeNode()) {
|
|
815
|
+
elem.attributes.forEach(({ name, value }) => {
|
|
816
|
+
attributes.push(name, glassEaselVarToString(toGlassEaselVar(value)))
|
|
817
|
+
})
|
|
818
|
+
}
|
|
819
|
+
Object.entries(elem.dataset ?? {}).forEach(([key, value]) => {
|
|
820
|
+
const name = `data:${key}`
|
|
821
|
+
attributes.push(name, glassEaselVarToString(toGlassEaselVar(value)))
|
|
822
|
+
})
|
|
823
|
+
const marks = Reflect.get(elem, '_$marks') as { [key: string]: unknown } | undefined
|
|
824
|
+
Object.entries(marks ?? {}).forEach(([key, value]) => {
|
|
825
|
+
const name = `mark:${key}`
|
|
826
|
+
attributes.push(name, glassEaselVarToString(toGlassEaselVar(value)))
|
|
827
|
+
})
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// collect shadow-roots
|
|
832
|
+
const sr = node.asGeneralComponent()?.getShadowRoot()
|
|
833
|
+
const shadowRootType = sr ? 'open' : undefined
|
|
834
|
+
let shadowRoots: dom.Node[] | undefined
|
|
835
|
+
if (sr) {
|
|
836
|
+
const nodeMeta = this.activateNode(sr)
|
|
837
|
+
const n = this.collectNodeDetails(nodeMeta, depth - 1, false)
|
|
838
|
+
n.nodeName = 'shadow-root'
|
|
839
|
+
if (n) shadowRoots = [n]
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
// collect children
|
|
843
|
+
let children: dom.Node[] | undefined
|
|
844
|
+
if (depth > 1 && ty !== GlassEaselNodeType.TextNode) {
|
|
845
|
+
const elem = node.asElement()!
|
|
846
|
+
children = []
|
|
847
|
+
nodeMeta.childNodesSent = true
|
|
848
|
+
elem.childNodes.forEach((child) => {
|
|
849
|
+
const nodeMeta = this.activateNode(child)
|
|
850
|
+
const n = this.collectNodeDetails(nodeMeta, depth - 1, false)
|
|
851
|
+
if (n) children!.push(n)
|
|
852
|
+
})
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
// collect slot content
|
|
856
|
+
let distributedNodes: dom.BackendNode[] | undefined
|
|
857
|
+
if (typeof slotName === 'string') {
|
|
858
|
+
const elem = node.asElement()!
|
|
859
|
+
distributedNodes = []
|
|
860
|
+
elem.forEachComposedChild((child) => {
|
|
861
|
+
const nodeId = this.addBackendNode(child)
|
|
862
|
+
const n = this.collectNodeBasicInfomation(nodeId, child)
|
|
863
|
+
if (n) distributedNodes!.push(n)
|
|
864
|
+
})
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
return {
|
|
868
|
+
backendNodeId,
|
|
869
|
+
nodeType,
|
|
870
|
+
glassEaselNodeType: ty,
|
|
871
|
+
nodeName,
|
|
872
|
+
virtual,
|
|
873
|
+
inheritSlots,
|
|
874
|
+
nodeId,
|
|
875
|
+
parentId,
|
|
876
|
+
localName,
|
|
877
|
+
nodeValue,
|
|
878
|
+
attributes,
|
|
879
|
+
glassEaselAttributeCount,
|
|
880
|
+
shadowRootType,
|
|
881
|
+
shadowRoots,
|
|
882
|
+
children,
|
|
883
|
+
distributedNodes,
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
sendChildNodes(nodeMeta: NodeMeta) {
|
|
888
|
+
const { node, nodeId } = nodeMeta
|
|
889
|
+
const elem = node.asElement()
|
|
890
|
+
if (!elem) return
|
|
891
|
+
nodeMeta.childNodesSent = true
|
|
892
|
+
const nodes: dom.Node[] = []
|
|
893
|
+
elem.childNodes.forEach((child) => {
|
|
894
|
+
const nodeMeta = this.activateNode(child)
|
|
895
|
+
nodes.push(this.collectNodeDetails(nodeMeta, 0, false))
|
|
896
|
+
})
|
|
897
|
+
this.conn.sendEvent('DOM.setChildNodes', { parentId: nodeId, nodes })
|
|
898
|
+
}
|
|
899
|
+
}
|