Skip to content

Commit

Permalink
⚡ serialize scroll positions only for full snapshots
Browse files Browse the repository at this point in the history
  • Loading branch information
bcaudan committed Aug 5, 2022
1 parent 8a218c1 commit eca9052
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 23 deletions.
1 change: 1 addition & 0 deletions packages/rum/src/domain/record/mutationObserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ function processChildListMutations(
document,
serializedNodeIds,
parentNodePrivacyLevel,
serializationContext: 'mutation',
})
if (!serializedNode) {
continue
Expand Down
51 changes: 36 additions & 15 deletions packages/rum/src/domain/record/serialize.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
import type { ElementNode, SerializedNodeWithId, TextNode } from '../../types'
import { NodeType } from '../../types'
import { hasSerializedNode } from './serializationUtils'
import type { SerializeOptions } from './serialize'
import type { SerializeOptions, SerializationContext } from './serialize'
import {
serializeDocument,
serializeNodeWithId,
Expand All @@ -30,6 +30,7 @@ import { MAX_ATTRIBUTE_VALUE_CHAR_LENGTH } from './privacy'
const DEFAULT_OPTIONS: SerializeOptions = {
document,
parentNodePrivacyLevel: NodePrivacyLevel.ALLOW,
serializationContext: 'full-snapshot',
}

describe('serializeNodeWithId', () => {
Expand Down Expand Up @@ -113,22 +114,41 @@ describe('serializeNodeWithId', () => {
style: 'width: 10px;',
})
})

it('serializes scroll position', () => {
const element = document.createElement('div')
Object.assign(element.style, { width: '100px', height: '100px', overflow: 'scroll' })
const inner = document.createElement('div')
Object.assign(inner.style, { width: '200px', height: '200px' })
element.appendChild(inner)
sandbox.appendChild(element)
element.scrollBy(10, 20)

expect((serializeNodeWithId(element, DEFAULT_OPTIONS)! as ElementNode).attributes).toEqual(
jasmine.objectContaining({
;[
{
description: 'serializes scroll position during full snapshot',
serializationContext: 'full-snapshot' as SerializationContext,
shouldSerializeScroll: true,
},
{
description: 'does not serialize scroll position during mutation',
serializationContext: 'mutation' as SerializationContext,
shouldSerializeScroll: false,
},
].forEach(({ description, serializationContext, shouldSerializeScroll }) => {
it(description, () => {
const element = document.createElement('div')
Object.assign(element.style, { width: '100px', height: '100px', overflow: 'scroll' })
const inner = document.createElement('div')
Object.assign(inner.style, { width: '200px', height: '200px' })
element.appendChild(inner)
sandbox.appendChild(element)
element.scrollBy(10, 20)

const serializedAttributes = (
serializeNodeWithId(element, { ...DEFAULT_OPTIONS, serializationContext })! as ElementNode
).attributes
const attributesWithScrollPositions = jasmine.objectContaining({
rr_scrollTop: 20,
rr_scrollLeft: 10,
})
)

if (shouldSerializeScroll) {
expect(serializedAttributes).toEqual(attributesWithScrollPositions)
} else {
expect(serializedAttributes).not.toEqual(attributesWithScrollPositions)
}
})
})

it('ignores white space in <head>', () => {
Expand Down Expand Up @@ -480,9 +500,10 @@ describe('serializeDocumentNode handles', function testAllowDomTree() {
})

it('a masked DOM Document itself is still serialized ', () => {
const serializeOptionsMask = {
const serializeOptionsMask: SerializeOptions = {
document,
parentNodePrivacyLevel: NodePrivacyLevel.MASK,
serializationContext: 'full-snapshot',
}
expect(serializeDocumentNode(document, serializeOptionsMask)).toEqual({
type: NodeType.Document,
Expand Down
23 changes: 15 additions & 8 deletions packages/rum/src/domain/record/serialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,14 @@ type ParentNodePrivacyLevel =
| typeof NodePrivacyLevel.MASK
| typeof NodePrivacyLevel.MASK_USER_INPUT

export type SerializationContext = 'full-snapshot' | 'mutation'

export interface SerializeOptions {
document: Document
serializedNodeIds?: Set<number>
ignoreWhiteSpace?: boolean
parentNodePrivacyLevel: ParentNodePrivacyLevel
serializationContext: SerializationContext
}

export function serializeDocument(
Expand All @@ -49,6 +52,7 @@ export function serializeDocument(
return serializeNodeWithId(document, {
document,
parentNodePrivacyLevel: defaultPrivacyLevel,
serializationContext: 'full-snapshot',
})!
}

Expand Down Expand Up @@ -145,7 +149,7 @@ export function serializeElementNode(element: Element, options: SerializeOptions
return
}

const attributes = getAttributesForPrivacyLevel(element, nodePrivacyLevel)
const attributes = getAttributesForPrivacyLevel(element, nodePrivacyLevel, options.serializationContext)

let childNodes: SerializedNodeWithId[] = []
if (element.childNodes.length) {
Expand Down Expand Up @@ -303,7 +307,8 @@ function isSVGElement(el: Element): boolean {

function getAttributesForPrivacyLevel(
element: Element,
nodePrivacyLevel: NodePrivacyLevel
nodePrivacyLevel: NodePrivacyLevel,
serializationContext: SerializationContext
): Record<string, string | number | boolean> {
if (nodePrivacyLevel === NodePrivacyLevel.HIDDEN) {
return {}
Expand Down Expand Up @@ -393,13 +398,15 @@ function getAttributesForPrivacyLevel(
}

/**
* Serialize the scroll state for each element
* Serialize the scroll state for each element only for full snapshot
*/
if (element.scrollLeft) {
safeAttrs.rr_scrollLeft = Math.round(element.scrollLeft)
}
if (element.scrollTop) {
safeAttrs.rr_scrollTop = Math.round(element.scrollTop)
if (serializationContext === 'full-snapshot') {
if (element.scrollLeft) {
safeAttrs.rr_scrollLeft = Math.round(element.scrollLeft)
}
if (element.scrollTop) {
safeAttrs.rr_scrollTop = Math.round(element.scrollTop)
}
}

return safeAttrs
Expand Down
1 change: 1 addition & 0 deletions packages/rum/test/htmlAst.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const generateLeanSerializedDoc = (htmlContent: string, privacyTag: strin
serializeNodeWithId(newDoc, {
document: newDoc,
parentNodePrivacyLevel: NodePrivacyLevel.ALLOW,
serializationContext: 'full-snapshot',
})! as unknown as Record<string, unknown>
) as unknown as SerializedNodeWithId
return serializedDoc
Expand Down

0 comments on commit eca9052

Please sign in to comment.