* 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
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