From e2c4e048ea8540ec19922a649626608268d6dff4 Mon Sep 17 00:00:00 2001 From: Alex Kaminskii Date: Sat, 1 Apr 2023 00:43:35 +0200 Subject: [PATCH] fix(player): fix and centralize CSS pseudoclasses rewriting --- .../app/player/web/managers/DOM/DOMManager.ts | 7 +--- .../player/web/managers/DOM/FocusManager.ts | 7 ++-- .../player/web/managers/DOM/StylesManager.ts | 14 +++---- .../player/web/managers/MouseMoveManager.ts | 14 ++----- .../web/messages/JSONRawMessageReader.ts | 4 +- .../app/player/web/messages/MFileReader.ts | 4 +- .../player/web/messages/rewriter/constants.ts | 2 + .../rewriteMessage.ts} | 41 +++++++++++-------- .../web/messages/{ => rewriter}/urlResolve.ts | 0 9 files changed, 44 insertions(+), 49 deletions(-) create mode 100644 frontend/app/player/web/messages/rewriter/constants.ts rename frontend/app/player/web/messages/{urlBasedResolver.ts => rewriter/rewriteMessage.ts} (55%) rename frontend/app/player/web/messages/{ => rewriter}/urlResolve.ts (100%) diff --git a/frontend/app/player/web/managers/DOM/DOMManager.ts b/frontend/app/player/web/managers/DOM/DOMManager.ts index 5ae59c5c4..8e0fb8b6b 100644 --- a/frontend/app/player/web/managers/DOM/DOMManager.ts +++ b/frontend/app/player/web/managers/DOM/DOMManager.ts @@ -4,7 +4,7 @@ import type Screen from '../../Screen/Screen'; import type { Message, SetNodeScroll } from '../../messages'; import { MType } from '../../messages'; import ListWalker from '../../../common/ListWalker'; -import StylesManager, { rewriteNodeStyleSheet } from './StylesManager'; +import StylesManager from './StylesManager'; import FocusManager from './FocusManager'; import SelectionManager from './SelectionManager'; import type { StyleElement } from './VirtualDOM'; @@ -289,11 +289,6 @@ export default class DOMManager extends ListWalker { vn = this.vTexts.get(msg.id) if (!vn) { logger.error("SetCssData: Node not found", msg); return } vn.setData(msg.data) - if (vn.node instanceof HTMLStyleElement) { - doc = this.screen.document - // TODO: move to message parsing - doc && rewriteNodeStyleSheet(doc, vn.node) - } if (msg.tp === MType.SetCssData) { // Styles in priority (do we need inlines as well?) vn.applyChanges() } diff --git a/frontend/app/player/web/managers/DOM/FocusManager.ts b/frontend/app/player/web/managers/DOM/FocusManager.ts index c75f3ddc3..5a808766c 100644 --- a/frontend/app/player/web/managers/DOM/FocusManager.ts +++ b/frontend/app/player/web/managers/DOM/FocusManager.ts @@ -2,8 +2,7 @@ import logger from 'App/logger'; import type { SetNodeFocus } from '../../messages'; import type { VElement } from './VirtualDOM'; import ListWalker from '../../../common/ListWalker'; - -const FOCUS_CLASS = "-openreplay-focus" +import { FOCUS_CLASSNAME } from '../../messages/rewriter/constants' export default class FocusManager extends ListWalker { constructor(private readonly vElements: Map) {super()} @@ -11,7 +10,7 @@ export default class FocusManager extends ListWalker { move(t: number) { const msg = this.moveGetLast(t) if (!msg) {return} - this.focused?.classList.remove(FOCUS_CLASS) + this.focused?.classList.remove(FOCUS_CLASSNAME) if (msg.id === -1) { this.focused = null return @@ -19,7 +18,7 @@ export default class FocusManager extends ListWalker { const vn = this.vElements.get(msg.id) if (!vn) { logger.error("Node not found", msg); return } this.focused = vn.node - this.focused.classList.add(FOCUS_CLASS) + this.focused.classList.add(FOCUS_CLASSNAME) } } \ No newline at end of file diff --git a/frontend/app/player/web/managers/DOM/StylesManager.ts b/frontend/app/player/web/managers/DOM/StylesManager.ts index 295b95d2f..92f51aed9 100644 --- a/frontend/app/player/web/managers/DOM/StylesManager.ts +++ b/frontend/app/player/web/managers/DOM/StylesManager.ts @@ -1,19 +1,18 @@ import type Screen from '../../Screen/Screen'; import type { CssInsertRule, CssDeleteRule } from '../../messages'; +import { replaceCSSPseudoclasses } from '../../messages/rewriter/rewriteMessage' type CSSRuleMessage = CssInsertRule | CssDeleteRule; -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) { +// Doesn't work with css files (hasOwnProperty returns false) +// TODO: recheck and remove if true +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) + r.selectorText = replaceCSSPseudoclasses(r.selectorText) } } } @@ -29,7 +28,7 @@ export default class StylesManager { this.linkLoadingCount = 0; this.linkLoadPromises = []; - //cancel all promises? tothinkaboutit + //cancel all promises? thinkaboutit } setStyleHandlers(node: HTMLLinkElement, value: string): void { @@ -44,6 +43,7 @@ export default class StylesManager { } timeoutId = setTimeout(addSkipAndResolve, 4000); + // It would be better to make it more relyable with addEventListener node.onload = () => { const doc = this.screen.document; doc && rewriteNodeStyleSheet(doc, node); diff --git a/frontend/app/player/web/managers/MouseMoveManager.ts b/frontend/app/player/web/managers/MouseMoveManager.ts index fa320ab13..2cb508f59 100644 --- a/frontend/app/player/web/managers/MouseMoveManager.ts +++ b/frontend/app/player/web/managers/MouseMoveManager.ts @@ -1,20 +1,14 @@ import type Screen from '../Screen/Screen' import type { MouseMove } from "../messages"; - +import { HOVER_CLASSNAME } from '../messages/rewriter/constants' import ListWalker from '../../common/ListWalker' -const HOVER_CLASS = "-openreplay-hover"; -const HOVER_CLASS_DEPR = "-asayer-hover"; export default class MouseMoveManager extends ListWalker { private hoverElements: Array = [] constructor(private screen: Screen) {super()} - // private getCursorTarget() { - // return this.screen.getElementFromInternalPoint(this.current) - // } - private getCursorTargets() { return this.screen.getElementsFromInternalPoint(this.current) } @@ -25,12 +19,10 @@ export default class MouseMoveManager extends ListWalker { const diffRemove = this.hoverElements.filter(elem => !curHoverElements.includes(elem)) this.hoverElements = curHoverElements diffAdd.forEach(elem => { - elem.classList.add(HOVER_CLASS) - elem.classList.add(HOVER_CLASS_DEPR) + elem.classList.add(HOVER_CLASSNAME) }) diffRemove.forEach(elem => { - elem.classList.remove(HOVER_CLASS) - elem.classList.remove(HOVER_CLASS_DEPR) + elem.classList.remove(HOVER_CLASSNAME) }) } diff --git a/frontend/app/player/web/messages/JSONRawMessageReader.ts b/frontend/app/player/web/messages/JSONRawMessageReader.ts index ca193c326..c2499f4c3 100644 --- a/frontend/app/player/web/messages/JSONRawMessageReader.ts +++ b/frontend/app/player/web/messages/JSONRawMessageReader.ts @@ -2,7 +2,7 @@ import type { RawMessage } from './raw.gen' import type { TrackerMessage } from './tracker.gen' import translate from './tracker.gen' import { TP_MAP } from './tracker-legacy.gen' -import resolveURL from './urlBasedResolver' +import rewriteMessage from './rewriter/rewriteMessage' function legacyTranslate(msg: any): RawMessage | null { @@ -30,7 +30,7 @@ export default class JSONRawMessageReader { if (!rawMsg) { return this.readMessage() } - return resolveURL(rawMsg) + return rewriteMessage(rawMsg) } } diff --git a/frontend/app/player/web/messages/MFileReader.ts b/frontend/app/player/web/messages/MFileReader.ts index b5fdde85c..a7411b155 100644 --- a/frontend/app/player/web/messages/MFileReader.ts +++ b/frontend/app/player/web/messages/MFileReader.ts @@ -2,7 +2,7 @@ import type { Message } from './message.gen'; import type { RawMessage } from './raw.gen'; import { MType } from './raw.gen'; import RawMessageReader from './RawMessageReader.gen'; -import resolveURL from './urlBasedResolver' +import rewriteMessage from './rewriter/rewriteMessage' import Logger from 'App/logger' // TODO: composition instead of inheritance @@ -77,7 +77,7 @@ export default class MFileReader extends RawMessageReader { } const index = this.getLastMessageID() - const msg = Object.assign(resolveURL(rMsg), { + const msg = Object.assign(rewriteMessage(rMsg), { time: this.currentTime, _index: index, }) diff --git a/frontend/app/player/web/messages/rewriter/constants.ts b/frontend/app/player/web/messages/rewriter/constants.ts new file mode 100644 index 000000000..abd05897a --- /dev/null +++ b/frontend/app/player/web/messages/rewriter/constants.ts @@ -0,0 +1,2 @@ +export const HOVER_CLASSNAME = "-openreplay-hover" +export const FOCUS_CLASSNAME = "-openreplay-focus" diff --git a/frontend/app/player/web/messages/urlBasedResolver.ts b/frontend/app/player/web/messages/rewriter/rewriteMessage.ts similarity index 55% rename from frontend/app/player/web/messages/urlBasedResolver.ts rename to frontend/app/player/web/messages/rewriter/rewriteMessage.ts index 53bf1ed81..d488c8362 100644 --- a/frontend/app/player/web/messages/urlBasedResolver.ts +++ b/frontend/app/player/web/messages/rewriter/rewriteMessage.ts @@ -10,24 +10,31 @@ import type { RawAdoptedSsInsertRule, RawAdoptedSsReplaceURLBased, RawAdoptedSsReplace, -} from './raw.gen' -import { MType } from './raw.gen' +} from '../raw.gen' +import { MType } from '../raw.gen' import { resolveURL, resolveCSS } from './urlResolve' +import { HOVER_CLASSNAME, FOCUS_CLASSNAME } from './constants' -// type PickMessage = Extract; -// type ResolversMap = { -// [Key in MType]: (event: PickMessage) => RawMessage -// } +const HOVER_SELECTOR = `.${HOVER_CLASSNAME}` +const FOCUS_SELECTOR = `.${FOCUS_CLASSNAME}` +export function replaceCSSPseudoclasses(cssText: string): string { + return cssText + .replace('/\:hover/g', HOVER_SELECTOR) + .replace('/\:focus/g', FOCUS_SELECTOR) +} +function rewriteCSS(baseURL: string, cssText: string): string { + return replaceCSSPseudoclasses(resolveCSS(baseURL, cssText)) +} -// TODO: commonURLBased logic for feilds -const resolvers = { +// TODO: common logic for URL fields in all the ...URLBased messages +const REWRITERS = { [MType.SetNodeAttributeURLBased]: (msg: RawSetNodeAttributeURLBased): RawSetNodeAttribute => ({ ...msg, value: msg.name === 'src' || msg.name === 'href' ? resolveURL(msg.baseURL, msg.value) : (msg.name === 'style' - ? resolveCSS(msg.baseURL, msg.value) + ? rewriteCSS(msg.baseURL, msg.value) : msg.value ), tp: MType.SetNodeAttribute, @@ -35,35 +42,35 @@ const resolvers = { [MType.SetCssDataURLBased]: (msg: RawSetCssDataURLBased): RawSetCssData => ({ ...msg, - data: resolveCSS(msg.baseURL, msg.data), + data: rewriteCSS(msg.baseURL, msg.data), tp: MType.SetCssData, }), [MType.CssInsertRuleURLBased]: (msg: RawCssInsertRuleURLBased): RawCssInsertRule => ({ ...msg, - rule: resolveCSS(msg.baseURL, msg.rule), + rule: rewriteCSS(msg.baseURL, msg.rule), tp: MType.CssInsertRule, }), [MType.AdoptedSsInsertRuleURLBased]: (msg: RawAdoptedSsInsertRuleURLBased): RawAdoptedSsInsertRule => ({ ...msg, - rule: resolveCSS(msg.baseURL, msg.rule), + rule: rewriteCSS(msg.baseURL, msg.rule), tp: MType.AdoptedSsInsertRule, }), [MType.AdoptedSsReplaceURLBased]: (msg: RawAdoptedSsReplaceURLBased): RawAdoptedSsReplace => ({ ...msg, - text: resolveCSS(msg.baseURL, msg.text), + text: rewriteCSS(msg.baseURL, msg.text), tp: MType.AdoptedSsReplace, }), } as const -export default function resolve(msg: RawMessage): RawMessage { - // @ts-ignore --- any idea? - if (resolvers[msg.tp]) { +export default function rewriteMessage(msg: RawMessage): RawMessage { + // @ts-ignore --- any idea for correct typing? + if (REWRITERS[msg.tp]) { // @ts-ignore - return resolvers[msg.tp](msg) + return REWRITERS[msg.tp](msg) } return msg } \ No newline at end of file diff --git a/frontend/app/player/web/messages/urlResolve.ts b/frontend/app/player/web/messages/rewriter/urlResolve.ts similarity index 100% rename from frontend/app/player/web/messages/urlResolve.ts rename to frontend/app/player/web/messages/rewriter/urlResolve.ts