diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index 216c76a392..fd4ac71060 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -271,7 +271,10 @@ export default class MutationBuffer { const shadowHost: Element | null = n.getRootNode ? (n.getRootNode() as ShadowRoot)?.host : null; - const notInDoc = !this.doc.contains(n) && !this.doc.contains(shadowHost); + // ensure shadowHost is a Node, or doc.contains will throw an error + const notInDoc = + !this.doc.contains(n) && + (!(shadowHost instanceof Node) || !this.doc.contains(shadowHost)); if (!n.parentNode || notInDoc) { return; } @@ -359,13 +362,16 @@ export default class MutationBuffer { if (!node) { for (let index = addList.length - 1; index >= 0; index--) { const _node = addList.get(index)!; - const parentId = this.mirror.getId( - (_node.value.parentNode as Node) as INode, - ); - const nextId = getNextId(_node.value); - if (parentId !== -1 && nextId !== -1) { - node = _node; - break; + // ensure _node is defined before attempting to find value + if (_node) { + const parentId = this.mirror.getId( + (_node.value.parentNode as Node) as INode, + ); + const nextId = getNextId(_node.value); + if (parentId !== -1 && nextId !== -1) { + node = _node; + break; + } } } } diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index 2c96c8f768..bd4ecca4f5 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -194,21 +194,43 @@ function initMoveObserver( }, callbackThreshold, ); - const updatePosition = throttle( + + // update position for mouse, touch, and drag events (drag event extends mouse event) + function handleUpdatePositionEvent(evt: MouseEvent | TouchEvent) { + const target = getEventTarget(evt); + const { clientX, clientY } = isTouchEvent(evt) + ? evt.changedTouches[0] + : evt; + if (!timeBaseline) { + timeBaseline = Date.now(); + } + positions.push({ + x: clientX, + y: clientY, + id: mirror.getId(target as INode), + timeOffset: Date.now() - timeBaseline, + }); + } + + // separate call for non-drag events, in case DragEvent is not defined + const updatePosition = throttle( (evt) => { - const target = getEventTarget(evt); - const { clientX, clientY } = isTouchEvent(evt) - ? evt.changedTouches[0] - : evt; - if (!timeBaseline) { - timeBaseline = Date.now(); - } - positions.push({ - x: clientX, - y: clientY, - id: mirror.getId(target as INode), - timeOffset: Date.now() - timeBaseline, - }); + handleUpdatePositionEvent(evt); + wrappedCb( + evt instanceof MouseEvent + ? IncrementalSource.MouseMove + : IncrementalSource.TouchMove, + ); + }, + threshold, + { + trailing: false, + }, + ); + // call for drag events, when DragEvent is defined + const updateDragPosition = throttle( + (evt) => { + handleUpdatePositionEvent(evt); wrappedCb( evt instanceof DragEvent ? IncrementalSource.Drag @@ -222,10 +244,13 @@ function initMoveObserver( trailing: false, }, ); + // it is possible DragEvent is undefined even on devices + // that support event 'drag' + const dragEventDefined = typeof DragEvent !== 'undefined'; const handlers = [ on('mousemove', updatePosition, doc), on('touchmove', updatePosition, doc), - on('drag', updatePosition, doc), + on('drag', dragEventDefined ? updateDragPosition : updatePosition, doc), ]; return () => { handlers.forEach((h) => h());