Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions packages/rrdom/src/document.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { NodeType as RRNodeType } from '@rrweb/types';
import { parseCSSText, camelize, toCSSText } from './style';
import { escapeHtmlText, escapeHtmlAttr } from './helpers';

export interface IRRNode {
parentElement: IRRNode | null;
parentNode: IRRNode | null;
Expand Down Expand Up @@ -373,6 +375,26 @@ export class BaseRRDocument extends BaseRRNode implements IRRDocument {
return CDATASection;
}

// not something that is on the browser document object, but a convenient way to export
public get outerHTML() {
return this.childNodes
.map((cn) => {
if (cn instanceof BaseRRDocumentType) {
return `<!DOCTYPE ${cn.name}${
cn.publicId ? ` PUBLIC "${cn.publicId}"` : ''
}${cn.systemId ? ` "${cn.systemId}"` : ''}>\n`;
} else if (cn instanceof BaseRRElement) {
return cn.outerHTML;
} else if (cn.textContent !== null) {
// presumably newlines or other spacing characters
return escapeHtmlText(cn.textContent);
} else {
return cn.toString(); // for debugging
}
})
.join('');
}

toString() {
return 'RRDocument';
}
Expand Down Expand Up @@ -518,6 +540,62 @@ export class BaseRRElement extends BaseRRNode implements IRRElement {
return true;
}

public get outerHTML(): string {
const voidElements = new Set([
'area',
'base',
'br',
'col',
'embed',
'hr',
'img',
'input',
'link',
'meta',
'param',
'source',
'track',
'wbr',
]);

const tagLower = this.tagName.toLowerCase();
const tagAttrs = [tagLower];
for (const attribute in this.attributes) {
if (attribute === '"' && this.attributes[attribute] === '') {
// Badly authored html, e.g. `<a href="#""><span>`
continue;
}
// attribute names are case insensitive in HTML5; chrome normalizes them to lowercase
tagAttrs.push(
`${attribute.toLowerCase()}="${escapeHtmlAttr(
this.attributes[attribute],
)}"`,
);
}
if (voidElements.has(tagLower)) {
return `<${tagAttrs.join(' ')}>`;
}

const children = this.childNodes
.map((cn) => {
if (cn instanceof BaseRRElement) {
return cn.outerHTML;
} else if (cn.textContent !== null) {
if (tagLower === 'style') {
// don't escape to &gt; in `select > option.selected`
return cn.textContent;
} else {
return escapeHtmlText(cn.textContent);
}
} else {
return cn.toString(); // for debugging
}
})
.join('');

return `<${tagAttrs.join(' ')}>${children}</${tagLower}>`;
}

toString() {
let attributeString = '';
for (const attribute in this.attributes) {
Expand Down Expand Up @@ -591,6 +669,10 @@ export class BaseRRText extends BaseRRNode implements IRRText {
this.data = textContent;
}

public get outerHTML(): string {
return escapeHtmlText(this.textContent || '');
}

toString() {
return `RRText text=${JSON.stringify(this.data)}`;
}
Expand All @@ -615,6 +697,10 @@ export class BaseRRComment extends BaseRRNode implements IRRComment {
this.data = textContent;
}

public get outerHTML() {
return `<!--${this.textContent || ''}-->`;
}

toString() {
return `RRComment text=${JSON.stringify(this.data)}`;
}
Expand Down
16 changes: 16 additions & 0 deletions packages/rrdom/src/escape.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// for serializing a virtual DOM to html

function escapeHtmlText(text: string): string {
return text
.replace(/&/g, '&amp;')
.replace(/ /g, '&nbsp;') // unicode version is correct, but output the html escape syntax for consistency with what chrome outputs
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
}

function escapeHtmlAttr(text: string): string {
return text
.replace(/&/g, '&amp;')
.replace(/ /g, '&nbsp;') // unicode version is correct, but output the html escape syntax for consistency with what chrome outputs
.replace(/"/g, '&quot;');
}
7 changes: 3 additions & 4 deletions packages/rrweb-cutter/src/cut-session.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { NodeType } from 'rrweb-snapshot';
import {
import { NodeType, MediaInteractions } from '@rrweb/types';
import type {
adoptedStyleSheetData,
eventWithTime,
MediaInteractions,
styleDeclarationData,
styleSheetRuleData,
} from '@rrweb/types';
Expand All @@ -15,7 +14,7 @@ import {
} from 'rrdom';
import type { IRRNode, RRElement } from 'rrdom';
import { IncrementalSource, EventType, SyncReplayer } from 'rrweb';
import { playerConfig } from 'rrweb/typings/types';
import type { playerConfig } from '@rrweb/replay';
import cloneDeep from 'lodash.clonedeep';
import snapshot from './snapshot';
export type CutterConfig = {
Expand Down
2 changes: 1 addition & 1 deletion packages/rrweb-cutter/src/prune-branches.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { serializedNodeWithId } from 'rrweb-snapshot';
import type {
serializedNodeWithId,
addedNodeMutation,
eventWithTime,
mousePosition,
Expand Down
10 changes: 8 additions & 2 deletions packages/rrweb-cutter/src/snapshot.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import type { serializedNodeWithId, attributes } from 'rrweb-snapshot';
import { elementNode, NodeType, serializedNode } from 'rrweb-snapshot';
import type {
elementNode,
serializedNode,
serializedNodeWithId,
attributes,
} from '@rrweb/types';
import { NodeType } from '@rrweb/types';

import type {
IRRComment,
IRRDocument,
Expand Down