@@ -29,7 +29,6 @@ import {
29
29
toLowerCase ,
30
30
extractFileExtension ,
31
31
absolutifyURLs ,
32
- markCssSplits ,
33
32
} from './utils' ;
34
33
import dom from '@posthog/rrweb-utils' ;
35
34
@@ -407,7 +406,6 @@ function serializeNode(
407
406
* `newlyAddedElement: true` skips scrollTop and scrollLeft check
408
407
*/
409
408
newlyAddedElement ?: boolean ;
410
- cssCaptured ?: boolean ;
411
409
} ,
412
410
) : serializedNode | false {
413
411
const {
@@ -425,7 +423,6 @@ function serializeNode(
425
423
recordCanvas,
426
424
keepIframeSrcFn,
427
425
newlyAddedElement = false ,
428
- cssCaptured = false ,
429
426
} = options ;
430
427
// Only record root id when document object is not the base document
431
428
const rootId = getRootId ( doc , mirror ) ;
@@ -472,7 +469,6 @@ function serializeNode(
472
469
needsMask,
473
470
maskTextFn,
474
471
rootId,
475
- cssCaptured,
476
472
} ) ;
477
473
case n . CDATA_SECTION_NODE :
478
474
return {
@@ -504,38 +500,48 @@ function serializeTextNode(
504
500
needsMask : boolean ;
505
501
maskTextFn : MaskTextFn | undefined ;
506
502
rootId : number | undefined ;
507
- cssCaptured ?: boolean ;
508
503
} ,
509
504
) : serializedNode {
510
- const { needsMask, maskTextFn, rootId, cssCaptured } = options ;
505
+ const { needsMask, maskTextFn, rootId } = options ;
511
506
// The parent node may not be a html element which has a tagName attribute.
512
507
// So just let it be undefined which is ok in this use case.
513
508
const parent = dom . parentNode ( n ) ;
514
509
const parentTagName = parent && ( parent as HTMLElement ) . tagName ;
515
- let textContent : string | null = '' ;
510
+ let text = dom . textContent ( n ) ;
516
511
const isStyle = parentTagName === 'STYLE' ? true : undefined ;
517
512
const isScript = parentTagName === 'SCRIPT' ? true : undefined ;
518
- if ( isScript ) {
519
- textContent = 'SCRIPT_PLACEHOLDER' ;
520
- } else if ( ! cssCaptured ) {
521
- textContent = dom . textContent ( n ) ;
522
- if ( isStyle && textContent ) {
523
- // mutation only: we don't need to use stringifyStylesheet
524
- // as a <style> text node mutation obliterates any previous
525
- // programmatic rule manipulation (.insertRule etc.)
526
- // so the current textContent represents the most up to date state
527
- textContent = absolutifyURLs ( textContent , getHref ( options . doc ) ) ;
513
+ if ( isStyle && text ) {
514
+ try {
515
+ // try to read style sheet
516
+ if ( n . nextSibling || n . previousSibling ) {
517
+ // This is not the only child of the stylesheet.
518
+ // We can't read all of the sheet's .cssRules and expect them
519
+ // to _only_ include the current rule(s) added by the text node.
520
+ // So we'll be conservative and keep textContent as-is.
521
+ } else if ( ( parent as HTMLStyleElement ) . sheet ?. cssRules ) {
522
+ text = stringifyStylesheet ( ( parent as HTMLStyleElement ) . sheet ! ) ;
523
+ }
524
+ } catch ( err ) {
525
+ console . warn (
526
+ `Cannot get CSS styles from text's parentNode. Error: ${ err as string } ` ,
527
+ n ,
528
+ ) ;
528
529
}
530
+ text = absolutifyURLs ( text , getHref ( options . doc ) ) ;
531
+ }
532
+ if ( isScript ) {
533
+ text = 'SCRIPT_PLACEHOLDER' ;
529
534
}
530
- if ( ! isStyle && ! isScript && textContent && needsMask ) {
531
- textContent = maskTextFn
532
- ? maskTextFn ( textContent , dom . parentElement ( n ) )
533
- : textContent . replace ( / [ \S ] / g, '*' ) ;
535
+ if ( ! isStyle && ! isScript && text && needsMask ) {
536
+ text = maskTextFn
537
+ ? maskTextFn ( text , dom . parentElement ( n ) )
538
+ : text . replace ( / [ \S ] / g, '*' ) ;
534
539
}
535
540
536
541
return {
537
542
type : NodeType . Text ,
538
- textContent : textContent || '' ,
543
+ textContent : text || '' ,
544
+ isStyle,
539
545
rootId,
540
546
} ;
541
547
}
@@ -628,14 +634,17 @@ function serializeElementNode(
628
634
}
629
635
}
630
636
}
631
- if ( tagName === 'style' && ( n as HTMLStyleElement ) . sheet ) {
632
- let cssText = stringifyStylesheet (
637
+ // dynamic stylesheet
638
+ if (
639
+ tagName === 'style' &&
640
+ ( n as HTMLStyleElement ) . sheet &&
641
+ // TODO: Currently we only try to get dynamic stylesheet when it is an empty style element
642
+ ! ( n . innerText || dom . textContent ( n ) || '' ) . trim ( ) . length
643
+ ) {
644
+ const cssText = stringifyStylesheet (
633
645
( n as HTMLStyleElement ) . sheet as CSSStyleSheet ,
634
646
) ;
635
647
if ( cssText ) {
636
- if ( n . childNodes . length > 1 ) {
637
- cssText = markCssSplits ( cssText , n as HTMLStyleElement ) ;
638
- }
639
648
attributes . _cssText = cssText ;
640
649
}
641
650
}
@@ -961,7 +970,6 @@ export function serializeNodeWithId(
961
970
node : serializedElementNodeWithId ,
962
971
) => unknown ;
963
972
stylesheetLoadTimeout ?: number ;
964
- cssCaptured ?: boolean ;
965
973
} ,
966
974
) : serializedNodeWithId | null {
967
975
const {
@@ -987,7 +995,6 @@ export function serializeNodeWithId(
987
995
stylesheetLoadTimeout = 5000 ,
988
996
keepIframeSrcFn = ( ) => false ,
989
997
newlyAddedElement = false ,
990
- cssCaptured = false ,
991
998
} = options ;
992
999
let { needsMask } = options ;
993
1000
let { preserveWhiteSpace = true } = options ;
@@ -1018,7 +1025,6 @@ export function serializeNodeWithId(
1018
1025
recordCanvas,
1019
1026
keepIframeSrcFn,
1020
1027
newlyAddedElement,
1021
- cssCaptured,
1022
1028
} ) ;
1023
1029
if ( ! _serializedNode ) {
1024
1030
// TODO: dev only
@@ -1034,6 +1040,7 @@ export function serializeNodeWithId(
1034
1040
slimDOMExcluded ( _serializedNode , slimDOMOptions ) ||
1035
1041
( ! preserveWhiteSpace &&
1036
1042
_serializedNode . type === NodeType . Text &&
1043
+ ! _serializedNode . isStyle &&
1037
1044
! _serializedNode . textContent . replace ( / ^ \s + | \s + $ / gm, '' ) . length )
1038
1045
) {
1039
1046
id = IGNORED_NODE ;
@@ -1098,7 +1105,6 @@ export function serializeNodeWithId(
1098
1105
onStylesheetLoad,
1099
1106
stylesheetLoadTimeout,
1100
1107
keepIframeSrcFn,
1101
- cssCaptured : false ,
1102
1108
} ;
1103
1109
1104
1110
if (
@@ -1108,13 +1114,6 @@ export function serializeNodeWithId(
1108
1114
) {
1109
1115
// value parameter in DOM reflects the correct value, so ignore childNode
1110
1116
} else {
1111
- if (
1112
- serializedNode . type === NodeType . Element &&
1113
- ( serializedNode as elementNode ) . attributes . _cssText !== undefined &&
1114
- typeof serializedNode . attributes . _cssText === 'string'
1115
- ) {
1116
- bypassOptions . cssCaptured = true ;
1117
- }
1118
1117
for ( const childN of Array . from ( dom . childNodes ( n ) ) ) {
1119
1118
const serializedChildNode = serializeNodeWithId ( childN , bypassOptions ) ;
1120
1119
if ( serializedChildNode ) {
0 commit comments