Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Webgl-patches #58

Merged
merged 4 commits into from
Jan 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@highlight-run/rrweb",
"version": "1.1.0",
"version": "1.1.1",
"description": "record and replay the web",
"scripts": {
"test": "npm run bundle:browser && cross-env TS_NODE_CACHE=false TS_NODE_FILES=true mocha -r ts-node/register -r ignore-styles -r jsdom-global/register test/**.test.ts",
Expand Down
49 changes: 42 additions & 7 deletions src/record/mutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -489,13 +489,47 @@ export default class MutationBuffer {
break;
}
}
// overwrite attribute if the mutations was triggered in same time
item.attributes[m.attributeName!] = transformAttribute(
this.doc,
(m.target as HTMLElement).tagName,
m.attributeName!,
value!,
);
if (m.attributeName === 'style') {
const old = this.doc.createElement('span');
if (m.oldValue) {
old.setAttribute('style', m.oldValue);
}
if (
item.attributes.style === undefined ||
item.attributes.style === null
) {
item.attributes.style = {};
}
const styleObj = item.attributes.style as styleAttributeValue;
for (const pname of Array.from(target.style)) {
const newValue = target.style.getPropertyValue(pname);
const newPriority = target.style.getPropertyPriority(pname);
if (
newValue !== old.style.getPropertyValue(pname) ||
newPriority !== old.style.getPropertyPriority(pname)
) {
if (newPriority === '') {
styleObj[pname] = newValue;
} else {
styleObj[pname] = [newValue, newPriority];
}
}
}
for (const pname of Array.from(old.style)) {
if (target.style.getPropertyValue(pname) === '') {
// "if not set, returns the empty string"
styleObj[pname] = false; // delete
}
}
} else {
// overwrite attribute if the mutations was triggered in same time
item.attributes[m.attributeName!] = transformAttribute(
this.doc,
(m.target as HTMLElement).tagName,
m.attributeName!,
value!,
);
}
break;
}
case 'childList': {
Expand Down Expand Up @@ -569,6 +603,7 @@ export default class MutationBuffer {
this.addedSet.add(n);
this.droppedSet.delete(n);
}

// if this node is blocked `serializeNode` will turn it into a placeholder element
// but we have to remove it's children otherwise they will be added as placeholders too
if (!isBlocked(n, this.blockClass))
Expand Down
2 changes: 1 addition & 1 deletion src/record/shadow-dom-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export class ShadowDomManager {
this.bypassOptions.iframeManager,
this,
shadowRoot,
this.bypassOptions.enableStrictPrivacy
this.bypassOptions.enableStrictPrivacy,
);
initScrollObserver(
this.scrollCb,
Expand Down
112 changes: 66 additions & 46 deletions src/replay/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
styleAttributeValue,
styleValueWithPriority,
CanvasContext,
IWindow,
} from '../types';
import {
createMirror,
Expand Down Expand Up @@ -110,6 +111,7 @@ export class Replayer {

private emitter: Emitter = mitt();

private nextUserInteractionEvent: eventWithTime | null;
private activityIntervals: Array<SessionInterval> = [];
private inactiveEndTimestamp: number | null;

Expand All @@ -127,9 +129,6 @@ export class Replayer {

private imageMap: Map<eventWithTime | string, HTMLImageElement> = new Map();

/** The first time the player is playing. */
private nextUserInteractionEvent: eventWithTime | null;

private mirror: Mirror = createMirror();

private firstFullSnapshot: eventWithTime | true | null = null;
Expand Down Expand Up @@ -555,7 +554,7 @@ export class Replayer {
this.iframe.contentDocument,
);

polyfill(this.iframe.contentWindow as Window & typeof globalThis);
polyfill(this.iframe.contentWindow as IWindow);
}
}

Expand Down Expand Up @@ -630,12 +629,11 @@ export class Replayer {
};
break;
case EventType.Meta:
castFn = () => {
castFn = () =>
this.emitter.emit(ReplayerEvents.Resize, {
width: event.data.width,
height: event.data.height,
});
};
break;
case EventType.FullSnapshot:
castFn = () => {
Expand Down Expand Up @@ -894,37 +892,6 @@ export class Replayer {
}
}

private hasImageArg(args: any[]): boolean {
for (const arg of args) {
if (!arg || typeof arg !== 'object') {
// do nothing
} else if ('rr_type' in arg && 'args' in arg) {
if (this.hasImageArg(arg.args)) return true;
} else if ('rr_type' in arg && arg.rr_type === 'HTMLImageElement') {
return true; // has image!
} else if (arg instanceof Array) {
if (this.hasImageArg(arg)) return true;
}
}
return false;
}

private getImageArgs(args: any[]): string[] {
const images: string[] = [];
for (const arg of args) {
if (!arg || typeof arg !== 'object') {
// do nothing
} else if ('rr_type' in arg && 'args' in arg) {
images.push(...this.getImageArgs(arg.args));
} else if ('rr_type' in arg && arg.rr_type === 'HTMLImageElement') {
images.push(arg.src);
} else if (arg instanceof Array) {
images.push(...this.getImageArgs(arg));
}
}
return images;
}

/**
* pause when loading style sheet, resume when loaded all timeout exceed
*/
Expand Down Expand Up @@ -981,6 +948,37 @@ export class Replayer {
}
}

private hasImageArg(args: any[]): boolean {
for (const arg of args) {
if (!arg || typeof arg !== 'object') {
// do nothing
} else if ('rr_type' in arg && 'args' in arg) {
if (this.hasImageArg(arg.args)) return true;
} else if ('rr_type' in arg && arg.rr_type === 'HTMLImageElement') {
return true; // has image!
} else if (arg instanceof Array) {
if (this.hasImageArg(arg)) return true;
}
}
return false;
}

private getImageArgs(args: any[]): string[] {
const images: string[] = [];
for (const arg of args) {
if (!arg || typeof arg !== 'object') {
// do nothing
} else if ('rr_type' in arg && 'args' in arg) {
images.push(...this.getImageArgs(arg.args));
} else if ('rr_type' in arg && arg.rr_type === 'HTMLImageElement') {
images.push(arg.src);
} else if (arg instanceof Array) {
images.push(...this.getImageArgs(arg));
}
}
return images;
}

/**
* pause when there are some canvas drawImage args need to be loaded
*/
Expand All @@ -991,8 +989,6 @@ export class Replayer {
};
this.emitter.on(ReplayerEvents.Start, stateHandler);
this.emitter.on(ReplayerEvents.Pause, stateHandler);
let count = 0;
let resolved = 0;
for (const event of this.service.state.context.events) {
if (
event.type === EventType.IncrementalSnapshot &&
Expand All @@ -1001,7 +997,6 @@ export class Replayer {
typeof event.data.args[0] === 'string' &&
!this.imageMap.has(event)
) {
count++;
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const imgd = ctx?.createImageData(canvas.width, canvas.height);
Expand All @@ -1020,9 +1015,6 @@ export class Replayer {
});
}
}
if (count !== resolved) {
this.service.send({ type: 'PAUSE' });
}
}

private applyIncremental(
Expand Down Expand Up @@ -1393,13 +1385,15 @@ export class Replayer {
if (!target) {
return this.debugNodeNotFound(d, d.id);
}

canvasMutation({
event: e,
mutation: d,
target: (target as unknown) as HTMLCanvasElement,
imageMap: this.imageMap,
errorHandler: this.warnCanvasMutationFailed.bind(this),
});

break;
}
case IncrementalSource.Font: {
Expand Down Expand Up @@ -1597,6 +1591,21 @@ export class Replayer {
return;
}

if (
'__sn' in parent &&
parent.__sn.type === NodeType.Element &&
parent.__sn.tagName === 'textarea' &&
mutation.node.type === NodeType.Text
) {
// https://github.com/rrweb-io/rrweb/issues/745
// parent is textarea, will only keep one child node as the value
for (const c of Array.from(parent.childNodes)) {
if (c.nodeType === parent.TEXT_NODE) {
parent.removeChild(c);
}
}
}

if (previous && previous.nextSibling && previous.nextSibling.parentNode) {
parent.insertBefore(target, previous.nextSibling);
} else if (next && next.parentNode) {
Expand Down Expand Up @@ -1755,6 +1764,13 @@ export class Replayer {
left: d.x,
behavior: 'smooth',
});
} else if (target.__sn.type === NodeType.Document) {
// nest iframe content document
((target as unknown) as Document).defaultView!.scrollTo({
top: d.y,
left: d.x,
behavior: 'smooth',
});
} else {
try {
((target as Node) as Element).scrollTop = d.y;
Expand Down Expand Up @@ -1896,7 +1912,7 @@ export class Replayer {
}

private backToNormal() {
this.inactiveEndTimestamp = null;
this.nextUserInteractionEvent = null;
if (this.speedService.state.matches('normal')) {
return;
}
Expand Down Expand Up @@ -1982,9 +1998,13 @@ export class Replayer {

private restoreNodeSheet(node: INode) {
const storedRules = this.virtualStyleRulesMap.get(node);
if (node.nodeName !== 'STYLE') return;
if (node.nodeName !== 'STYLE') {
return;
}

if (!storedRules) return;
if (!storedRules) {
return;
}

const styleNode = (node as unknown) as HTMLStyleElement;

Expand Down
Loading