You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
CI: add a prettier GitHub action to format code automatically (#988)
* CI: add a prettier GitHub action to format code automatically
* improve GitHub Action config and format some files
* Apply formatting changes
* CI: make the prettier action a standalone action
* Apply formatting changes
* CI: add push as new trigger event
Co-authored-by: Mark-Fenng <[email protected]>
Copy file name to clipboardExpand all lines: docs/replay.md
+5
Original file line number
Diff line number
Diff line change
@@ -1,7 +1,9 @@
1
1
# Replay
2
+
2
3
A design principle of rrweb is to process as little as possible on the recording side, minimizing the impact on the recorded page. This means we need to do some special processing on the replay side.
3
4
4
5
## High precision timer
6
+
5
7
During replay, we will get the complete snapshot chain at one time. If all the snapshots are executed in sequence, we can directly get the last state of the recorded page, but what we need is to synchronously initialize the first full snapshot, and then apply the remaining incremental snapshots asynchronously. Using a time interval we replay each incremental snapshot one after the other, which requires a high-precision timer.
6
8
7
9
The reason why **high precision** is emphasized is because the native `setTimeout` does not guarantee accurate execution after the set delay time, for example, when the main thread is blocked.
@@ -11,6 +13,7 @@ For our replay function, this imprecise delay is unacceptable and can lead to va
11
13
At the same time, the custom timer is also the basis for our "fast forward" function.
12
14
13
15
## Completing missing nodes
16
+
14
17
The delay serialization strategy when rrweb uses MutationObserver is mentioned in the [incremental snapshot design](./observer.md), which may result in the following scenarios where we cannot record a full incremental snapshot:
15
18
16
19
```
@@ -29,6 +32,7 @@ During replay, when we process the incremental snapshot of the new `foo`, we kno
29
32
After processing the incremental snapshot of the new n1, we normally process and insert `bar`. After the replay is completed, we check whether the neighbor node id of `foo` points to a node which is in the missing node pool. If it matches, then it will be removed from the pool and be inserted into the DOM tree.
30
33
31
34
## Simulation Hover
35
+
32
36
CSS styles for the `:hover` selector are present in many web pages, but we can't trigger the hover state via JavaScript. So when playing back we need to simulate the hover state to make the style display correctly.
33
37
34
38
The specific method includes two parts:
@@ -37,6 +41,7 @@ The specific method includes two parts:
37
41
2. When playing back the mouse up mouse interaction event, add the `.:hover` class name to the event target and all its ancestors, and remove it when the mouse moves away again.
38
42
39
43
## Play from any point in time
44
+
40
45
In addition to the basic replay features, we also want players like `rrweb-player` to provide similar functionality to video players, such as dragging and dropping to the progress bar to any point in time.
41
46
42
47
In actual implementation, we pass a start time to the method. We can then divide the snapshot chain into two parts: The parts before and the part after the start time. Then, the snapshot chain before the start time is executed synchronously, and then the snapshot chain after the starting times uses the normal asynchronous execution. This way we can achieve starting replay from any point in time.
Copy file name to clipboardExpand all lines: docs/sandbox.md
+3
Original file line number
Diff line number
Diff line change
@@ -5,6 +5,7 @@ In the [serialization design](./serialization.md) we mentioned the "de-scripting
5
5
There are many kinds of scripting behaviors. A filtering approach to getting rid of these scripts will never be a complete solution, and once a script slips through and is executed, it may cause irreversible unintended consequences. So we use the iframe sandbox feature provided by HTML for browser-level restrictions.
6
6
7
7
## iframe sandbox
8
+
8
9
We reconstruct the recorded DOM in an `iframe` element when we rebuild the snapshot. By setting its `sandbox` attribute, we can disable the following behavior:
9
10
10
11
- Form submission
@@ -14,13 +15,15 @@ We reconstruct the recorded DOM in an `iframe` element when we rebuild the snaps
14
15
This is in line with our expectations, especially when dealing with JS scripts is safer and more reliable than implementing this security ourselves.
15
16
16
17
## Avoid link jumps
18
+
17
19
When you click the a element link, the default event is to jump to the URL corresponding to its href attribute. During replay, we will ensure visually correct replay by rebuilding the page DOM after the jump, and the original jump should be prohibited.
18
20
19
21
Usually we will capture all an elements click events through the event handler proxy and disable the default event via `event.preventDefault()`. But when we put the replay page in the sandbox, all the event handlers will not be executed, and we will not be able to implement the event delegation.
20
22
21
23
When replaying interactive events, note that replaying the JS `click` event is not nessecary because click events do not have any impact when JS is disabled. However, in order to optimize the replay effect, we can add special animation effects to visualize elements being clicked with the mouse, to clearly show the viewer that a click has occurred.
22
24
23
25
## iframe style settings
26
+
24
27
Since we're rebuilding the DOM in an iframe, we can't affect the elements in the iframe through the CSS stylesheet of the parent page. But if JS scripts are not allowed to execute, the `noscript` tag will be displayed, and we want to hide it. So we need to dynamically add styles to the iframe. The sample code is as follows:
Copy file name to clipboardExpand all lines: docs/serialization.md
+7-5
Original file line number
Diff line number
Diff line change
@@ -1,4 +1,5 @@
1
1
# Serialization
2
+
2
3
If you only need to record and replay changes within the browser locally, then we can simply save the current view by deep copying the DOM object. For example, the following code implementation (simplified example with jQuery, saves only the body part):
3
4
4
5
```javascript
@@ -18,6 +19,7 @@ We do not use an existing open source solutions such as [parse5](https://github.
18
19
2. This part of the code needs to run on the recorded page, and we want to control the amount of code as much as possible, only retaining the necessary functions.
19
20
20
21
## Special handling in serialization
22
+
21
23
The reason why our serialization method is non-standard is because we still need to do the following parts:
22
24
23
25
1. Output needs to be descriptive. All JavaScript in the original recorded page should not be executed on replay. In rrweb we do this by replacing `script` tags with placeholder `noscript` tags in snapshots. The content inside the script is no longer important. We instead record any changes to the DOM that scripts cause, and we do not need to fully record large amounts of script content that may be present on the original web page.
@@ -26,15 +28,15 @@ The reason why our serialization method is non-standard is because we still need
26
28
4. We want to record the contents of the CSS style sheet. If the recorded page links to external style sheets, we can get its parsed CSS rules from the browser, generate an inline style sheet containing all these rules. This way stylesheets that are not always accessible (for example, because they are located on an intranet or localhost) are included in the recording and can be replayed correctly.
27
29
28
30
## Uniquely identifies
31
+
29
32
At the same time, our serialization should also include both full and incremental types. Full serialization can transform a DOM tree into a corresponding tree data structure.
30
33
31
34
For example, the following DOM tree:
32
35
33
36
```html
34
37
<html>
35
38
<body>
36
-
<header>
37
-
</header>
39
+
<header></header>
38
40
</body>
39
41
</html>
40
42
```
@@ -98,12 +100,12 @@ There are two things to note in this serialization result:
98
100
99
101
Imagine if we recorded the click of a button on the same page and played it back, we can record the operation in the following format (that is what we call an incremental snapshot):
100
102
101
-
```javascript
103
+
```typescript
102
104
typeclickSnapshot= {
103
105
source:'MouseInteraction';
104
106
type:'Click';
105
107
node:HTMLButtonElement;
106
-
}
108
+
};
107
109
```
108
110
109
111
The operation can be executed again by `snapshot.node.click()`.
*Note that you will need to have [Node.js](https://nodejs.org) installed.*
21
-
20
+
_Note that you will need to have [Node.js](https://nodejs.org) installed._
22
21
23
22
## Get started
24
23
@@ -37,7 +36,6 @@ npm run dev
37
36
38
37
Navigate to [localhost:5000](http://localhost:5000). You should see your app running. Edit a component file in `src`, save it, and reload the page to see your changes.
0 commit comments