Skip to content

Conversation

@wfk007
Copy link
Contributor

@wfk007 wfk007 commented Nov 21, 2025

@changeset-bot
Copy link

changeset-bot bot commented Nov 21, 2025

🦋 Changeset detected

Latest commit: 4ec8273

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 19 packages
Name Type
rrweb Patch
@rrweb/utils Patch
rrweb-snapshot Patch
rrdom Patch
rrdom-nodejs Patch
rrweb-player Patch
@rrweb/all Patch
@rrweb/replay Patch
@rrweb/record Patch
@rrweb/types Patch
@rrweb/packer Patch
@rrweb/web-extension Patch
rrvideo Patch
@rrweb/rrweb-plugin-console-record Patch
@rrweb/rrweb-plugin-console-replay Patch
@rrweb/rrweb-plugin-sequential-id-record Patch
@rrweb/rrweb-plugin-sequential-id-replay Patch
@rrweb/rrweb-plugin-canvas-webrtc-record Patch
@rrweb/rrweb-plugin-canvas-webrtc-replay Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@wfk007
Copy link
Contributor Author

wfk007 commented Nov 21, 2025

Wujie framework proxiesiframe.contentDocument.contains to the contains which this bind to shadow-root(document-fragment)

image

This explained the log output below:

const iframe = window.document.querySelector('IFRAME');
const head = iframe.contentDocument.querySelector('head');

// updated code simulate output
iframe.contentDocument.contains(head); // true

// Previous code simulate output
window.Node.prototype.contains.call(iframe.contentDocument, head); // false
iframe.contentWindow.Node.prototype.contains.bind(iframe.contentDocument)(head); // false
image

@wfk007 wfk007 requested a review from Juice10 November 24, 2025 06:11
@JiuRanYa
Copy link

Is there anyone follow up this pr?

if (!doc) return false;
return dom.contains(doc, n) || shadowHostInDom(n);
// fix: https://github.com/rrweb-io/rrweb/issues/1682
return doc.contains(n) || shadowHostInDom(n);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dom.contains was trying to get the original unproxied .contains.
Changing it to use the regular doc.contains could fix it for wujie but would break it for other frameworks that monkeypatch .contains and make it do non-standard behavior.
To fix this correctly check out how dom.contains is implemented, it sounds like wujie is getting around our proxy bypass

Copy link
Contributor Author

@wfk007 wfk007 Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy New Year Justin! Thanks for your great suggestion, in fact, wujie modified the this reference in the iframe, binding it to the this reference in shadowRoot. To be honest, I didn't think it was a very elegant fix from the start and want to discuss with you.

}

export function inDom(n: Node): boolean {
const doc = n.ownerDocument;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It turns out the real problem is here, wujie monkey patches n.ownerDocument, we should make sure access to this method goes through getUntaintedAccessor like we do with many other accessors, then this should record ok:

export function childNodes(n: Node): NodeListOf<Node> {
return getUntaintedAccessor('Node', n, 'childNodes');
}
export function parentNode(n: Node): ParentNode | null {
return getUntaintedAccessor('Node', n, 'parentNode');
}
export function parentElement(n: Node): HTMLElement | null {
return getUntaintedAccessor('Node', n, 'parentElement');
}
export function textContent(n: Node): string | null {
return getUntaintedAccessor('Node', n, 'textContent');
}

@Juice10
Copy link
Member

Juice10 commented Jan 2, 2026

Looks like this is the correct fix: wfk007/rrweb-wujie#1
wujie monkeypatches .ownerDocument and we where still accessing using the un-monkey accessor method which lead to the recording breaking

@wfk007
Copy link
Contributor Author

wfk007 commented Jan 2, 2026

Looks like this is the correct fix: wfk007/rrweb-wujie#1 wujie monkeypatches .ownerDocument and we where still accessing using the un-monkey accessor method which lead to the recording breaking

This pr is working for me! 👍

@wfk007
Copy link
Contributor Author

wfk007 commented Jan 4, 2026

Looks like this is the correct fix: wfk007/rrweb-wujie#1 wujie monkeypatches .ownerDocument and we where still accessing using the un-monkey accessor method which lead to the recording breaking

@Juice10 This is how I understand:

  1. wujie:
    monkeypatches ownerDocument, making shadowRootNode.ownerDocument to iframe doc(created by wujie).
    https://github.com/Tencent/wujie/blob/ddfc5ccd730a2a87dfbfb455dbcac4618f5cee4b/packages/wujie-core/src/iframe.ts#L748

  2. rrweb record:
    In inDom function, ownerDocument(tainted by wijie) with contains(unTainted with rrweb) give the false return, breaking the node serialized.

so, we need to keep ownerDocument and contains all unTainted.

@wfk007 wfk007 force-pushed the wfk/fix-wujie-shadow-root branch from 3938584 to 67d8900 Compare January 4, 2026 07:28
@Juice10 Juice10 merged commit 6388fb5 into rrweb-io:master Jan 4, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants