frontend (feat): maintaining of iframes in replay & hover styles rewriting
This commit is contained in:
parent
2ebdd3d67f
commit
43862e32e1
3 changed files with 171 additions and 5 deletions
|
|
@ -3,7 +3,7 @@ import type { Message, SetNodeScroll, CreateElementNode } from '../messages';
|
|||
import type { TimedMessage } from '../Timed';
|
||||
|
||||
import logger from 'App/logger';
|
||||
import StylesManager from './StylesManager';
|
||||
import StylesManager, { rewriteNodeStyleSheet } from './StylesManager';
|
||||
import ListWalker from './ListWalker';
|
||||
import type { Timed }from '../Timed';
|
||||
|
||||
|
|
@ -147,6 +147,7 @@ export default class DOMManager extends ListWalker<TimedMessage> {
|
|||
this.insertNode(msg);
|
||||
break;
|
||||
case "create_element_node":
|
||||
// console.log('elementnode', msg)
|
||||
if (msg.svg) {
|
||||
this.nl[ msg.id ] = document.createElementNS('http://www.w3.org/2000/svg', msg.tag);
|
||||
} else {
|
||||
|
|
@ -207,9 +208,14 @@ export default class DOMManager extends ListWalker<TimedMessage> {
|
|||
break;
|
||||
case "set_node_data":
|
||||
case "set_css_data":
|
||||
if (!this.nl[ msg.id ]) { logger.error("Node not found", msg); break; }
|
||||
node = this.nl[ msg.id ]
|
||||
if (!node) { logger.error("Node not found", msg); break; }
|
||||
// @ts-ignore
|
||||
this.nl[ msg.id ].data = msg.data;
|
||||
node.data = msg.data;
|
||||
if (node instanceof HTMLStyleElement) {
|
||||
const doc = this.screen.document
|
||||
doc && rewriteNodeStyleSheet(doc, node)
|
||||
}
|
||||
break;
|
||||
case "css_insert_rule":
|
||||
node = this.nl[ msg.id ];
|
||||
|
|
@ -239,6 +245,23 @@ export default class DOMManager extends ListWalker<TimedMessage> {
|
|||
} catch (e) {
|
||||
logger.warn(e, msg)
|
||||
}
|
||||
break;
|
||||
case "create_i_frame_document":
|
||||
// console.log('ifr', msg)
|
||||
node = this.nl[ msg.frameID ];
|
||||
if (!(node instanceof HTMLIFrameElement)) {
|
||||
logger.warn("create_i_frame_document message. Node is not iframe")
|
||||
return;
|
||||
}
|
||||
console.log("iframe", msg)
|
||||
// await new Promise(resolve => { node.onload = resolve })
|
||||
|
||||
const doc = node.contentDocument;
|
||||
if (!doc) {
|
||||
logger.warn("No iframe doc", msg, node, node.contentDocument);
|
||||
return;
|
||||
}
|
||||
this.nl[ msg.id ] = doc.documentElement
|
||||
break;
|
||||
//not sure what to do with this one
|
||||
//case "disconnected":
|
||||
|
|
|
|||
109
frontend/app/player/MessageDistributor/managers/StylesManager.ts
Normal file
109
frontend/app/player/MessageDistributor/managers/StylesManager.ts
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
|
||||
import type StatedScreen from '../StatedScreen';
|
||||
import type { CssInsertRule, CssDeleteRule } from '../messages';
|
||||
import type { Timed } from '../Timed';
|
||||
|
||||
type CSSRuleMessage = CssInsertRule | CssDeleteRule;
|
||||
type TimedCSSRuleMessage = Timed & CSSRuleMessage;
|
||||
|
||||
import logger from 'App/logger';
|
||||
import ListWalker from './ListWalker';
|
||||
|
||||
|
||||
const HOVER_CN = "-openreplay-hover";
|
||||
const HOVER_SELECTOR = `.${HOVER_CN}`;
|
||||
|
||||
// Doesn't work with css files (hasOwnProperty)
|
||||
export function rewriteNodeStyleSheet(doc: Document, node: HTMLLinkElement | HTMLStyleElement) {
|
||||
const ss = Object.values(doc.styleSheets).find(s => s.ownerNode === node);
|
||||
if (!ss || !ss.hasOwnProperty('rules')) { return; }
|
||||
for(let i = 0; i < ss.rules.length; i++){
|
||||
const r = ss.rules[i]
|
||||
if (r instanceof CSSStyleRule) {
|
||||
r.selectorText = r.selectorText.replace('/\:hover/g', HOVER_SELECTOR)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default class StylesManager extends ListWalker<TimedCSSRuleMessage> {
|
||||
private linkLoadingCount: number = 0;
|
||||
private linkLoadPromises: Array<Promise<void>> = [];
|
||||
private skipCSSLinks: Array<string> = []; // should be common for all pages
|
||||
|
||||
constructor(private readonly screen: StatedScreen) {
|
||||
super();
|
||||
}
|
||||
|
||||
reset():void {
|
||||
super.reset();
|
||||
this.linkLoadingCount = 0;
|
||||
this.linkLoadPromises = [];
|
||||
|
||||
//cancel all promises? tothinkaboutit
|
||||
}
|
||||
|
||||
setStyleHandlers(node: HTMLLinkElement, value: string): void {
|
||||
let timeoutId;
|
||||
const promise = new Promise((resolve) => {
|
||||
if (this.skipCSSLinks.includes(value)) resolve(null);
|
||||
this.linkLoadingCount++;
|
||||
this.screen.setCSSLoading(true);
|
||||
const addSkipAndResolve = () => {
|
||||
this.skipCSSLinks.push(value); // watch out
|
||||
resolve(null);
|
||||
}
|
||||
timeoutId = setTimeout(addSkipAndResolve, 4000);
|
||||
|
||||
node.onload = () => {
|
||||
const doc = this.screen.document;
|
||||
doc && rewriteNodeStyleSheet(doc, node);
|
||||
resolve(null);
|
||||
}
|
||||
node.onerror = addSkipAndResolve;
|
||||
}).then(() => {
|
||||
node.onload = null;
|
||||
node.onerror = null;
|
||||
clearTimeout(timeoutId);
|
||||
this.linkLoadingCount--;
|
||||
if (this.linkLoadingCount === 0) {
|
||||
this.screen.setCSSLoading(false);
|
||||
}
|
||||
});
|
||||
this.linkLoadPromises.push(promise);
|
||||
}
|
||||
|
||||
private manageRule = (msg: CSSRuleMessage):void => {
|
||||
// if (msg.tp === "css_insert_rule") {
|
||||
// let styleSheet = this.#screen.document.styleSheets[ msg.stylesheetID ];
|
||||
// if (!styleSheet) {
|
||||
// logger.log("No stylesheet with corresponding ID found: ", msg)
|
||||
// styleSheet = this.#screen.document.styleSheets[0];
|
||||
// if (!styleSheet) {
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// try {
|
||||
// styleSheet.insertRule(msg.rule, msg.index);
|
||||
// } catch (e) {
|
||||
// logger.log(e, msg)
|
||||
// //const index = Math.min(msg.index, styleSheet.cssRules.length);
|
||||
// styleSheet.insertRule(msg.rule, styleSheet.cssRules.length);
|
||||
// //styleSheet.ownerNode.innerHTML += msg.rule;
|
||||
// }
|
||||
// }
|
||||
// if (msg.tp === "css_delete_rule") {
|
||||
// // console.warn('Warning: STYLESHEET_DELETE_RULE msg')
|
||||
// const styleSheet = this.#screen.document.styleSheets[msg.stylesheetID];
|
||||
// if (!styleSheet) {
|
||||
// logger.log("No stylesheet with corresponding ID found: ", msg)
|
||||
// return;
|
||||
// }
|
||||
// styleSheet.deleteRule(msg.index);
|
||||
// }
|
||||
}
|
||||
|
||||
moveReady(t: number): Promise<void> {
|
||||
return Promise.all(this.linkLoadPromises)
|
||||
.then(() => this.moveApply(t, this.manageRule));
|
||||
}
|
||||
}
|
||||
|
|
@ -36,6 +36,8 @@ export const ID_TP_MAP = {
|
|||
54: "connection_information",
|
||||
55: "set_page_visibility",
|
||||
59: "long_task",
|
||||
69: "mouse_click",
|
||||
70: "create_i_frame_document",
|
||||
} as const;
|
||||
|
||||
|
||||
|
|
@ -255,11 +257,26 @@ export interface LongTask {
|
|||
containerName: string,
|
||||
}
|
||||
|
||||
export interface MouseClick {
|
||||
tp: "mouse_click",
|
||||
id: number,
|
||||
hesitationTime: number,
|
||||
label: string,
|
||||
selector: string,
|
||||
}
|
||||
|
||||
export type Message = Timestamp | SetPageLocation | SetViewportSize | SetViewportScroll | CreateDocument | CreateElementNode | CreateTextNode | MoveNode | RemoveNode | SetNodeAttribute | RemoveNodeAttribute | SetNodeData | SetCssData | SetNodeScroll | SetInputValue | SetInputChecked | MouseMove | ConsoleLog | CssInsertRule | CssDeleteRule | Fetch | Profiler | OTable | Redux | Vuex | MobX | NgRx | GraphQl | PerformanceTrack | ConnectionInformation | SetPageVisibility | LongTask;
|
||||
export interface CreateIFrameDocument {
|
||||
tp: "create_i_frame_document",
|
||||
frameID: number,
|
||||
id: number,
|
||||
}
|
||||
|
||||
|
||||
export type Message = Timestamp | SetPageLocation | SetViewportSize | SetViewportScroll | CreateDocument | CreateElementNode | CreateTextNode | MoveNode | RemoveNode | SetNodeAttribute | RemoveNodeAttribute | SetNodeData | SetCssData | SetNodeScroll | SetInputValue | SetInputChecked | MouseMove | ConsoleLog | CssInsertRule | CssDeleteRule | Fetch | Profiler | OTable | Redux | Vuex | MobX | NgRx | GraphQl | PerformanceTrack | ConnectionInformation | SetPageVisibility | LongTask | MouseClick | CreateIFrameDocument;
|
||||
|
||||
export default function (r: PrimitiveReader): Message | null {
|
||||
switch (r.readUint()) {
|
||||
const ui= r.readUint()
|
||||
switch (ui) {
|
||||
|
||||
case 0:
|
||||
return {
|
||||
|
|
@ -509,7 +526,24 @@ export default function (r: PrimitiveReader): Message | null {
|
|||
containerName: r.readString(),
|
||||
};
|
||||
|
||||
case 69:
|
||||
return {
|
||||
tp: ID_TP_MAP[69],
|
||||
id: r.readUint(),
|
||||
hesitationTime: r.readUint(),
|
||||
label: r.readString(),
|
||||
selector: r.readString(),
|
||||
};
|
||||
|
||||
case 70:
|
||||
return {
|
||||
tp: ID_TP_MAP[70],
|
||||
frameID: r.readUint(),
|
||||
id: r.readUint(),
|
||||
};
|
||||
|
||||
default:
|
||||
console.log("wtf is this", ui)
|
||||
r.readUint(); // IOS skip timestamp
|
||||
r.skip(r.readUint());
|
||||
return null;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue