@@ -338,6 +338,79 @@ describe('record', function (this: ISuite) {
338338 await assertSnapshot ( ctx . events ) ;
339339 } ) ;
340340
341+ it ( 'skips vendor-prefixed CSS rules not supported by the current browser' , async ( ) => {
342+ // ::-moz-focus-inner is valid in Firefox but rejected by other browsers.
343+ // The insertRule proxy catches any parse error and swallows it, so recording
344+ // continues without throwing. The rule IS still emitted as a StyleSheetRule
345+ // event so it can be replayed in a browser that supports it.
346+ await ctx . page . evaluate ( ( ) => {
347+ const { record } = ( window as unknown as IWindow ) . rrweb ;
348+ record ( {
349+ emit : ( window as unknown as IWindow ) . emit ,
350+ } ) ;
351+
352+ const styleElement = document . createElement ( 'style' ) ;
353+ document . head . appendChild ( styleElement ) ;
354+
355+ const styleSheet = < CSSStyleSheet > styleElement . sheet ;
356+ // insertRule calls must be deferred past initial serialization
357+ setTimeout ( ( ) => {
358+ styleSheet . insertRule ( '.foo::-moz-focus-inner { border-style: none; }' ) ;
359+ styleSheet . insertRule ( 'body { color: #fff; }' ) ;
360+ } , 0 ) ;
361+ } ) ;
362+ await ctx . page . waitForTimeout ( 50 ) ;
363+
364+ const styleSheetRuleEvents = ctx . events . filter (
365+ ( e ) =>
366+ e . type === EventType . IncrementalSnapshot &&
367+ e . data . source === IncrementalSource . StyleSheetRule ,
368+ ) ;
369+ const addRules = styleSheetRuleEvents . filter ( ( e ) =>
370+ Boolean ( ( e . data as styleSheetRuleData ) . adds ) ,
371+ ) ;
372+ // Both rules captured as events (for replay in the browser that supports them)
373+ expect ( addRules . length ) . toEqual ( 2 ) ;
374+ expect ( ( addRules [ 0 ] . data as styleSheetRuleData ) . adds ) . toEqual ( [
375+ { rule : '.foo::-moz-focus-inner { border-style: none; }' } ,
376+ ] ) ;
377+ expect ( ( addRules [ 1 ] . data as styleSheetRuleData ) . adds ) . toEqual ( [
378+ { rule : 'body { color: #fff; }' } ,
379+ ] ) ;
380+ } ) ;
381+
382+ it ( 'skips vendor-prefixed CSS rules inside nested rules not supported by the current browser' , async ( ) => {
383+ await ctx . page . evaluate ( ( ) => {
384+ const { record } = ( window as unknown as IWindow ) . rrweb ;
385+ record ( {
386+ emit : ( window as unknown as IWindow ) . emit ,
387+ } ) ;
388+
389+ const styleElement = document . createElement ( 'style' ) ;
390+ document . head . appendChild ( styleElement ) ;
391+
392+ const styleSheet = < CSSStyleSheet > styleElement . sheet ;
393+ styleSheet . insertRule ( '@media {}' ) ;
394+ const atMediaRule = styleSheet . cssRules [ 0 ] as CSSMediaRule ;
395+
396+ setTimeout ( ( ) => {
397+ atMediaRule . insertRule ( '.foo::-moz-focus-inner { border-style: none; }' , 0 ) ;
398+ atMediaRule . insertRule ( 'body { color: #fff; }' , 0 ) ;
399+ } , 0 ) ;
400+ } ) ;
401+ await ctx . page . waitForTimeout ( 50 ) ;
402+
403+ const styleSheetRuleEvents = ctx . events . filter (
404+ ( e ) =>
405+ e . type === EventType . IncrementalSnapshot &&
406+ e . data . source === IncrementalSource . StyleSheetRule ,
407+ ) ;
408+ const addRuleCount = styleSheetRuleEvents . filter ( ( e ) =>
409+ Boolean ( ( e . data as styleSheetRuleData ) . adds ) ,
410+ ) . length ;
411+ expect ( addRuleCount ) . toEqual ( 2 ) ;
412+ } ) ;
413+
341414 it ( 'captures stylesheet rules with deprecated addRule & removeRule properties' , async ( ) => {
342415 await ctx . page . evaluate ( ( ) => {
343416 const { record } = ( window as unknown as IWindow ) . rrweb ;
0 commit comments