@@ -205,6 +205,7 @@ function serializeNode(
205205 inlineStylesheet : boolean ;
206206 maskInputOptions : MaskInputOptions ;
207207 recordCanvas : boolean ;
208+ enableStrictPrivacy : boolean ;
208209 } ,
209210) : serializedNode | false {
210211 const {
@@ -214,7 +215,9 @@ function serializeNode(
214215 inlineStylesheet,
215216 maskInputOptions = { } ,
216217 recordCanvas,
218+ enableStrictPrivacy,
217219 } = options ;
220+
218221 switch ( n . nodeType ) {
219222 case n . DOCUMENT_NODE :
220223 return {
@@ -229,7 +232,7 @@ function serializeNode(
229232 systemId : ( n as DocumentType ) . systemId ,
230233 } ;
231234 case n . ELEMENT_NODE :
232- const needBlock = _isBlockedElement (
235+ let needBlock = _isBlockedElement (
233236 n as HTMLElement ,
234237 blockClass ,
235238 blockSelector ,
@@ -318,13 +321,14 @@ function serializeNode(
318321 if ( ( n as HTMLElement ) . scrollTop ) {
319322 attributes . rr_scrollTop = ( n as HTMLElement ) . scrollTop ;
320323 }
321- if ( needBlock ) {
324+ if ( needBlock || ( tagName === 'img' && enableStrictPrivacy ) ) {
322325 const { width, height } = ( n as HTMLElement ) . getBoundingClientRect ( ) ;
323326 attributes = {
324327 class : attributes . class ,
325328 rr_width : `${ width } px` ,
326329 rr_height : `${ height } px` ,
327330 } ;
331+ needBlock = true ;
328332 }
329333 return {
330334 type : NodeType . Element ,
@@ -341,11 +345,38 @@ function serializeNode(
341345 n . parentNode && ( n . parentNode as HTMLElement ) . tagName ;
342346 let textContent = ( n as Text ) . textContent ;
343347 const isStyle = parentTagName === 'STYLE' ? true : undefined ;
348+ /** Determines if this node has been handled already. */
349+ let textContentHandled = false ;
344350 if ( isStyle && textContent ) {
345351 textContent = absoluteToStylesheet ( textContent , getHref ( ) ) ;
352+ textContentHandled = true ;
346353 }
347354 if ( parentTagName === 'SCRIPT' ) {
348355 textContent = 'SCRIPT_PLACEHOLDER' ;
356+ textContentHandled = true ;
357+ } else if ( parentTagName === 'NOSCRIPT' ) {
358+ textContent = '' ;
359+ textContentHandled = true ;
360+ }
361+
362+ // Randomizes the text content to a string of the same length.
363+ if ( enableStrictPrivacy && ! textContentHandled && parentTagName ) {
364+ const IGNORE_TAG_NAMES = new Set ( [
365+ 'HEAD' ,
366+ 'TITLE' ,
367+ 'STYLE' ,
368+ 'SCRIPT' ,
369+ 'HTML' ,
370+ 'BODY' ,
371+ 'NOSCRIPT' ,
372+ ] ) ;
373+ if ( ! IGNORE_TAG_NAMES . has ( parentTagName ) ) {
374+ textContent =
375+ textContent
376+ ?. split ( ' ' )
377+ . map ( ( word ) => Math . random ( ) . toString ( 20 ) . substr ( 2 , word . length ) )
378+ . join ( ' ' ) || '' ;
379+ }
349380 }
350381 return {
351382 type : NodeType . Text ,
@@ -472,6 +503,7 @@ export function serializeNodeWithId(
472503 slimDOMOptions : SlimDOMOptions ;
473504 recordCanvas ?: boolean ;
474505 preserveWhiteSpace ?: boolean ;
506+ enableStrictPrivacy : boolean ;
475507 } ,
476508) : serializedNodeWithId | null {
477509 const {
@@ -484,6 +516,7 @@ export function serializeNodeWithId(
484516 maskInputOptions = { } ,
485517 slimDOMOptions,
486518 recordCanvas = false ,
519+ enableStrictPrivacy,
487520 } = options ;
488521 let { preserveWhiteSpace = true } = options ;
489522 const _serializedNode = serializeNode ( n , {
@@ -493,6 +526,7 @@ export function serializeNodeWithId(
493526 inlineStylesheet,
494527 maskInputOptions,
495528 recordCanvas,
529+ enableStrictPrivacy,
496530 } ) ;
497531 if ( ! _serializedNode ) {
498532 // TODO: dev only
@@ -524,6 +558,16 @@ export function serializeNodeWithId(
524558 let recordChild = ! skipChild ;
525559 if ( serializedNode . type === NodeType . Element ) {
526560 recordChild = recordChild && ! serializedNode . needBlock ;
561+
562+ /** Highlight Code Begin */
563+ // Remove the image's src if enableStrictPrivacy.
564+ if ( serializedNode . needBlock && serializedNode . tagName === 'img' ) {
565+ const clone = n . cloneNode ( ) ;
566+ ( ( clone as unknown ) as HTMLImageElement ) . src = '' ;
567+ map [ id ] = clone as INode ;
568+ }
569+ /** Highlight Code End */
570+
527571 // this property was not needed in replay side
528572 delete serializedNode . needBlock ;
529573 }
@@ -552,6 +596,7 @@ export function serializeNodeWithId(
552596 slimDOMOptions,
553597 recordCanvas,
554598 preserveWhiteSpace,
599+ enableStrictPrivacy,
555600 } ) ;
556601 if ( serializedChildNode ) {
557602 serializedNode . childNodes . push ( serializedChildNode ) ;
@@ -570,15 +615,17 @@ function snapshot(
570615 slimDOM ?: boolean | SlimDOMOptions ;
571616 recordCanvas ?: boolean ;
572617 blockSelector ?: string | null ;
618+ enableStrictPrivacy : boolean ;
573619 } ,
574620) : [ serializedNodeWithId | null , idNodeMap ] {
575621 const {
576- blockClass = 'rr -block' ,
622+ blockClass = 'highlight -block' ,
577623 inlineStylesheet = true ,
578624 recordCanvas = false ,
579625 blockSelector = null ,
580626 maskAllInputs = false ,
581627 slimDOM = false ,
628+ enableStrictPrivacy = false ,
582629 } = options || { } ;
583630 const idNodeMap : idNodeMap = { } ;
584631 const maskInputOptions : MaskInputOptions =
@@ -632,6 +679,7 @@ function snapshot(
632679 maskInputOptions,
633680 slimDOMOptions,
634681 recordCanvas,
682+ enableStrictPrivacy,
635683 } ) ,
636684 idNodeMap ,
637685 ] ;
0 commit comments