From 2a926d038d9c051ec7310a000cb25b9c1c0e1a15 Mon Sep 17 00:00:00 2001 From: Alex Kaminskii Date: Fri, 16 Sep 2022 18:33:29 +0200 Subject: [PATCH] fix(frontend/player): PostponedStyleSheet callbacks & logs in tracker & accept virtual svg-style in tracker --- .../managers/DOM/DOMManager.ts | 26 ++++++++---- .../managers/DOM/VirtualDOM.ts | 41 +++++++++++++++++++ tracker/tracker/src/main/modules/cssrules.ts | 8 ++-- 3 files changed, 63 insertions(+), 12 deletions(-) diff --git a/frontend/app/player/MessageDistributor/managers/DOM/DOMManager.ts b/frontend/app/player/MessageDistributor/managers/DOM/DOMManager.ts index c15fc5682..39c1dab87 100644 --- a/frontend/app/player/MessageDistributor/managers/DOM/DOMManager.ts +++ b/frontend/app/player/MessageDistributor/managers/DOM/DOMManager.ts @@ -5,7 +5,15 @@ import type { Message, SetNodeScroll, CreateElementNode } from '../../messages'; import ListWalker from '../ListWalker'; import StylesManager, { rewriteNodeStyleSheet } from './StylesManager'; -import { VElement, VText, VShadowRoot, VDocument, VNode, VStyleElement } from './VirtualDOM'; +import { + VElement, + VText, + VShadowRoot, + VDocument, + VNode, + VStyleElement, + PostponedStyleSheet, +} from './VirtualDOM'; import type { StyleElement } from './VirtualDOM'; @@ -24,20 +32,21 @@ const ATTR_NAME_REGEXP = /([^\t\n\f \/>"'=]+)/; // regexp costs ~ // .replace(/\-webkit\-/g, "") // } -function insertRule(sheet: CSSStyleSheet, msg: { rule: string, index: number }) { +function insertRule(sheet: CSSStyleSheet | PostponedStyleSheet, msg: { rule: string, index: number }) { try { sheet.insertRule(msg.rule, msg.index) } catch (e) { logger.warn(e, msg) try { - sheet.insertRule(msg.rule) + sheet.insertRule(msg.rule, 0) + logger.warn("Inserting rule into 0-index", e, msg) } catch (e) { logger.warn("Cannot insert rule.", e, msg) } } } -function deleteRule(sheet: CSSStyleSheet, msg: { index: number }) { +function deleteRule(sheet: CSSStyleSheet | PostponedStyleSheet, msg: { index: number }) { try { sheet.deleteRule(msg.index) } catch (e) { @@ -51,6 +60,7 @@ export default class DOMManager extends ListWalker { private vRoots: Map = new Map() private activeIframeRoots: Map = new Map() private styleSheets: Map = new Map() + private ppStyleSheets: Map = new Map() private upperBodyId: number = -1; @@ -141,7 +151,7 @@ export default class DOMManager extends ListWalker { let node: Node | undefined let vn: VNode | undefined let doc: Document | null - let styleSheet: CSSStyleSheet | undefined + let styleSheet: CSSStyleSheet | PostponedStyleSheet | undefined switch (msg.tp) { case "create_document": doc = this.screen.document; @@ -321,7 +331,7 @@ export default class DOMManager extends ListWalker { } return case "adopted_ss_insert_rule": - styleSheet = this.styleSheets.get(msg.sheetID) + styleSheet = this.styleSheets.get(msg.sheetID) || this.ppStyleSheets.get(msg.sheetID) if (!styleSheet) { logger.warn("No stylesheet was created for ", msg) return @@ -329,7 +339,7 @@ export default class DOMManager extends ListWalker { insertRule(styleSheet, msg) return case "adopted_ss_delete_rule": - styleSheet = this.styleSheets.get(msg.sheetID) + styleSheet = this.styleSheets.get(msg.sheetID) || this.ppStyleSheets.get(msg.sheetID) if (!styleSheet) { logger.warn("No stylesheet was created for ", msg) return @@ -353,7 +363,7 @@ export default class DOMManager extends ListWalker { vn = this.vElements.get(msg.id) if (!vn) { logger.error("Node not found", msg); return } if (!(vn instanceof VStyleElement)) { logger.error("Non-style owner", msg); return } - this.styleSheets.set(msg.sheetID, vn.node.sheet) + this.ppStyleSheets.set(msg.sheetID, new PostponedStyleSheet(vn.node)) return } styleSheet = this.styleSheets.get(msg.sheetID) diff --git a/frontend/app/player/MessageDistributor/managers/DOM/VirtualDOM.ts b/frontend/app/player/MessageDistributor/managers/DOM/VirtualDOM.ts index e06647151..5efa2ddf1 100644 --- a/frontend/app/player/MessageDistributor/managers/DOM/VirtualDOM.ts +++ b/frontend/app/player/MessageDistributor/managers/DOM/VirtualDOM.ts @@ -119,6 +119,8 @@ export class VElement extends VParent { type StyleSheetCallback = (s: CSSStyleSheet) => void export type StyleElement = HTMLStyleElement | SVGStyleElement + +// @Depricated TODO: remove in favor of PostponedStyleSheet export class VStyleElement extends VElement { private loaded = false private stylesheetCallbacks: StyleSheetCallback[] = [] @@ -149,6 +151,45 @@ export class VStyleElement extends VElement { } } + +export class PostponedStyleSheet { + private loaded = false + private stylesheetCallbacks: StyleSheetCallback[] = [] + + constructor(private readonly node: StyleElement) { + node.onload = () => { + const sheet = node.sheet + if (sheet) { + this.stylesheetCallbacks.forEach(cb => cb(sheet)) + this.stylesheetCallbacks = [] + } else { + console.warn("Style node onload: sheet is null") + } + this.loaded = true + } + } + + private applyCallback(cb: StyleSheetCallback) { + if (this.loaded) { + if (!this.node.sheet) { + console.warn("Style tag is loaded, but sheet is null") + return + } + cb(this.node.sheet) + } else { + this.stylesheetCallbacks.push(cb) + } + } + + insertRule(rule: string, index: number) { + this.applyCallback(s => s.insertRule(rule, index)) + } + + deleteRule(index: number) { + this.applyCallback(s => s.deleteRule(index)) + } +} + export class VText { parentNode: VParent | null = null constructor(public readonly node: Text = new Text()) {} diff --git a/tracker/tracker/src/main/modules/cssrules.ts b/tracker/tracker/src/main/modules/cssrules.ts index 8e2217017..c4fce0fb7 100644 --- a/tracker/tracker/src/main/modules/cssrules.ts +++ b/tracker/tracker/src/main/modules/cssrules.ts @@ -20,8 +20,7 @@ export default function (app: App | null) { const sendInserDeleteRule = app.safe((sheet: CSSStyleSheet, index: number, rule?: string) => { const sheetID = styleSheetIDMap.get(sheet) if (!sheetID) { - app.debug.log('StyleSheet not registered ', sheet, sheet.ownerNode) - // Sheet haven't been registered yet. Rules will be sent on registration. + // OK-case. Sheet haven't been registered yet. Rules will be sent on registration. return } if (typeof rule === 'string') { @@ -53,6 +52,8 @@ export default function (app: App | null) { if (idx >= 0) { app.send(AdoptedSSInsertRuleURLBased(sheetID, cssText, idx, app.getBaseHref())) app.send(AdoptedSSDeleteRule(sheetID, idx + 1)) // Remove previous clone + } else { + app.debug.warn('Rule index not found in', sheet, topmostRule) } }) @@ -86,7 +87,7 @@ export default function (app: App | null) { app.observer.attachContextCallback(patchContext) app.nodes.attachNodeCallback((node: Node): void => { - if (!hasTag(node, 'STYLE') || !node.sheet) { + if (!(hasTag(node, 'STYLE') || hasTag(node, 'style')) || !node.sheet) { return } if (node.textContent !== null && node.textContent.trim().length > 0) { @@ -101,7 +102,6 @@ export default function (app: App | null) { const sheetID = nextID() styleSheetIDMap.set(sheet, sheetID) app.send(AdoptedSSAddOwner(sheetID, nodeID)) - app.debug.log('StyleSheet registered ', sheet, node) const rules = sheet.cssRules for (let i = 0; i < rules.length; i++) {