RUMS-6014: Fix missing text boxes with mask none#1305
Merged
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes an Android Session Replay mapping issue where DdPrivacyView (used by SessionReplayView.MaskNone/MaskAll/Hide) prevented the Session Replay framework from traversing and recording its child views, causing wrapped content to be missing from recordings.
Changes:
- Updated
SvgViewMapperto implementTraverseAllChildrenMapperand tightened its generic bound toT : ViewGroupso the SR framework traversesDdPrivacyViewchildren. - Adjusted
SvgViewMapper.map()to return a containerShapeWireframewhenDdPrivacyViewis used as a privacy wrapper (no SVGhashattribute), instead of returning an empty list. - Ensured SVG
ImageWireframeIDs don’t collide with independently-traversed child views by usingresolveChildUniqueIdentifier(view, "svg"), and added unit tests covering these scenarios.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| packages/react-native-session-replay/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/mappers/SvgViewMapper.kt | Enables child traversal for DdPrivacyView, maps privacy-wrapper containers as shapes, and avoids SVG wireframe ID collisions. |
| packages/react-native-session-replay/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/mappers/SvgViewMapperTest.kt | Adds test coverage for privacy-wrapper behavior, SVG behavior, and the ID uniqueness invariant. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
sbarrio
approved these changes
Jun 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Problem
On Android, any content placed inside SessionReplayView.MaskNone, SessionReplayView.MaskAll, or SessionReplayView.Hide was completely absent from Session Replay recordings. iOS captured the same content correctly under identical configuration.
Root cause
DdPrivacyView — the Android native backing view for all SessionReplayView variants — was registered with SvgViewMapper. That mapper was originally written to handle a separate use case: rendering inline SVGs via the Datadog babel plugin, where DdPrivacyView carries SVG binary data in its attributes map.
When DdPrivacyView is used as a privacy wrapper (no SVG attributes), two things went wrong:
SvgViewMapper.map() returned an empty wireframe list immediately, so the wrapper container itself never appeared in the recording.
SvgViewMapper did not implement TraverseAllChildrenMapper, so the Session Replay framework never descended into the wrapper's children regardless of what the mapper returned.
Fix
SvgViewMapper now implements TraverseAllChildrenMapper, which tells the SR framework to traverse the children of any DdPrivacyView it handles.
When DdPrivacyView has no hash attribute (privacy-wrapper mode), map() now returns a ShapeWireframe for the container instead of an empty list.
The type bound was tightened from T : View to T : ViewGroup, required by the TraverseAllChildrenMapper interface.
The SVG path also has a latent fix: ImageWireframe now uses viewIdentifierResolver.resolveChildUniqueIdentifier(view, "svg") for its ID instead of resolveViewId(subView). With TraverseAllChildrenMapper in place the framework independently traverses the SVG child view, which would produce a second wireframe with the same resolveViewId(subView) ID — overwriting the image in the recording.
Testing
Added SvgViewMapperTest covering the privacy-wrapper path, the SVG path, and the ID uniqueness invariant.
Manually verified on Android: MaskNone content is visible and unmasked, MaskAll content is visible and masked (text rendered as x's), Hide wrapper is absent from the recording.
Motivation
What inspired you to submit this pull request?
Additional Notes
Anything else we should know when reviewing?
Review checklist (to be filled by reviewers)