Skip to content

Commit

Permalink
[REPLAY] Add support for adoptedStylesheets in serialisation
Browse files Browse the repository at this point in the history
  • Loading branch information
ThibautGeriz committed Jan 4, 2023
1 parent 8f4eacf commit d86c640
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 1 deletion.
4 changes: 4 additions & 0 deletions packages/core/src/tools/browserDetection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@ export function isIE() {
export function isChromium() {
return !!(window as any).chrome || /HeadlessChrome/.test(window.navigator.userAgent)
}

export function isAdoptedStyleSheetsSupported() {
return Boolean((document as any).adoptedStyleSheets)
}
12 changes: 12 additions & 0 deletions packages/rum/src/domain/record/browser.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// TODO: remove this once typescript has been update to 4.8+
declare global {
interface ShadowRoot {
adoptedStyleSheets: CSSStyleSheet[] | undefined
}

interface Document {
adoptedStyleSheets: CSSStyleSheet[] | undefined
}
}

export {}
27 changes: 26 additions & 1 deletion packages/rum/src/domain/record/serializationUtils.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { isIE } from '@datadog/browser-core'
import { isAdoptedStyleSheetsSupported, isIE } from '@datadog/browser-core'
import { NodePrivacyLevel } from '../../constants'
import {
getSerializedNodeId,
hasSerializedNode,
setSerializedNodeId,
getElementInputValue,
switchToAbsoluteUrl,
getStyleSheets,
} from './serializationUtils'

describe('serialized Node storage in DOM Nodes', () => {
Expand Down Expand Up @@ -183,3 +184,27 @@ describe('switchToAbsoluteUrl', () => {
})
})
})

describe('getStyleSheets', () => {
beforeEach(() => {
if (!isAdoptedStyleSheetsSupported()) {
pending('no adoptedStyleSheets support')
}
})
it('should return undefined if no stylesheets', () => {
expect(getStyleSheets(undefined)).toBe(undefined)
expect(getStyleSheets([])).toBe(undefined)
})

it('should return serialized stylesheet', () => {
const disabledStylesheet = new CSSStyleSheet({ disabled: true })
disabledStylesheet.insertRule('div { width: 100%; }')
const printStylesheet = new CSSStyleSheet({ disabled: false, media: 'print' })
printStylesheet.insertRule('a { color: red; }')

expect(getStyleSheets([disabledStylesheet, printStylesheet])).toEqual([
{ cssRules: ['div { width: 100%; }'], disabled: true, media: [] },
{ cssRules: ['a { color: red; }'], disabled: false, media: ['print'] },
])
})
})
18 changes: 18 additions & 0 deletions packages/rum/src/domain/record/serializationUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { buildUrl } from '@datadog/browser-core'
import { getParentNode, isNodeShadowRoot } from '@datadog/browser-rum-core'
import type { NodePrivacyLevel } from '../../constants'
import { CENSORED_STRING_MARK } from '../../constants'
import type { StyleSheet } from '../../types'
import { shouldMaskNode } from './privacy'

export type NodeWithSerializedNode = Node & { s: 'Node with serialized node' }
Expand Down Expand Up @@ -106,3 +107,20 @@ export function makeUrlAbsolute(url: string, baseUrl: string): string {
return url
}
}

export function getStyleSheets(cssStyleSheets: CSSStyleSheet[] | undefined): StyleSheet[] | undefined {
if (cssStyleSheets === undefined || cssStyleSheets.length === 0) {
return undefined
}
return cssStyleSheets.map((cssStyleSheet) => {
const rules = cssStyleSheet.rules || cssStyleSheet.cssRules
const cssRules = Array.from(rules, (cssRule) => cssRule.cssText)

const styleSheet: StyleSheet = {
cssRules,
disabled: cssStyleSheet.disabled,
media: Array.from(cssStyleSheet.media),
}
return styleSheet
})
}
4 changes: 4 additions & 0 deletions packages/rum/src/domain/record/serialize.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ describe('serializeNodeWithId', () => {
jasmine.objectContaining({ type: NodeType.DocumentType, name: 'html', publicId: '', systemId: '' }),
jasmine.objectContaining({ type: NodeType.Element, tagName: 'html' }),
],
adoptedStyleSheets: undefined,
id: jasmine.any(Number) as unknown as number,
})
})
Expand Down Expand Up @@ -437,6 +438,7 @@ describe('serializeNodeWithId', () => {
isShadowRoot: true,
childNodes: [],
id: jasmine.any(Number) as unknown as number,
adoptedStyleSheets: undefined,
},
],
id: jasmine.any(Number) as unknown as number,
Expand Down Expand Up @@ -468,6 +470,7 @@ describe('serializeNodeWithId', () => {
{
type: NodeType.DocumentFragment,
isShadowRoot: true,
adoptedStyleSheets: undefined,
childNodes: [
{
type: NodeType.Element,
Expand Down Expand Up @@ -692,6 +695,7 @@ describe('serializeDocumentNode handles', function testAllowDomTree() {
expect(serializeDocumentNode(document, serializeOptionsMask)).toEqual({
type: NodeType.Document,
childNodes: serializeChildNodes(document, serializeOptionsMask),
adoptedStyleSheets: undefined,
})
})

Expand Down
4 changes: 4 additions & 0 deletions packages/rum/src/domain/record/serialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@ import {
setSerializedNodeId,
getElementInputValue,
switchToAbsoluteUrl,
getStyleSheets,
} from './serializationUtils'
import { forEach } from './utils'
import type { ElementsScrollPositions } from './elementsScrollPositions'
import type { ShadowRootsController } from './shadowRootsController'
import './browser.types'

// Those values are the only one that can be used when inheriting privacy levels from parent to
// children during serialization, since HIDDEN and IGNORE shouldn't serialize their children. This
Expand Down Expand Up @@ -125,6 +127,7 @@ export function serializeDocumentNode(document: Document, options: SerializeOpti
return {
type: NodeType.Document,
childNodes: serializeChildNodes(document, options),
adoptedStyleSheets: getStyleSheets(document.adoptedStyleSheets),
}
}

Expand Down Expand Up @@ -155,6 +158,7 @@ function serializeDocumentFragmentNode(
type: NodeType.DocumentFragment,
childNodes,
isShadowRoot,
adoptedStyleSheets: isShadowRoot ? getStyleSheets(element.adoptedStyleSheets) : undefined,
}
}

Expand Down

0 comments on commit d86c640

Please sign in to comment.