Skip to content

Commit 6bcfbdd

Browse files
🎨 [PANA-4398] Convert SerializeOptions#parentNodePrivacyLevel into a normal function argument (#3893)
1 parent ccf6ee3 commit 6bcfbdd

File tree

6 files changed

+73
-62
lines changed

6 files changed

+73
-62
lines changed

‎packages/rum/src/domain/record/serialization/htmlAst.specHelper.ts‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@ const DEFAULT_SHADOW_ROOT_CONTROLLER = {
4343
export const generateLeanSerializedDoc = (htmlContent: string, privacyTag: string) => {
4444
const newDoc = makeHtmlDoc(htmlContent, privacyTag)
4545
const serializedDoc = removeIdFieldsRecursivelyClone(
46-
serializeNodeWithId(newDoc, {
47-
parentNodePrivacyLevel: NodePrivacyLevel.ALLOW,
46+
serializeNodeWithId(newDoc, NodePrivacyLevel.ALLOW, {
4847
serializationContext: {
4948
serializationStats: createSerializationStats(),
5049
shadowRootsController: DEFAULT_SHADOW_ROOT_CONTROLLER,

‎packages/rum/src/domain/record/serialization/serialization.types.ts‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import type { SerializationStats } from './serializationStats'
77
// Those values are the only one that can be used when inheriting privacy levels from parent to
88
// children during serialization, since HIDDEN and IGNORE shouldn't serialize their children. This
99
// ensures that no children are serialized when they shouldn't.
10-
type ParentNodePrivacyLevel =
10+
export type ParentNodePrivacyLevel =
1111
| typeof NodePrivacyLevel.ALLOW
1212
| typeof NodePrivacyLevel.MASK
1313
| typeof NodePrivacyLevel.MASK_USER_INPUT
@@ -40,7 +40,6 @@ export type SerializationContext =
4040

4141
export interface SerializeOptions {
4242
serializedNodeIds?: Set<number>
43-
parentNodePrivacyLevel: ParentNodePrivacyLevel
4443
serializationContext: SerializationContext
4544
configuration: RumConfiguration
4645
scope: SerializationScope

‎packages/rum/src/domain/record/serialization/serializeDocument.ts‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@ export function serializeDocument(
1313
serializationContext: SerializationContext
1414
): SerializedNodeWithId {
1515
const serializationStart = timeStampNow()
16-
const serializedNode = serializeNodeWithId(document, {
16+
const serializedNode = serializeNodeWithId(document, configuration.defaultPrivacyLevel, {
1717
serializationContext,
18-
parentNodePrivacyLevel: configuration.defaultPrivacyLevel,
1918
configuration,
2019
scope,
2120
})

‎packages/rum/src/domain/record/serialization/serializeNode.spec.ts‎

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ describe('serializeNodeWithId', () => {
5858
let scope: SerializationScope
5959

6060
const getDefaultOptions = (): SerializeOptions => ({
61-
parentNodePrivacyLevel: NodePrivacyLevel.ALLOW,
6261
serializationContext: getDefaultSerializationContext(),
6362
configuration: DEFAULT_CONFIGURATION,
6463
scope,
@@ -89,7 +88,9 @@ describe('serializeNodeWithId', () => {
8988
node: Element,
9089
options: SerializeOptions | undefined = undefined
9190
): (ElementNode & { id: number }) | null {
92-
return serializeNodeWithId(node, options ?? getDefaultOptions()) as (ElementNode & { id: number }) | null
91+
return serializeNodeWithId(node, NodePrivacyLevel.ALLOW, options ?? getDefaultOptions()) as
92+
| (ElementNode & { id: number })
93+
| null
9394
}
9495

9596
it('serializes a div', () => {
@@ -670,7 +671,7 @@ describe('serializeNodeWithId', () => {
670671
)
671672

672673
const options = getDefaultOptions()
673-
expect(serializeNodeWithId(linkNode, options)).toEqual({
674+
expect(serializeNodeWithId(linkNode, NodePrivacyLevel.ALLOW, options)).toEqual({
674675
type: NodeType.Element,
675676
tagName: 'link',
676677
id: jasmine.any(Number) as unknown as number,
@@ -700,7 +701,7 @@ describe('serializeNodeWithId', () => {
700701
})
701702

702703
const options = getDefaultOptions()
703-
expect(serializeNodeWithId(linkNode, options)).toEqual({
704+
expect(serializeNodeWithId(linkNode, NodePrivacyLevel.ALLOW, options)).toEqual({
704705
type: NodeType.Element,
705706
tagName: 'link',
706707
id: jasmine.any(Number) as unknown as number,
@@ -737,7 +738,7 @@ describe('serializeNodeWithId', () => {
737738
})
738739

739740
const options = getDefaultOptions()
740-
expect(serializeNodeWithId(linkNode, options)).toEqual({
741+
expect(serializeNodeWithId(linkNode, NodePrivacyLevel.ALLOW, options)).toEqual({
741742
type: NodeType.Element,
742743
tagName: 'link',
743744
id: jasmine.any(Number) as unknown as number,
@@ -762,7 +763,7 @@ describe('serializeNodeWithId', () => {
762763
parentEl.setAttribute(PRIVACY_ATTR_NAME, PRIVACY_ATTR_VALUE_ALLOW)
763764
const textNode = document.createTextNode('foo')
764765
parentEl.appendChild(textNode)
765-
expect(serializeNodeWithId(textNode, getDefaultOptions())).toEqual({
766+
expect(serializeNodeWithId(textNode, NodePrivacyLevel.ALLOW, getDefaultOptions())).toEqual({
766767
type: NodeType.Text,
767768
id: jasmine.any(Number) as unknown as number,
768769
textContent: 'foo',
@@ -773,7 +774,7 @@ describe('serializeNodeWithId', () => {
773774
const parentEl = document.createElement('bar')
774775
const textNode = document.createTextNode('')
775776
parentEl.appendChild(textNode)
776-
expect(serializeNodeWithId(textNode, getDefaultOptions())).toEqual({
777+
expect(serializeNodeWithId(textNode, NodePrivacyLevel.ALLOW, getDefaultOptions())).toEqual({
777778
type: NodeType.Text,
778779
id: jasmine.any(Number) as unknown as number,
779780
textContent: '',
@@ -784,15 +785,17 @@ describe('serializeNodeWithId', () => {
784785
const head = document.getElementsByTagName('head')[0]
785786
const textNode = document.createTextNode(' ')
786787
head.appendChild(textNode)
787-
expect(serializeNodeWithId(textNode, getDefaultOptions())).toEqual(null)
788+
expect(serializeNodeWithId(textNode, NodePrivacyLevel.ALLOW, getDefaultOptions())).toEqual(null)
788789
head.removeChild(textNode)
789790
})
790791
})
791792

792793
describe('CDATA nodes serialization', () => {
793794
it('serializes a CDATA node', () => {
794795
const xmlDocument = new DOMParser().parseFromString('<root></root>', 'text/xml')
795-
expect(serializeNodeWithId(xmlDocument.createCDATASection('foo'), getDefaultOptions())).toEqual({
796+
expect(
797+
serializeNodeWithId(xmlDocument.createCDATASection('foo'), NodePrivacyLevel.ALLOW, getDefaultOptions())
798+
).toEqual({
796799
type: NodeType.CDATA,
797800
id: jasmine.any(Number) as unknown as number,
798801
textContent: '',
@@ -802,47 +805,55 @@ describe('serializeNodeWithId', () => {
802805

803806
it('adds serialized node ids to the provided Set', () => {
804807
const serializedNodeIds = new Set<number>()
805-
const node = serializeNodeWithId(document.createElement('div'), { ...getDefaultOptions(), serializedNodeIds })!
808+
const node = serializeNodeWithId(document.createElement('div'), NodePrivacyLevel.ALLOW, {
809+
...getDefaultOptions(),
810+
serializedNodeIds,
811+
})!
806812
expect(serializedNodeIds).toEqual(new Set([node.id]))
807813
})
808814

809815
describe('ignores some nodes', () => {
810816
it('does not save ignored nodes in the serializedNodeIds set', () => {
811817
const serializedNodeIds = new Set<number>()
812-
serializeNodeWithId(document.createElement('script'), { ...getDefaultOptions(), serializedNodeIds })
818+
serializeNodeWithId(document.createElement('script'), NodePrivacyLevel.ALLOW, {
819+
...getDefaultOptions(),
820+
serializedNodeIds,
821+
})
813822
expect(serializedNodeIds.size).toBe(0)
814823
})
815824

816825
it('does not serialize ignored nodes', () => {
817826
const scriptElement = document.createElement('script')
818-
serializeNodeWithId(scriptElement, getDefaultOptions())
827+
serializeNodeWithId(scriptElement, NodePrivacyLevel.ALLOW, getDefaultOptions())
819828
expect(scope.nodeIds.get(scriptElement)).toBe(undefined)
820829
})
821830

822831
it('ignores script tags', () => {
823-
expect(serializeNodeWithId(document.createElement('script'), getDefaultOptions())).toEqual(null)
832+
const scriptElement = document.createElement('script')
833+
expect(serializeNodeWithId(scriptElement, NodePrivacyLevel.ALLOW, getDefaultOptions())).toEqual(null)
824834
})
825835

826836
it('ignores comments', () => {
827-
expect(serializeNodeWithId(document.createComment('foo'), getDefaultOptions())).toEqual(null)
837+
const commentNode = document.createComment('foo')
838+
expect(serializeNodeWithId(commentNode, NodePrivacyLevel.ALLOW, getDefaultOptions())).toEqual(null)
828839
})
829840

830841
it('ignores link favicons', () => {
831842
const linkElement = document.createElement('link')
832843
linkElement.setAttribute('rel', 'shortcut icon')
833-
expect(serializeNodeWithId(linkElement, getDefaultOptions())).toEqual(null)
844+
expect(serializeNodeWithId(linkElement, NodePrivacyLevel.ALLOW, getDefaultOptions())).toEqual(null)
834845
})
835846

836847
it('ignores meta keywords', () => {
837848
const metaElement = document.createElement('meta')
838849
metaElement.setAttribute('name', 'keywords')
839-
expect(serializeNodeWithId(metaElement, getDefaultOptions())).toEqual(null)
850+
expect(serializeNodeWithId(metaElement, NodePrivacyLevel.ALLOW, getDefaultOptions())).toEqual(null)
840851
})
841852

842853
it('ignores meta name attribute casing', () => {
843854
const metaElement = document.createElement('meta')
844855
metaElement.setAttribute('name', 'KeYwOrDs')
845-
expect(serializeNodeWithId(metaElement, getDefaultOptions())).toEqual(null)
856+
expect(serializeNodeWithId(metaElement, NodePrivacyLevel.ALLOW, getDefaultOptions())).toEqual(null)
846857
})
847858
})
848859

@@ -994,7 +1005,6 @@ describe('serializeDocumentNode handles', function testAllowDomTree() {
9941005
let scope: SerializationScope
9951006

9961007
const getDefaultOptions = (): SerializeOptions => ({
997-
parentNodePrivacyLevel: NodePrivacyLevel.ALLOW,
9981008
serializationContext: getDefaultSerializationContext(),
9991009
configuration: DEFAULT_CONFIGURATION,
10001010
scope,
@@ -1036,13 +1046,9 @@ describe('serializeDocumentNode handles', function testAllowDomTree() {
10361046
})
10371047

10381048
it('a masked DOM Document itself is still serialized ', () => {
1039-
const serializeOptionsMask: SerializeOptions = {
1040-
...getDefaultOptions(),
1041-
parentNodePrivacyLevel: NodePrivacyLevel.MASK,
1042-
}
1043-
expect(serializeDocumentNode(document, serializeOptionsMask)).toEqual({
1049+
expect(serializeDocumentNode(document, NodePrivacyLevel.MASK, getDefaultOptions())).toEqual({
10441050
type: NodeType.Document,
1045-
childNodes: serializeChildNodes(document, serializeOptionsMask),
1051+
childNodes: serializeChildNodes(document, NodePrivacyLevel.MASK, getDefaultOptions()),
10461052
adoptedStyleSheets: undefined,
10471053
})
10481054
})

‎packages/rum/src/domain/record/serialization/serializeNode.ts‎

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,16 @@ import type {
2121
} from '../../../types'
2222
import { NodeType } from '../../../types'
2323
import { getValidTagName } from './serializationUtils'
24-
import type { SerializeOptions } from './serialization.types'
24+
import type { ParentNodePrivacyLevel, SerializeOptions } from './serialization.types'
2525
import { serializeStyleSheets } from './serializeStyleSheets'
2626
import { serializeAttributes } from './serializeAttributes'
2727

28-
export function serializeNodeWithId(node: Node, options: SerializeOptions): SerializedNodeWithId | null {
29-
const serializedNode = serializeNode(node, options)
28+
export function serializeNodeWithId(
29+
node: Node,
30+
parentNodePrivacyLevel: ParentNodePrivacyLevel,
31+
options: SerializeOptions
32+
): SerializedNodeWithId | null {
33+
const serializedNode = serializeNode(node, parentNodePrivacyLevel, options)
3034
if (!serializedNode) {
3135
return null
3236
}
@@ -40,44 +44,57 @@ export function serializeNodeWithId(node: Node, options: SerializeOptions): Seri
4044
return serializedNodeWithId
4145
}
4246

43-
export function serializeChildNodes(node: Node, options: SerializeOptions): SerializedNodeWithId[] {
47+
export function serializeChildNodes(
48+
node: Node,
49+
parentNodePrivacyLevel: ParentNodePrivacyLevel,
50+
options: SerializeOptions
51+
): SerializedNodeWithId[] {
4452
const result: SerializedNodeWithId[] = []
4553
forEachChildNodes(node, (childNode) => {
46-
const serializedChildNode = serializeNodeWithId(childNode, options)
54+
const serializedChildNode = serializeNodeWithId(childNode, parentNodePrivacyLevel, options)
4755
if (serializedChildNode) {
4856
result.push(serializedChildNode)
4957
}
5058
})
5159
return result
5260
}
5361

54-
function serializeNode(node: Node, options: SerializeOptions): SerializedNode | undefined {
62+
function serializeNode(
63+
node: Node,
64+
parentNodePrivacyLevel: ParentNodePrivacyLevel,
65+
options: SerializeOptions
66+
): SerializedNode | undefined {
5567
switch (node.nodeType) {
5668
case node.DOCUMENT_NODE:
57-
return serializeDocumentNode(node as Document, options)
69+
return serializeDocumentNode(node as Document, parentNodePrivacyLevel, options)
5870
case node.DOCUMENT_FRAGMENT_NODE:
59-
return serializeDocumentFragmentNode(node as DocumentFragment, options)
71+
return serializeDocumentFragmentNode(node as DocumentFragment, parentNodePrivacyLevel, options)
6072
case node.DOCUMENT_TYPE_NODE:
6173
return serializeDocumentTypeNode(node as DocumentType)
6274
case node.ELEMENT_NODE:
63-
return serializeElementNode(node as Element, options)
75+
return serializeElementNode(node as Element, parentNodePrivacyLevel, options)
6476
case node.TEXT_NODE:
65-
return serializeTextNode(node as Text, options)
77+
return serializeTextNode(node as Text, parentNodePrivacyLevel)
6678
case node.CDATA_SECTION_NODE:
6779
return serializeCDataNode()
6880
}
6981
}
7082

71-
export function serializeDocumentNode(document: Document, options: SerializeOptions): DocumentNode {
83+
export function serializeDocumentNode(
84+
document: Document,
85+
parentNodePrivacyLevel: ParentNodePrivacyLevel,
86+
options: SerializeOptions
87+
): DocumentNode {
7288
return {
7389
type: NodeType.Document,
74-
childNodes: serializeChildNodes(document, options),
90+
childNodes: serializeChildNodes(document, parentNodePrivacyLevel, options),
7591
adoptedStyleSheets: serializeStyleSheets(document.adoptedStyleSheets),
7692
}
7793
}
7894

7995
function serializeDocumentFragmentNode(
8096
element: DocumentFragment,
97+
parentNodePrivacyLevel: ParentNodePrivacyLevel,
8198
options: SerializeOptions
8299
): DocumentFragmentNode | undefined {
83100
const isShadowRoot = isNodeShadowRoot(element)
@@ -87,7 +104,7 @@ function serializeDocumentFragmentNode(
87104

88105
return {
89106
type: NodeType.DocumentFragment,
90-
childNodes: serializeChildNodes(element, options),
107+
childNodes: serializeChildNodes(element, parentNodePrivacyLevel, options),
91108
isShadowRoot,
92109
adoptedStyleSheets: isShadowRoot ? serializeStyleSheets(element.adoptedStyleSheets) : undefined,
93110
}
@@ -120,13 +137,17 @@ function serializeDocumentTypeNode(documentType: DocumentType): DocumentTypeNode
120137
* - fullscreen mode
121138
*/
122139

123-
function serializeElementNode(element: Element, options: SerializeOptions): ElementNode | undefined {
140+
function serializeElementNode(
141+
element: Element,
142+
parentNodePrivacyLevel: ParentNodePrivacyLevel,
143+
options: SerializeOptions
144+
): ElementNode | undefined {
124145
const tagName = getValidTagName(element.tagName)
125146
const isSVG = isSVGElement(element) || undefined
126147

127148
// For performance reason, we don't use getNodePrivacyLevel directly: we leverage the
128149
// parentNodePrivacyLevel option to avoid iterating over all parents
129-
const nodePrivacyLevel = reducePrivacyLevel(getNodeSelfPrivacyLevel(element), options.parentNodePrivacyLevel)
150+
const nodePrivacyLevel = reducePrivacyLevel(getNodeSelfPrivacyLevel(element), parentNodePrivacyLevel)
130151

131152
if (nodePrivacyLevel === NodePrivacyLevel.HIDDEN) {
132153
const { width, height } = element.getBoundingClientRect()
@@ -156,19 +177,7 @@ function serializeElementNode(element: Element, options: SerializeOptions): Elem
156177
// Do not serialize style children as the css rules are already in the _cssText attribute
157178
tagName !== 'style'
158179
) {
159-
// OBJECT POOLING OPTIMIZATION:
160-
// We should not create a new object systematically as it could impact performances. Try to reuse
161-
// the same object as much as possible, and clone it only if we need to.
162-
let childNodesSerializationOptions
163-
if (options.parentNodePrivacyLevel === nodePrivacyLevel) {
164-
childNodesSerializationOptions = options
165-
} else {
166-
childNodesSerializationOptions = {
167-
...options,
168-
parentNodePrivacyLevel: nodePrivacyLevel,
169-
}
170-
}
171-
childNodes = serializeChildNodes(element, childNodesSerializationOptions)
180+
childNodes = serializeChildNodes(element, nodePrivacyLevel, options)
172181
}
173182

174183
return {
@@ -190,8 +199,8 @@ function isSVGElement(el: Element): boolean {
190199
* for privacy level.
191200
*/
192201

193-
function serializeTextNode(textNode: Text, options: SerializeOptions): TextNode | undefined {
194-
const textContent = getTextContent(textNode, options.parentNodePrivacyLevel)
202+
function serializeTextNode(textNode: Text, parentNodePrivacyLevel: ParentNodePrivacyLevel): TextNode | undefined {
203+
const textContent = getTextContent(textNode, parentNodePrivacyLevel)
195204
if (textContent === undefined) {
196205
return
197206
}

‎packages/rum/src/domain/record/trackers/trackMutation.ts‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,9 +240,8 @@ function processChildListMutations(
240240
}
241241

242242
const serializationStart = timeStampNow()
243-
const serializedNode = serializeNodeWithId(node, {
243+
const serializedNode = serializeNodeWithId(node, parentNodePrivacyLevel, {
244244
serializedNodeIds,
245-
parentNodePrivacyLevel,
246245
serializationContext,
247246
configuration,
248247
scope,

0 commit comments

Comments
 (0)