diff --git a/frontend/app/player/MessageDistributor/managers/DOM/DOMManager.ts b/frontend/app/player/MessageDistributor/managers/DOM/DOMManager.ts index 33169fa9c..0da4423d3 100644 --- a/frontend/app/player/MessageDistributor/managers/DOM/DOMManager.ts +++ b/frontend/app/player/MessageDistributor/managers/DOM/DOMManager.ts @@ -265,6 +265,8 @@ export default class DOMManager extends ListWalker { vn.applyChanges() } return + + // @depricated since 4.0.2 in favor of adopted_ss_insert/delete_rule + add_owner as being common case for StyleSheets case "css_insert_rule": vn = this.vElements.get(msg.id) if (!vn) { logger.error("Node not found", msg); return } @@ -283,6 +285,8 @@ export default class DOMManager extends ListWalker { } vn.onStyleSheet(sheet => deleteRule(sheet, msg)) return + // end @depricated + case "create_i_frame_document": vn = this.vElements.get(msg.frameID) if (!vn) { logger.error("Node not found", msg); return } @@ -326,18 +330,7 @@ export default class DOMManager extends ListWalker { } deleteRule(styleSheet, msg) return - case "replace_vcss": - styleSheet = this.styleSheets.get(msg.sheetID) - if (!styleSheet) { - logger.warn("No stylesheet was created for ", msg) - return - } - const toRemove = styleSheet.cssRules.length - for (let i = 0; i < toRemove; i++) { - styleSheet.deleteRule(i) - } - styleSheet.insertRule(msg.styles) - return + case "adopted_ss_replace": styleSheet = this.styleSheets.get(msg.sheetID) if (!styleSheet) { @@ -349,7 +342,14 @@ export default class DOMManager extends ListWalker { return case "adopted_ss_add_owner": vn = this.vRoots.get(msg.id) - if (!vn) { logger.error("Node not found", msg); return } + if (!vn) { + // non-constructed case + 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) + return + } styleSheet = this.styleSheets.get(msg.sheetID) if (!styleSheet) { let context: typeof globalThis diff --git a/mobs/messages.rb b/mobs/messages.rb index a3a7111de..6d21c2e05 100644 --- a/mobs/messages.rb +++ b/mobs/messages.rb @@ -238,15 +238,18 @@ message 36, 'CustomEvent', :tracker => false, :replayer => false do string 'Name' string 'Payload' end +# depricated since 4.0.2 in favor of AdoptedSSInsertRule + AdoptedSSAddOwner message 37, 'CSSInsertRule' do uint 'ID' string 'Rule' uint 'Index' end +# depricated since 4.0.2 message 38, 'CSSDeleteRule' do uint 'ID' uint 'Index' end + message 39, 'Fetch' do string 'Method' string 'URL' @@ -421,6 +424,7 @@ message 70, 'CreateIFrameDocument' do end #Since 4.0.0 AdoptedStyleSheets etc +# TODO: rename to StyleSheets... message 71, 'AdoptedSSReplaceURLBased' do uint 'SheetID' string 'Text' diff --git a/tracker/tracker/src/main/app/observer/observer.ts b/tracker/tracker/src/main/app/observer/observer.ts index de489d835..f2e218e27 100644 --- a/tracker/tracker/src/main/app/observer/observer.ts +++ b/tracker/tracker/src/main/app/observer/observer.ts @@ -291,6 +291,7 @@ export default abstract class Observer { const width = el.clientWidth const height = el.clientHeight el = node.cloneNode() as Element + // TODO: use ResizeObserver ;(el as HTMLElement | SVGElement).style.width = width + 'px' ;(el as HTMLElement | SVGElement).style.height = height + 'px' } diff --git a/tracker/tracker/src/main/index.ts b/tracker/tracker/src/main/index.ts index ff8d27b4c..7025a5301 100644 --- a/tracker/tracker/src/main/index.ts +++ b/tracker/tracker/src/main/index.ts @@ -19,7 +19,7 @@ import Performance from './modules/performance.js' import Scroll from './modules/scroll.js' import Viewport from './modules/viewport.js' import CSSRules from './modules/cssrules.js' -import AdoptedStyleSheets from './modules/adoptedStyleSheets.js' +import ConstructedStyleSheets from './modules/constructedStyleSheets.js' import { IN_BROWSER, deprecationWarn, DOCS_HOST } from './utils.js' import type { Options as AppOptions } from './app/index.js' @@ -112,7 +112,7 @@ export default class API { if (app !== null) { Viewport(app) CSSRules(app) - AdoptedStyleSheets(app) + ConstructedStyleSheets(app) Connection(app) Console(app, options) Exception(app, options) diff --git a/tracker/tracker/src/main/modules/adoptedStyleSheets.ts b/tracker/tracker/src/main/modules/constructedStyleSheets.ts similarity index 95% rename from tracker/tracker/src/main/modules/adoptedStyleSheets.ts rename to tracker/tracker/src/main/modules/constructedStyleSheets.ts index 538951f56..acc662a64 100644 --- a/tracker/tracker/src/main/modules/adoptedStyleSheets.ts +++ b/tracker/tracker/src/main/modules/constructedStyleSheets.ts @@ -19,6 +19,13 @@ function hasAdoptedSS(node: Node): node is StyleSheetOwner { ) } +// TODO: incapsulate to be init-ed on-start and join with cssrules.ts under one folder +let _id = 0xf +export function nextID(): number { + return _id++ +} +export const styleSheetIDMap: Map = new Map() + export default function (app: App | null) { if (app === null) { return @@ -31,7 +38,6 @@ export default function (app: App | null) { return } - let nextID = 0xf const styleSheetIDMap: Map = new Map() const adoptedStyleSheetsOwnings: Map = new Map() @@ -53,7 +59,7 @@ export default function (app: App | null) { let sheetID = styleSheetIDMap.get(s) const init = !sheetID if (!sheetID) { - sheetID = ++nextID + sheetID = nextID() } nowOwning.push(sheetID) if (!pastOwning.includes(sheetID)) { diff --git a/tracker/tracker/src/main/modules/cssrules.ts b/tracker/tracker/src/main/modules/cssrules.ts index 159f9c25d..10a072b4e 100644 --- a/tracker/tracker/src/main/modules/cssrules.ts +++ b/tracker/tracker/src/main/modules/cssrules.ts @@ -1,6 +1,12 @@ import type App from '../app/index.js' -import { CSSInsertRuleURLBased, CSSDeleteRule, TechnicalInfo } from '../app/messages.gen.js' +import { + AdoptedSSInsertRuleURLBased, // TODO: rename to common StyleSheet names + AdoptedSSDeleteRule, + AdoptedSSAddOwner, + TechnicalInfo, +} from '../app/messages.gen.js' import { hasTag } from '../app/guards.js' +import { nextID, styleSheetIDMap } from './constructedStyleSheets.js' export default function (app: App | null) { if (app === null) { @@ -11,41 +17,43 @@ export default function (app: App | null) { return } - const processOperation = app.safe((stylesheet: CSSStyleSheet, index: number, rule?: string) => { - const sendMessage = - typeof rule === 'string' - ? (nodeID: number) => - app.send(CSSInsertRuleURLBased(nodeID, rule, index, app.getBaseHref())) - : (nodeID: number) => app.send(CSSDeleteRule(nodeID, index)) - // TODO: Extend messages to maintain nested rules (CSSGroupingRule prototype, as well as CSSKeyframesRule) - if (!stylesheet.ownerNode) { - throw new Error('Owner Node not found') - } - const nodeID = app.nodes.getID(stylesheet.ownerNode) - if (nodeID !== undefined) { - sendMessage(nodeID) - } // else error? - }) + const sendInserDeleteRule = app.safe( + (stylesheet: CSSStyleSheet, index: number, rule?: string) => { + const sheetID = styleSheetIDMap.get(stylesheet) + if (!sheetID) { + app.debug.warn('No sheedID found', stylesheet, styleSheetIDMap) + return + } + if (typeof rule === 'string') { + app.send(AdoptedSSInsertRuleURLBased(sheetID, rule, index, app.getBaseHref())) + } else { + app.send(AdoptedSSDeleteRule(sheetID, index)) + } + }, + ) - const replaceGroupingRule = app.safe((ctx: CSSGroupingRule) => { - let topmostRule: CSSRule = ctx + // TODO: proper rule insertion/removal (how?) + const sendReplaceGroupingRule = app.safe((rule: CSSGroupingRule) => { + let topmostRule: CSSRule = rule while (topmostRule.parentRule) { topmostRule = topmostRule.parentRule } - if (topmostRule.parentStyleSheet?.ownerNode) { - const entireStyle = topmostRule.cssText - const nodeID = app.nodes.getID(topmostRule.parentStyleSheet.ownerNode) - if (nodeID === undefined) { - return - } - const ruleList = topmostRule.parentStyleSheet.cssRules - const idx = Array.from(ruleList).indexOf(topmostRule) - if (idx >= 0) { - app.send(CSSDeleteRule(nodeID, idx)) - app.send(CSSInsertRuleURLBased(nodeID, entireStyle, idx, app.getBaseHref())) - } - } else { - app.debug.error('Owner Node not found for the rule', topmostRule) + const sheet = topmostRule.parentStyleSheet + if (!sheet) { + app.debug.warn('No parent StyleSheet found for', topmostRule, rule) + return + } + const sheetID = styleSheetIDMap.get(sheet) + if (!sheetID) { + app.debug.warn('No sheedID found for', sheet, styleSheetIDMap) + return + } + const cssText = topmostRule.cssText + const ruleList = sheet.cssRules + const idx = Array.from(ruleList).indexOf(topmostRule) + if (idx >= 0) { + app.send(AdoptedSSInsertRuleURLBased(sheetID, cssText, idx, app.getBaseHref())) + app.send(AdoptedSSDeleteRule(sheetID, idx + 1)) // Remove previous clone } }) @@ -55,22 +63,22 @@ export default function (app: App | null) { context.CSSGroupingRule.prototype context.CSSStyleSheet.prototype.insertRule = function (rule: string, index = 0) { - processOperation(this, index, rule) + sendInserDeleteRule(this, index, rule) return insertRule.call(this, rule, index) as number } context.CSSStyleSheet.prototype.deleteRule = function (index: number) { - processOperation(this, index) + sendInserDeleteRule(this, index) return deleteRule.call(this, index) as number } context.CSSGroupingRule.prototype.insertRule = function (rule: string, index = 0) { const result = groupInsertRule.call(this, rule, index) as number - replaceGroupingRule(this) + sendReplaceGroupingRule(this) return result } context.CSSGroupingRule.prototype.deleteRule = function (index = 0) { const result = groupDeleteRule.call(this, index) as number - replaceGroupingRule(this) + sendReplaceGroupingRule(this) return result } } @@ -83,11 +91,21 @@ export default function (app: App | null) { return } if (node.textContent !== null && node.textContent.trim().length > 0) { - return // Only fully virtual sheets maintained so far + return // Non-virtual styles captured by the observer as a text } - const rules = node.sheet.cssRules + + const nodeID = app.nodes.getID(node) + if (!nodeID) { + return + } + const sheet = node.sheet + const sheetID = nextID() + styleSheetIDMap.set(sheet, sheetID) + app.send(AdoptedSSAddOwner(sheetID, nodeID)) + + const rules = sheet.cssRules for (let i = 0; i < rules.length; i++) { - processOperation(node.sheet, i, rules[i].cssText) + sendInserDeleteRule(sheet, i, rules[i].cssText) } }) }