11import { NodePrivacyLevel , shouldMaskNode } from '@datadog/browser-rum-core'
22import { isSafari } from '@datadog/browser-core'
3- import { getElementInputValue , switchToAbsoluteUrl , getValidTagName } from './serializationUtils'
3+ import { getElementInputValue , normalizedTagName , switchToAbsoluteUrl } from './serializationUtils'
44import { serializeAttribute } from './serializeAttribute'
55import type { SerializationTransaction } from './serializationTransaction'
66import { SerializationKind } from './serializationTransaction'
7+ import type { VirtualAttributes } from './serialization.types'
78
89export function serializeAttributes (
910 element : Element ,
1011 nodePrivacyLevel : NodePrivacyLevel ,
1112 transaction : SerializationTransaction
12- ) : Record < string , string | number | boolean > {
13+ ) : Record < string , boolean | number | string > {
14+ return {
15+ ...serializeDOMAttributes ( element , nodePrivacyLevel , transaction ) ,
16+ ...serializeVirtualAttributes ( element , nodePrivacyLevel , transaction ) ,
17+ }
18+ }
19+
20+ export function serializeDOMAttributes (
21+ element : Element ,
22+ nodePrivacyLevel : NodePrivacyLevel ,
23+ transaction : SerializationTransaction
24+ ) : Record < string , boolean | string > {
1325 if ( nodePrivacyLevel === NodePrivacyLevel . HIDDEN ) {
1426 return { }
1527 }
16- const safeAttrs : Record < string , string | number | boolean > = { }
17- const tagName = getValidTagName ( element . tagName )
18- const doc = element . ownerDocument
28+
29+ const attrs : Record < string , string | boolean > = { }
30+ const tagName = normalizedTagName ( element )
1931
2032 for ( let i = 0 ; i < element . attributes . length ; i += 1 ) {
2133 const attribute = element . attributes . item ( i ) !
2234 const attributeName = attribute . name
2335 const attributeValue = serializeAttribute ( element , nodePrivacyLevel , attributeName , transaction . scope . configuration )
2436 if ( attributeValue !== null ) {
25- safeAttrs [ attributeName ] = attributeValue
37+ attrs [ attributeName ] = attributeValue
2638 }
2739 }
2840
@@ -32,7 +44,7 @@ export function serializeAttributes(
3244 ) {
3345 const formValue = getElementInputValue ( element , nodePrivacyLevel )
3446 if ( formValue !== undefined ) {
35- safeAttrs . value = formValue
47+ attrs . value = formValue
3648 }
3749 }
3850
@@ -43,17 +55,50 @@ export function serializeAttributes(
4355 // For privacy=`MASK`, all the values would be the same, so skip.
4456 const optionElement = element as HTMLOptionElement
4557 if ( optionElement . selected ) {
46- safeAttrs . selected = optionElement . selected
58+ attrs . selected = optionElement . selected
4759 }
4860 }
4961
62+ /**
63+ * Forms: input[type=checkbox,radio]
64+ * The `checked` property for <input> is a little bit special:
65+ * 1. el.checked is a setter that returns if truthy.
66+ * 2. getAttribute returns the string value
67+ * getAttribute('checked') does not sync with `Element.checked`, so use JS property
68+ * NOTE: `checked` property exists on `HTMLInputElement`. For serializer assumptions, we check for type=radio|check.
69+ */
70+ const inputElement = element as HTMLInputElement
71+ if ( tagName === 'input' && ( inputElement . type === 'radio' || inputElement . type === 'checkbox' ) ) {
72+ if ( nodePrivacyLevel === NodePrivacyLevel . ALLOW ) {
73+ attrs . checked = ! ! inputElement . checked
74+ } else if ( shouldMaskNode ( inputElement , nodePrivacyLevel ) ) {
75+ delete attrs . checked
76+ }
77+ }
78+
79+ return attrs
80+ }
81+
82+ export function serializeVirtualAttributes (
83+ element : Element ,
84+ nodePrivacyLevel : NodePrivacyLevel ,
85+ transaction : SerializationTransaction
86+ ) : VirtualAttributes {
87+ if ( nodePrivacyLevel === NodePrivacyLevel . HIDDEN ) {
88+ return { }
89+ }
90+
91+ const attrs : VirtualAttributes = { }
92+ const doc = element . ownerDocument
93+ const tagName = normalizedTagName ( element )
94+
5095 // remote css
5196 if ( tagName === 'link' ) {
5297 const stylesheet = Array . from ( doc . styleSheets ) . find ( ( s ) => s . href === ( element as HTMLLinkElement ) . href )
5398 const cssText = getCssRulesString ( stylesheet )
5499 if ( cssText && stylesheet ) {
55100 transaction . addMetric ( 'cssText' , cssText . length )
56- safeAttrs . _cssText = cssText
101+ attrs . _cssText = cssText
57102 }
58103 }
59104
@@ -62,24 +107,7 @@ export function serializeAttributes(
62107 const cssText = getCssRulesString ( ( element as HTMLStyleElement ) . sheet )
63108 if ( cssText ) {
64109 transaction . addMetric ( 'cssText' , cssText . length )
65- safeAttrs . _cssText = cssText
66- }
67- }
68-
69- /**
70- * Forms: input[type=checkbox,radio]
71- * The `checked` property for <input> is a little bit special:
72- * 1. el.checked is a setter that returns if truthy.
73- * 2. getAttribute returns the string value
74- * getAttribute('checked') does not sync with `Element.checked`, so use JS property
75- * NOTE: `checked` property exists on `HTMLInputElement`. For serializer assumptions, we check for type=radio|check.
76- */
77- const inputElement = element as HTMLInputElement
78- if ( tagName === 'input' && ( inputElement . type === 'radio' || inputElement . type === 'checkbox' ) ) {
79- if ( nodePrivacyLevel === NodePrivacyLevel . ALLOW ) {
80- safeAttrs . checked = ! ! inputElement . checked
81- } else if ( shouldMaskNode ( inputElement , nodePrivacyLevel ) ) {
82- delete safeAttrs . checked
110+ attrs . _cssText = cssText
83111 }
84112 }
85113
@@ -88,7 +116,7 @@ export function serializeAttributes(
88116 */
89117 if ( tagName === 'audio' || tagName === 'video' ) {
90118 const mediaElement = element as HTMLMediaElement
91- safeAttrs . rr_mediaState = mediaElement . paused ? 'paused' : 'played'
119+ attrs . rr_mediaState = mediaElement . paused ? 'paused' : 'played'
92120 }
93121
94122 /**
@@ -111,13 +139,13 @@ export function serializeAttributes(
111139 break
112140 }
113141 if ( scrollLeft ) {
114- safeAttrs . rr_scrollLeft = scrollLeft
142+ attrs . rr_scrollLeft = scrollLeft
115143 }
116144 if ( scrollTop ) {
117- safeAttrs . rr_scrollTop = scrollTop
145+ attrs . rr_scrollTop = scrollTop
118146 }
119147
120- return safeAttrs
148+ return attrs
121149}
122150
123151export function getCssRulesString ( cssStyleSheet : CSSStyleSheet | undefined | null ) : string | null {
0 commit comments