Skip to content

Commit e26708f

Browse files
committed
Revert "Fix serialization and mutation of <textarea> elements (rrweb-io#1351)"
This reverts commit a2be77b.
1 parent fdf0ade commit e26708f

File tree

16 files changed

+55
-689
lines changed

16 files changed

+55
-689
lines changed

.changeset/rare-adults-sneeze.md

Lines changed: 0 additions & 6 deletions
This file was deleted.

packages/rrweb-snapshot/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
"scripts": {
66
"prepare": "npm run prepack",
77
"prepack": "npm run bundle && npm run typings",
8-
"retest": "jest",
9-
"test": "yarn bundle && yarn retest",
8+
"test": "jest",
109
"test:watch": "jest --watch",
1110
"test:update": "jest --updateSnapshot",
1211
"bundle": "rollup --config",

packages/rrweb-snapshot/src/rebuild.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
tagMap,
66
elementNode,
77
BuildCache,
8+
attributes,
89
legacyAttributes,
910
} from './types';
1011
import { isElement, Mirror, isNodeMetaEqual } from './utils';
@@ -227,9 +228,14 @@ function buildNode(
227228
value = adaptCssForReplay(value, cache);
228229
}
229230
if ((isTextarea || isRemoteOrDynamicCss) && typeof value === 'string') {
230-
node.appendChild(doc.createTextNode(value));
231+
const child = doc.createTextNode(value);
231232
// https://github.com/rrweb-io/rrweb/issues/112
232-
n.childNodes = []; // value overrides childNodes
233+
for (const c of Array.from(node.childNodes)) {
234+
if (c.nodeType === node.TEXT_NODE) {
235+
node.removeChild(c);
236+
}
237+
}
238+
node.appendChild(child);
233239
continue;
234240
}
235241

packages/rrweb-snapshot/src/snapshot.ts

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
MaskInputFn,
1111
KeepIframeSrcFn,
1212
ICanvas,
13-
elementNode,
1413
serializedElementNodeWithId,
1514
type mediaAttributes,
1615
} from './types';
@@ -675,9 +674,10 @@ function serializeElementNode(
675674
attributes.type !== 'button' &&
676675
value
677676
) {
677+
const type = getInputType(n);
678678
attributes.value = maskInputValue({
679679
element: n,
680-
type: getInputType(n),
680+
type,
681681
tagName,
682682
value,
683683
maskInputOptions,
@@ -1097,19 +1097,10 @@ export function serializeNodeWithId(
10971097
stylesheetLoadTimeout,
10981098
keepIframeSrcFn,
10991099
};
1100-
1101-
if (
1102-
serializedNode.type === NodeType.Element &&
1103-
serializedNode.tagName === 'textarea' &&
1104-
(serializedNode as elementNode).attributes.value !== undefined
1105-
) {
1106-
// value parameter in DOM reflects the correct value, so ignore childNode
1107-
} else {
1108-
for (const childN of Array.from(n.childNodes)) {
1109-
const serializedChildNode = serializeNodeWithId(childN, bypassOptions);
1110-
if (serializedChildNode) {
1111-
serializedNode.childNodes.push(serializedChildNode);
1112-
}
1100+
for (const childN of Array.from(n.childNodes)) {
1101+
const serializedChildNode = serializeNodeWithId(childN, bypassOptions);
1102+
if (serializedChildNode) {
1103+
serializedNode.childNodes.push(serializedChildNode);
11131104
}
11141105
}
11151106

packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,6 @@ exports[`integration tests [html file]: form-fields.html 1`] = `
247247
</label>
248248
<label for=\\"textarea\\">
249249
<textarea name=\\"\\" id=\\"\\" cols=\\"30\\" rows=\\"10\\">1234</textarea>
250-
<textarea name=\\"\\" id=\\"\\" cols=\\"30\\" rows=\\"10\\">1234</textarea>
251250
</label>
252251
<label for=\\"select\\">
253252
<select name=\\"\\" id=\\"\\" value=\\"2\\">

packages/rrweb-snapshot/test/html/form-fields.html

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
</label>
2121
<label for="textarea">
2222
<textarea name="" id="" cols="30" rows="10"></textarea>
23-
<textarea name="" id="" cols="30" rows="10">-1</textarea>
2423
</label>
2524
<label for="select">
2625
<select name="" id="">
@@ -37,8 +36,7 @@
3736
document.querySelector('input[type="text"]').value = '1';
3837
document.querySelector('input[type="radio"]').checked = true;
3938
document.querySelector('input[type="checkbox"]').checked = true;
40-
document.querySelector('textarea:empty').value = '1234';
41-
document.querySelector('textarea:not(:empty)').value = '1234';
39+
document.querySelector('textarea').value = '1234';
4240
document.querySelector('select').value = '2';
4341
</script>
4442
</html>

packages/rrweb-snapshot/test/snapshot.test.ts

Lines changed: 1 addition & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
serializeNodeWithId,
88
_isBlockedElement,
99
} from '../src/snapshot';
10-
import { serializedNodeWithId, elementNode } from '../src/types';
10+
import { serializedNodeWithId } from '../src/types';
1111
import { Mirror } from '../src/utils';
1212

1313
describe('absolute url to stylesheet', () => {
@@ -218,42 +218,3 @@ describe('scrollTop/scrollLeft', () => {
218218
});
219219
});
220220
});
221-
222-
describe('form', () => {
223-
const serializeNode = (node: Node): serializedNodeWithId | null => {
224-
return serializeNodeWithId(node, {
225-
doc: document,
226-
mirror: new Mirror(),
227-
blockClass: 'blockblock',
228-
blockSelector: null,
229-
maskTextClass: 'maskmask',
230-
maskTextSelector: null,
231-
skipChild: false,
232-
inlineStylesheet: true,
233-
maskTextFn: undefined,
234-
maskInputFn: undefined,
235-
slimDOMOptions: {},
236-
newlyAddedElement: false,
237-
});
238-
};
239-
240-
const render = (html: string): HTMLTextAreaElement => {
241-
document.write(html);
242-
return document.querySelector('textarea')!;
243-
};
244-
245-
it('should record textarea values once', () => {
246-
const el = render(`<textarea>Lorem ipsum</textarea>`);
247-
const sel = serializeNode(el) as elementNode;
248-
249-
// we serialize according to where the DOM stores the value, not how
250-
// the HTML stores it (this is so that maskInputValue can work over
251-
// inputs/textareas/selects in a uniform way)
252-
expect(sel).toMatchObject({
253-
attributes: {
254-
value: 'Lorem ipsum',
255-
},
256-
});
257-
expect(sel?.childNodes).toEqual([]); // shouldn't be stored in childNodes while in transit
258-
});
259-
});

packages/rrweb/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
"test": "yarn test:headless",
1313
"test:watch": "yarn test:headless -- --watch",
1414
"test:update": "yarn test:headless -- --updateSnapshot",
15-
"retest:update": "PUPPETEER_HEADLESS=true yarn retest -- --updateSnapshot",
1615
"repl": "yarn bundle:browser && node scripts/repl.js",
1716
"live-stream": "yarn bundle:browser && node scripts/stream.js",
1817
"dev": "yarn bundle:browser --watch",

packages/rrweb/src/record/mutation.ts

Lines changed: 5 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -285,11 +285,7 @@ export default class MutationBuffer {
285285
return nextId;
286286
};
287287
const pushAdd = (n: Node) => {
288-
if (
289-
!n.parentNode ||
290-
!inDom(n) ||
291-
(n.parentNode as Element).tagName === 'TEXTAREA'
292-
) {
288+
if (!n.parentNode || !inDom(n)) {
293289
return;
294290
}
295291
const parentId = isShadowRoot(n.parentNode)
@@ -439,20 +435,10 @@ export default class MutationBuffer {
439435

440436
const payload = {
441437
texts: this.texts
442-
.map((text) => {
443-
const n = text.node;
444-
if (
445-
n.parentNode &&
446-
(n.parentNode as Element).tagName === 'TEXTAREA'
447-
) {
448-
// the node is being ignored as it isn't in the mirror, so shift mutation to attributes on parent textarea
449-
this.genTextAreaValueMutation(n.parentNode as HTMLTextAreaElement);
450-
}
451-
return {
452-
id: this.mirror.getId(n),
453-
value: text.value,
454-
};
455-
})
438+
.map((text) => ({
439+
id: this.mirror.getId(text.node),
440+
value: text.value,
441+
}))
456442
// no need to include them on added elements, as they have just been serialized with up to date attribubtes
457443
.filter((text) => !addedIds.has(text.id))
458444
// text mutation's id was not in the mirror map means the target node has been removed
@@ -511,24 +497,6 @@ export default class MutationBuffer {
511497
this.mutationCb(payload);
512498
};
513499

514-
private genTextAreaValueMutation = (textarea: HTMLTextAreaElement) => {
515-
let item = this.attributeMap.get(textarea);
516-
if (!item) {
517-
item = {
518-
node: textarea,
519-
attributes: {},
520-
styleDiff: {},
521-
_unchangedStyles: {},
522-
};
523-
this.attributes.push(item);
524-
this.attributeMap.set(textarea, item);
525-
}
526-
item.attributes.value = Array.from(
527-
textarea.childNodes,
528-
(cn) => cn.textContent || '',
529-
).join('');
530-
};
531-
532500
private processMutation = (m: mutationRecord) => {
533501
if (isIgnored(m.target, this.mirror)) {
534502
return;
@@ -674,12 +642,6 @@ export default class MutationBuffer {
674642
if (isBlocked(m.target, this.blockClass, this.blockSelector, true))
675643
return;
676644

677-
if ((m.target as Element).tagName === 'TEXTAREA') {
678-
// children would be ignored in genAdds as they aren't in the mirror
679-
this.genTextAreaValueMutation(m.target as HTMLTextAreaElement);
680-
return; // any removedNodes won't have been in mirror either
681-
}
682-
683645
m.addedNodes.forEach((n) => this.genAdds(n, m.target));
684646
m.removedNodes.forEach((n) => {
685647
const nodeId = this.mirror.getId(n);

packages/rrweb/src/replay/index.ts

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1577,8 +1577,6 @@ export class Replayer {
15771577
const childNodeArray = Array.isArray(parent.childNodes)
15781578
? parent.childNodes
15791579
: Array.from(parent.childNodes);
1580-
// This should be redundant now as we are either recording the value or the childNode, and not both
1581-
// keeping around for backwards compatibility with old bad double data, see
15821580

15831581
// https://github.com/rrweb-io/rrweb/issues/745
15841582
// parent is textarea, will only keep one child node as the value
@@ -1776,24 +1774,10 @@ export class Replayer {
17761774
// for safe
17771775
}
17781776
}
1779-
if (attributeName === 'value' && target.nodeName === 'TEXTAREA') {
1780-
// this may or may not have an effect on the value property (which is what is displayed)
1781-
// depending on whether the textarea has been modified by the user yet
1782-
// TODO: replaceChildNodes is not available in RRDom
1783-
const textarea = target as TNode;
1784-
textarea.childNodes.forEach((c) =>
1785-
textarea.removeChild(c as TNode),
1786-
);
1787-
const tn = target.ownerDocument?.createTextNode(value);
1788-
if (tn) {
1789-
textarea.appendChild(tn as TNode);
1790-
}
1791-
} else {
1792-
(target as Element | RRElement).setAttribute(
1793-
attributeName,
1794-
value,
1795-
);
1796-
}
1777+
(target as Element | RRElement).setAttribute(
1778+
attributeName,
1779+
value,
1780+
);
17971781
} catch (error) {
17981782
this.warn(
17991783
'An error occurred may due to the checkout feature.',

0 commit comments

Comments
 (0)