Skip to content

Conversation

@sethfowler-datadog
Copy link
Contributor

Motivation

The current DOM serialization algorithm encodes two specific DOM attributes as boolean values:

  • The checked attribute found in <input type="checkbox"> and <input type="radio">.
  • The selected attribute found in <option selected>.

I'd like to avoid this special case handling in the new DOM serialization algorithm. Instead, I'd like to encode these attributes as strings, just like all other attributes. Since these are boolean attributes, the standard string encoding for the value is:

  • { selected: '' } (the empty string) for true. (e.g. <option selected>)
  • {} (no value recorded) for false. (e.g. <option>)

This is exactly the transformation that the session replay player already applies to boolean-valued attributes, so the result will be the same at playback time. Indeed, handling things this way is less work on both ends! The existing player will just work with this approach.

I'd like to take this opportunity to very slightly change the behavior of <option selected>, resolving an issue that was discovered when building the test suite added in #3994. Currently, there's a very particular circumstance where we do the wrong thing. The problematic situation is when an <option> element is masked, and:

  • It has a selected DOM attribute.
  • The value of of its selected property is false. (Because the user has selected another <option> since page load, perhaps.)

In this situation, we will add the selected DOM attribute to the element's list of attributes. Then, we will see that the selected property is false. We won't update the element's list of attributes, intending to omit the selected property, but because the selected property from the DOM is already there, we'll end up serializing it and including it in the element's attribute list in the recording. This is wrong both from a privacy perspective (we shouldn't capture this attribute on masked elements) and because it could cause us to show the wrong <option> as selected at replay time.

The fix is always delete selected from the element's list of attributes if the selected property is false. This aligns with what we do for checked, so in addition to fixing this bug, it ends up simplifying the tests and reducing the number of different attribute serialization behaviors we need to reason about.

Changes

Following the plan above, this PR:

  • Changes our encoding for checked and selected. Now, the attribute value is the empty string when these attributes are logically true, and the attributes are not recorded when they are logically false.
  • Adjusts the code for each so they follow a consistent pattern: if the element is not masked and the attribute is logically true, it's added to the attribute list; otherwise, it's deleted. This fixes the selected bug described above.
  • Narrows the types for serializeDOMAttributes() and related functions, since they can no longer produce boolean values.
  • Updates the tests to capture the new behavior.

Test instructions

It's enough to visit a page with an HTML <select> dropdown, a set of checkbox inputs, or a set of radio button inputs, and look at the generated session replay using the browser SDK extension. We should continue to show the correct checked or selected item after the change.

Checklist

  • Tested locally
  • Tested on staging
  • Added unit tests for this change.
  • Added e2e/integration tests for this change.

@sethfowler-datadog sethfowler-datadog requested a review from a team as a code owner December 4, 2025 18:12
Copy link
Contributor

@BeltranBulbarellaDD BeltranBulbarellaDD left a comment

Choose a reason for hiding this comment

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

LGTM and it works as expected!

Copy link

@Tizi42 Tizi42 left a comment

Choose a reason for hiding this comment

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

👍

Base automatically changed from seth.fowler/PANA-5053-separate-dom-and-virtual-attribute-serialization to main December 15, 2025 17:16
@sethfowler-datadog sethfowler-datadog requested a review from a team as a code owner December 15, 2025 17:16
…ibute-values-as-strings

* main: (25 commits)
  🎨 [PANA-5053] Separate DOM and virtual attribute serialization (#3998)
  🐛 clear chain after finalize (#4027)
  🎨 [PANA-5222] Make CODEOWNERS more accurate for recording code (#4034)
  ♻️ Replace longTaskRegistry by longTaskContexts (#4013)
  👷 Bump staging to staging-51
  Add flagEvaluationEndpointBuilder to TransportConfiguration interface. (#4025)
  👷 Enable more renovate flags... (#4023)
  🐛 fix developer extension packaging (#4024)
  👷 add a script to easily create an access token (#4020)
  👷 Ensure that renovate do a full install (#4021)
  [RUM Browser Profiler] stop profiler when session expires (#4011)
  👷 Ensure to have tarballs built at install (#4019)
  👷: migrate config renovate.json (#4016)
  👷 Update dependency vite to v5.4.21 [SECURITY] (#4015)
  👷 Configure test apps dependencies  (#4014)
  v6.25.0 (#4012)
  👷 Update dependency vite to v5.4.21 [SECURITY] (#4010)
  👷 Include test apps in renovate scan (#4009)
  👷 restore canary deployment (#4008)
  👷 Update all non-major dependencies (#3980)
  ...
@cit-pr-commenter
Copy link

cit-pr-commenter bot commented Dec 15, 2025

Bundles Sizes Evolution

📦 Bundle Name Base Size Local Size 𝚫 𝚫% Status
Rum 164.33 KiB 164.33 KiB 0 B 0.00%
Rum Profiler 4.32 KiB 4.32 KiB 0 B 0.00%
Rum Recorder 19.88 KiB 19.87 KiB -6 B -0.03%
Logs 56.14 KiB 56.14 KiB 0 B 0.00%
Flagging 944 B 944 B 0 B 0.00%
Rum Slim 121.61 KiB 121.61 KiB 0 B 0.00%
Worker 23.63 KiB 23.63 KiB 0 B 0.00%
🚀 CPU Performance
Action Name Base CPU Time (ms) Local CPU Time (ms) 𝚫%
RUM - add global context 0.004 0.0053 +32.50%
RUM - add action 0.012 0.0184 +53.33%
RUM - add error 0.0116 0.0186 +60.34%
RUM - add timing 0.0026 0.0031 +19.23%
RUM - start view 0.0031 0.0043 +38.71%
RUM - start/stop session replay recording 0.0009 0.0012 +33.33%
Logs - log message 0.013 0.0159 +22.31%
🧠 Memory Performance
Action Name Base Memory Consumption Local Memory Consumption 𝚫
RUM - add global context 26.17 KiB 26.46 KiB +295 B
RUM - add action 48.38 KiB 49.88 KiB +1.50 KiB
RUM - add timing 24.60 KiB 24.66 KiB +61 B
RUM - add error 53.51 KiB 54.13 KiB +633 B
RUM - start/stop session replay recording 24.10 KiB 23.90 KiB -211 B
RUM - start view 421.90 KiB 422.94 KiB +1.04 KiB
Logs - log message 42.97 KiB 43.00 KiB +34 B

🔗 RealWorld

@datadog-datadog-prod-us1
Copy link

✅ Tests

🎉 All green!

❄️ No new flaky tests detected
🧪 All tests passed

🎯 Code Coverage
Patch Coverage: 100.00%
Overall Coverage: 92.67% (+0.00%)

View detailed report

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 6dade4e | Docs | Datadog PR Page | Was this helpful? Give us feedback!

@sethfowler-datadog sethfowler-datadog merged commit 6c3e757 into main Dec 15, 2025
21 checks passed
@sethfowler-datadog sethfowler-datadog deleted the seth.fowler/PANA-5105-serialize-all-dom-attribute-values-as-strings branch December 15, 2025 18:13
@github-actions github-actions bot locked and limited conversation to collaborators Dec 15, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants