Fix serialization and mutation of <textarea> elements (#1351)

* Fix serialization and mutation of <textarea> elements taking account the duality that the value can be set in either the child node, or in the value _parameter_ (not attribute)

* Backwards compatibility: Bug fix and regression test for #112
 - this is to fix up 'historical' recordings, as duplicate textarea content should no longer be being created at record time
 - new test shows what the snapshot generated by previous versions of rrweb used to look like, hence 'bad'
 - original 0efe23f04a fix either didn't work or no longer works due to childNodes being appended subsequent to this part of the code
 - during review, we also verified that the `_cssText` case should still be handled okay, as there's currently no scenario where csstext is present with css child nodes of a <style>

* Masking: Fix that textarea values were being missed by the masking system if the value was recorded as a child node
 - I didn't notice that form.html was used in other tests, so lucky that I noticed that those tests also should have the 'pre value' masked out

* Simplify by always storing the textarea value in the `.value` attribute (from it's DOM property) and not as a childNode. It should still be rebuilt as a childNode rather than a property
---------

Authored-by: eoghanmurray <eoghan@getthere.ie>
This commit is contained in:
Eoghan Murray
2026-04-01 12:00:00 +08:00
committed by GitHub
parent d1d0c7f366
commit a8d93986f4
16 changed files with 686 additions and 55 deletions

View File

@@ -5,7 +5,6 @@ import {
tagMap,
elementNode,
BuildCache,
attributes,
legacyAttributes,
} from './types';
import { isElement, Mirror, isNodeMetaEqual } from './utils';
@@ -200,14 +199,9 @@ function buildNode(
value = addHoverClass(value, cache);
}
if ((isTextarea || isRemoteOrDynamicCss) && typeof value === 'string') {
const child = doc.createTextNode(value);
node.appendChild(doc.createTextNode(value));
// https://github.com/rrweb-io/rrweb/issues/112
for (const c of Array.from(node.childNodes)) {
if (c.nodeType === node.TEXT_NODE) {
node.removeChild(c);
}
}
node.appendChild(child);
n.childNodes = []; // value overrides childNodes
continue;
}