Commit Graph

23 Commits

Author SHA1 Message Date
re-fort
b1b25447ff Add options to mask texts (#540)
* feat: add options to mask texts

* feat: add the default mask function

* refactor: rename options to identify the difference between  mask text and mask input

* test: add tests about masking

* doc: add options about masking

* chore: bump up rrweb-snapshot version
2026-04-01 12:00:00 +08:00
Yanzhen Yu
3e8cacbb95 fix #531 use getRootNode with precheck 2026-04-01 12:00:00 +08:00
Yanzhen Yu
0e688bba0c impl shadow DOM manager
part of #38
1. observe DOM mutations in shadow DOM
2. rebuild DOM mutations in shadow DOM
2026-04-01 12:00:00 +08:00
Yanzhen Yu
4ed82274ae close #491 check whether link node is head 2026-04-01 12:00:00 +08:00
yz-yu
33f0ac5cfe Impl record iframe (#481)
* Impl record iframe

* iframe observe

* temp: add bundle file to git

* update bundle

* update with pick

* update bundle

* fix fragment map remove

* feat: add an option to determine whether to pause CSS animation when playback is paused (#428)

set pauseAnimation to true by default

* fix: elements would lose some states like scroll position because of "virtual parent" optimization (#427)

* fix: elements would lose some state like scroll position because of "virtual parent" optimization

* refactor: the bugfix code

bug: elements would lose some state like scroll position because of "virtual parent" optimization

* fix: an error occured at applyMutation(remove nodes part)

error message:
Uncaught (in promise) DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node

* pick fixes

* revert ignore file

* re-impl iframe record

* re-impl iframe replay

* code housekeeping

* move multi layer dimension calculation to replay side

* update test cases

* teardown test server

* upgrade rrweb-snapshot with iframe load timeout

Co-authored-by: Lucky Feng <yun.feng@smartx.com>
2026-04-01 12:00:00 +08:00
Yanzhen Yu
25d23bdc35 read __rrMutationObserver from window 2026-04-01 12:00:00 +08:00
Eoghan Murray
9f3f4d15f2 Discovered that the common case of mouse movement or scrolling happening during takeFullSnapshot was causing mutations to be immediately emitted, contrary to the goal of https://github.com/rrweb-io/rrweb/pull/385 (#470) 2026-04-01 12:00:00 +08:00
Yanzhen Yu
57f15ddc02 fix #460 ignore added node that are not in document anymore 2026-04-01 12:00:00 +08:00
Yanzhen Yu
bc6e6101d3 clean addList when meet a corner case 2026-04-01 12:00:00 +08:00
Yanzhen Yu
175c98d1ae fix #452 check isBlocked on add mutation's target 2026-04-01 12:00:00 +08:00
Yanzhen Yu
ec5b7a4635 pick #286, export slim DOM options 2026-04-01 12:00:00 +08:00
Yanzhen Yu
e9a5aeed06 expose blockSelector as a public option 2026-04-01 12:00:00 +08:00
Yanzhen Yu
528b8373a0 upgrade to rrweb-snapshot v1.0 2026-04-01 12:00:00 +08:00
Yanzhen Yu
f8c0643eb8 fix serializeNodeWithId args 2026-04-01 12:00:00 +08:00
Yanzhen Yu
f511425d7e ignore removed move set 2026-04-01 12:00:00 +08:00
Eoghan Murray
70fe32b5d2 Suspend mutations during snapshot (#385)
* The `processMutations` function needed to be bound to the `mutationBuffer` object, as otherwise `this` referred to the `MutationObserver` object itself

* Enable external pausing of mutation buffer emissions

 - no automatic pausing based on e.g. pageVisibility yet, assuming such a thing is desirable
   https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API
 - user code has to call new API method `freezePage` e.g. when page is hidden or after a timeout
 - automatically unpauses when the next user initiated event occurs
   (am assuming everything that isn't a mutation event counts as 'user initiated'
   either way think this is the correct thing to do until I see a counterexample
   of an event that shouldn't cause the mutations to be unbufferred)

* Avoid a build up of duplicate `adds` by delaying pushing to adds until emission time

* Need to export freezePage in order to use it from rrweb.min.js

* Add a test to check if mutations can be turned off with the `freezePage` method

* I noticed out of order ids (in terms of a DOM walk) in a FullSnapshot.  A DOM mutation was executed against the mirror asynchronously before it could be fully processed. This would lead to a situation in replay where a mutation is executed against a DOM tree that already has the mutation applied. This changeset fixes that by freezing any mutations until the snapshot is completed.

* Remove attribute modifications from a mutation event that were incorrect in that they were repeating the attributes of those nodes present in the 'adds' array of the same mutation

* I've manually verified that this empty text node is actually removed when the dropdown is opened:

document.getElementById('select2-results-1').childNodes
NodeList(2) [li.select2-results-dept-0.select2-result.select2-result-selectable.select2-highlighted, li.select2-results-dept-0.select2-result.select2-result-selectable]

and also that it is not reinstated after the second `await page.click('.select2-container');`

* Rearrange when removal happens in order to satisfy tests. I'm also reverting a recent test change (2600fe7) so that tests pass after this rearrangement; I believe that test change to still be the correct way of doing it, but maybe it is not strictly important that there are extra mutations on attributes of just added nodes

* As mutations are now paused during FullSnapshots, we shouldn't be counting this as a 'user emission'. We automatically emit mutations after unpause anyway ('emit anything queued up now')

* Ensure that we clear arrays before emitting, as the mutation could have the side effect of triggering a FullSnapshot (checkoutEveryNth), which would otherwise re-trigger emission of same mutation (through the new pause/fullsnapshot/mutationemit/unpause process)

* Don't let the programattic pausing during TakeFullSnapshot accidentally unpause a manual call to the API method `freezePage`

* Rename paused -> frozen for consistency and change to use getter/setter access methods
2026-04-01 12:00:00 +08:00
Yanzhen Yu
0e30a819ce clean up __ln property when remove node 2026-04-01 12:00:00 +08:00
yz-yu
6fad7f642c record canvas mutations (#296)
* record canvas mutations

close #60, #261

This patch implements the canvas mutation observer.
It consists of both the record and the replay side changes.

In the record side, we add a `recordCanvas` flag to indicate
whether to record canvas elements and the flag defaults to false.
Different from our other observers, the canvas observer was
disabled by default. Because some applications with heavy canvas
usage may emit a lot of data as canvas changed, especially the
scenarios that use a lot of `drawImage` API.
So the behavior should be audited by users and only record canvas
when the flag was set to true.

In the replay side, we add a `UNSAFE_replayCanvas` flag to indicate
whether to replay canvas mutations.
Similar to the `recordCanvas` flag, `UNSAFE_replayCanvas` defaults
to false. But unlike the record canvas implementation is stable and
safe, the replay canvas implementation is UNSAFE.
It's unsafe because we need to add `allow-scripts` to the replay
sandbox, which may cause some unexpected script execution. Currently,
users should be aware of this implementation detail and enable this
feature carefully.

* update canvas integration test
2026-04-01 12:00:00 +08:00
yz-yu
6de74d7bca close #280. Improve the performance of the DOM mutation observer. (#284)
This issue was originally reported in #280 but may also relate
to #167 and other potential performance issues in the recording.

In #206 I implemented the new mutation observer which will defer
the serialization of DOM, which helps us to have a consistent DOM
order for the replay.

In this implementation, we use an array to represent the `addQueue`.
Whenever we need to consume the queue, we will iterate it to make
sure there is no dead loop, and then shift the first item to see
whether it can be serialized at the new timing.

But this implementation may be very slow when there are a lot of newly
added DOM since it will do an O(n^2) iteration.

For example, if we have three newly added DOM `n1`, `n2`, `n3`,
the iteration looks like this:
```
[n1, n2, n3]
	n1 -> n2 -> n3, consume n3
[n1, n2]
	n1 -> n2, consume n2
[n1]
	n1, consume n1
```
We should have a better performance if te iteration looks like this:
```
[n1, n2, n3]
	n3, consume n3
[n1, n2]
	n2, consume n2
[n1]
	n1, consume n1
```

Simply reverse the mutation payload does not work, because it does
not always as same as the DOM order.

So in this patch, we replace the `addQueue` with a double linked list,
which can:
1. represent the DOM order in its data structure
2. has an O(1) time complexity when looking up the sibling of a list item
3. has an O(1) time complexity when removing a list item
2026-04-01 12:00:00 +08:00
Yanzhen Yu
0d6fe56b80 close #263 Since we improved the block class strategy,
we need to check parent node before pushAdd.
2026-04-01 12:00:00 +08:00
Yanzhen Yu
6c87b03e0d close #240 fix block class to handle text node changes 2026-04-01 12:00:00 +08:00
yz-yu
8bb1c791f5 mask input options and sampling options (#252)
* part of #80, support mask input options

* close #188 enhance sampling options
Use a more general sampling strategy interface to describe the
configuration of sampling events collection.

Implemented mousmove, mouse interaction, scroll and input sampling
strategy.
2026-04-01 12:00:00 +08:00
Eoghan Murray
d57cae3d51 Move mutation processing into it's own class (#223)
* Move mutation processing into it's own object.

This should stand on it's own as a refactor, but is intended as a basis
for exposing the new MutationBuffer object to further outside control e.g.
to 'mute' or batch up mutation emission when the page becomes inactive
from a https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API
point of view

* The `processMutations` function needed to be bound to the `mutationBuffer` object, as otherwise `this` referred to the `MutationObserver` object itself

* Neglected to add this output of `npm run typings`

* Get around the binding problem by using Arrow function expressions

* Prettier formatting
2026-04-01 12:00:00 +08:00