Skip to content

Commit

Permalink
Revert "Fix serialization and mutation of <textarea> elements (rrweb-…
Browse files Browse the repository at this point in the history
…io#1351)"

This reverts commit a2be77b.
  • Loading branch information
billyvg committed Apr 26, 2024
1 parent fdf0ade commit e26708f
Show file tree
Hide file tree
Showing 16 changed files with 55 additions and 689 deletions.
6 changes: 0 additions & 6 deletions .changeset/rare-adults-sneeze.md

This file was deleted.

3 changes: 1 addition & 2 deletions packages/rrweb-snapshot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
"scripts": {
"prepare": "npm run prepack",
"prepack": "npm run bundle && npm run typings",
"retest": "jest",
"test": "yarn bundle && yarn retest",
"test": "jest",
"test:watch": "jest --watch",
"test:update": "jest --updateSnapshot",
"bundle": "rollup --config",
Expand Down
10 changes: 8 additions & 2 deletions packages/rrweb-snapshot/src/rebuild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
tagMap,
elementNode,
BuildCache,
attributes,
legacyAttributes,
} from './types';
import { isElement, Mirror, isNodeMetaEqual } from './utils';
Expand Down Expand Up @@ -227,9 +228,14 @@ function buildNode(
value = adaptCssForReplay(value, cache);
}
if ((isTextarea || isRemoteOrDynamicCss) && typeof value === 'string') {
node.appendChild(doc.createTextNode(value));
const child = doc.createTextNode(value);
// https://github.com/rrweb-io/rrweb/issues/112
n.childNodes = []; // value overrides childNodes
for (const c of Array.from(node.childNodes)) {
if (c.nodeType === node.TEXT_NODE) {
node.removeChild(c);
}
}
node.appendChild(child);
continue;
}

Expand Down
21 changes: 6 additions & 15 deletions packages/rrweb-snapshot/src/snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
MaskInputFn,
KeepIframeSrcFn,
ICanvas,
elementNode,
serializedElementNodeWithId,
type mediaAttributes,
} from './types';
Expand Down Expand Up @@ -675,9 +674,10 @@ function serializeElementNode(
attributes.type !== 'button' &&
value
) {
const type = getInputType(n);
attributes.value = maskInputValue({
element: n,
type: getInputType(n),
type,
tagName,
value,
maskInputOptions,
Expand Down Expand Up @@ -1097,19 +1097,10 @@ export function serializeNodeWithId(
stylesheetLoadTimeout,
keepIframeSrcFn,
};

if (
serializedNode.type === NodeType.Element &&
serializedNode.tagName === 'textarea' &&
(serializedNode as elementNode).attributes.value !== undefined
) {
// value parameter in DOM reflects the correct value, so ignore childNode
} else {
for (const childN of Array.from(n.childNodes)) {
const serializedChildNode = serializeNodeWithId(childN, bypassOptions);
if (serializedChildNode) {
serializedNode.childNodes.push(serializedChildNode);
}
for (const childN of Array.from(n.childNodes)) {
const serializedChildNode = serializeNodeWithId(childN, bypassOptions);
if (serializedChildNode) {
serializedNode.childNodes.push(serializedChildNode);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,6 @@ exports[`integration tests [html file]: form-fields.html 1`] = `
</label>
<label for=\\"textarea\\">
<textarea name=\\"\\" id=\\"\\" cols=\\"30\\" rows=\\"10\\">1234</textarea>
<textarea name=\\"\\" id=\\"\\" cols=\\"30\\" rows=\\"10\\">1234</textarea>
</label>
<label for=\\"select\\">
<select name=\\"\\" id=\\"\\" value=\\"2\\">
Expand Down
4 changes: 1 addition & 3 deletions packages/rrweb-snapshot/test/html/form-fields.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
</label>
<label for="textarea">
<textarea name="" id="" cols="30" rows="10"></textarea>
<textarea name="" id="" cols="30" rows="10">-1</textarea>
</label>
<label for="select">
<select name="" id="">
Expand All @@ -37,8 +36,7 @@
document.querySelector('input[type="text"]').value = '1';
document.querySelector('input[type="radio"]').checked = true;
document.querySelector('input[type="checkbox"]').checked = true;
document.querySelector('textarea:empty').value = '1234';
document.querySelector('textarea:not(:empty)').value = '1234';
document.querySelector('textarea').value = '1234';
document.querySelector('select').value = '2';
</script>
</html>
41 changes: 1 addition & 40 deletions packages/rrweb-snapshot/test/snapshot.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
serializeNodeWithId,
_isBlockedElement,
} from '../src/snapshot';
import { serializedNodeWithId, elementNode } from '../src/types';
import { serializedNodeWithId } from '../src/types';
import { Mirror } from '../src/utils';

describe('absolute url to stylesheet', () => {
Expand Down Expand Up @@ -218,42 +218,3 @@ describe('scrollTop/scrollLeft', () => {
});
});
});

describe('form', () => {
const serializeNode = (node: Node): serializedNodeWithId | null => {
return serializeNodeWithId(node, {
doc: document,
mirror: new Mirror(),
blockClass: 'blockblock',
blockSelector: null,
maskTextClass: 'maskmask',
maskTextSelector: null,
skipChild: false,
inlineStylesheet: true,
maskTextFn: undefined,
maskInputFn: undefined,
slimDOMOptions: {},
newlyAddedElement: false,
});
};

const render = (html: string): HTMLTextAreaElement => {
document.write(html);
return document.querySelector('textarea')!;
};

it('should record textarea values once', () => {
const el = render(`<textarea>Lorem ipsum</textarea>`);
const sel = serializeNode(el) as elementNode;

// we serialize according to where the DOM stores the value, not how
// the HTML stores it (this is so that maskInputValue can work over
// inputs/textareas/selects in a uniform way)
expect(sel).toMatchObject({
attributes: {
value: 'Lorem ipsum',
},
});
expect(sel?.childNodes).toEqual([]); // shouldn't be stored in childNodes while in transit
});
});
1 change: 0 additions & 1 deletion packages/rrweb/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
"test": "yarn test:headless",
"test:watch": "yarn test:headless -- --watch",
"test:update": "yarn test:headless -- --updateSnapshot",
"retest:update": "PUPPETEER_HEADLESS=true yarn retest -- --updateSnapshot",
"repl": "yarn bundle:browser && node scripts/repl.js",
"live-stream": "yarn bundle:browser && node scripts/stream.js",
"dev": "yarn bundle:browser --watch",
Expand Down
48 changes: 5 additions & 43 deletions packages/rrweb/src/record/mutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,11 +285,7 @@ export default class MutationBuffer {
return nextId;
};
const pushAdd = (n: Node) => {
if (
!n.parentNode ||
!inDom(n) ||
(n.parentNode as Element).tagName === 'TEXTAREA'
) {
if (!n.parentNode || !inDom(n)) {
return;
}
const parentId = isShadowRoot(n.parentNode)
Expand Down Expand Up @@ -439,20 +435,10 @@ export default class MutationBuffer {

const payload = {
texts: this.texts
.map((text) => {
const n = text.node;
if (
n.parentNode &&
(n.parentNode as Element).tagName === 'TEXTAREA'
) {
// the node is being ignored as it isn't in the mirror, so shift mutation to attributes on parent textarea
this.genTextAreaValueMutation(n.parentNode as HTMLTextAreaElement);
}
return {
id: this.mirror.getId(n),
value: text.value,
};
})
.map((text) => ({
id: this.mirror.getId(text.node),
value: text.value,
}))
// no need to include them on added elements, as they have just been serialized with up to date attribubtes
.filter((text) => !addedIds.has(text.id))
// text mutation's id was not in the mirror map means the target node has been removed
Expand Down Expand Up @@ -511,24 +497,6 @@ export default class MutationBuffer {
this.mutationCb(payload);
};

private genTextAreaValueMutation = (textarea: HTMLTextAreaElement) => {
let item = this.attributeMap.get(textarea);
if (!item) {
item = {
node: textarea,
attributes: {},
styleDiff: {},
_unchangedStyles: {},
};
this.attributes.push(item);
this.attributeMap.set(textarea, item);
}
item.attributes.value = Array.from(
textarea.childNodes,
(cn) => cn.textContent || '',
).join('');
};

private processMutation = (m: mutationRecord) => {
if (isIgnored(m.target, this.mirror)) {
return;
Expand Down Expand Up @@ -674,12 +642,6 @@ export default class MutationBuffer {
if (isBlocked(m.target, this.blockClass, this.blockSelector, true))
return;

if ((m.target as Element).tagName === 'TEXTAREA') {
// children would be ignored in genAdds as they aren't in the mirror
this.genTextAreaValueMutation(m.target as HTMLTextAreaElement);
return; // any removedNodes won't have been in mirror either
}

m.addedNodes.forEach((n) => this.genAdds(n, m.target));
m.removedNodes.forEach((n) => {
const nodeId = this.mirror.getId(n);
Expand Down
24 changes: 4 additions & 20 deletions packages/rrweb/src/replay/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1577,8 +1577,6 @@ export class Replayer {
const childNodeArray = Array.isArray(parent.childNodes)
? parent.childNodes
: Array.from(parent.childNodes);
// This should be redundant now as we are either recording the value or the childNode, and not both
// keeping around for backwards compatibility with old bad double data, see

// https://github.com/rrweb-io/rrweb/issues/745
// parent is textarea, will only keep one child node as the value
Expand Down Expand Up @@ -1776,24 +1774,10 @@ export class Replayer {
// for safe
}
}
if (attributeName === 'value' && target.nodeName === 'TEXTAREA') {
// this may or may not have an effect on the value property (which is what is displayed)
// depending on whether the textarea has been modified by the user yet
// TODO: replaceChildNodes is not available in RRDom
const textarea = target as TNode;
textarea.childNodes.forEach((c) =>
textarea.removeChild(c as TNode),
);
const tn = target.ownerDocument?.createTextNode(value);
if (tn) {
textarea.appendChild(tn as TNode);
}
} else {
(target as Element | RRElement).setAttribute(
attributeName,
value,
);
}
(target as Element | RRElement).setAttribute(
attributeName,
value,
);
} catch (error) {
this.warn(
'An error occurred may due to the checkout feature.',
Expand Down
Loading

0 comments on commit e26708f

Please sign in to comment.