From e57d90e5a1439a5d36f13eb98954dabf258fa680 Mon Sep 17 00:00:00 2001 From: Alex Kaminskii Date: Fri, 3 Jun 2022 14:17:53 +0200 Subject: [PATCH] fix(tracker): use node guards instead of instanceof in some cases; import type App --- tracker/tracker/src/main/app/guards.ts | 32 +++++++++++++++++++ tracker/tracker/src/main/app/nodes.ts | 2 +- tracker/tracker/src/main/app/sanitizer.ts | 6 ++-- tracker/tracker/src/main/modules/console.ts | 5 +-- tracker/tracker/src/main/modules/cssrules.ts | 9 +++--- tracker/tracker/src/main/modules/exception.ts | 2 +- tracker/tracker/src/main/modules/img.ts | 6 ++-- tracker/tracker/src/main/modules/input.ts | 15 +++++---- tracker/tracker/src/main/modules/longtasks.ts | 2 +- tracker/tracker/src/main/modules/mouse.ts | 7 ++-- .../tracker/src/main/modules/performance.ts | 2 +- tracker/tracker/src/main/modules/scroll.ts | 13 ++++---- tracker/tracker/src/main/modules/timing.ts | 7 ++-- tracker/tracker/src/main/modules/viewport.ts | 2 +- 14 files changed, 74 insertions(+), 36 deletions(-) create mode 100644 tracker/tracker/src/main/app/guards.ts diff --git a/tracker/tracker/src/main/app/guards.ts b/tracker/tracker/src/main/app/guards.ts new file mode 100644 index 000000000..270ff52dd --- /dev/null +++ b/tracker/tracker/src/main/app/guards.ts @@ -0,0 +1,32 @@ +export function isSVGElement(node: Element): node is SVGElement { + return node.namespaceURI === 'http://www.w3.org/2000/svg'; +} + +export function isElementNode(node: Node): node is Element { + return node.nodeType === Node.ELEMENT_NODE +} + +export function isTextNode(node: Node): node is Text { + return node.nodeType === Node.TEXT_NODE +} + +export function isRootNode(node: Node): boolean { + return node.nodeType === Node.DOCUMENT_NODE || + node.nodeType === Node.DOCUMENT_FRAGMENT_NODE +} + + +type TagTypeMap = { + HTML: HTMLHtmlElement + IMG: HTMLImageElement + INPUT: HTMLInputElement + TEXTAREA: HTMLTextAreaElement + SELECT: HTMLSelectElement + LABEL: HTMLLabelElement + IFRAME: HTMLIFrameElement + STYLE: HTMLStyleElement | SVGStyleElement + LINK: HTMLLinkElement +} +export function hasTag(el: Node, tagName: T): el is TagTypeMap[typeof tagName] { + return el.nodeName.toUpperCase() === tagName +} diff --git a/tracker/tracker/src/main/app/nodes.ts b/tracker/tracker/src/main/app/nodes.ts index a4f19f4e0..73ba4f311 100644 --- a/tracker/tracker/src/main/app/nodes.ts +++ b/tracker/tracker/src/main/app/nodes.ts @@ -32,7 +32,7 @@ export default class Nodes { listeners.push([type, elementListener]); } - registerNode(node: Node): [number, boolean] { + registerNode(node: Node): [id: number, isNew: boolean] { let id: number = (node as any)[this.node_id]; const isNew = id === undefined; if (isNew) { diff --git a/tracker/tracker/src/main/app/sanitizer.ts b/tracker/tracker/src/main/app/sanitizer.ts index d085b5739..3e2e275ac 100644 --- a/tracker/tracker/src/main/app/sanitizer.ts +++ b/tracker/tracker/src/main/app/sanitizer.ts @@ -1,6 +1,6 @@ +import type App from "./index.js"; import { stars, hasOpenreplayAttribute } from "../utils.js"; -import App from "./index.js"; -import { isInstance } from "./context.js"; +import { isElementNode } from "./guards.js"; export interface Options { obscureTextEmails: boolean; @@ -21,7 +21,7 @@ export default class Sanitizer { handleNode(id: number, parentID: number, node: Node) { if ( this.masked.has(parentID) || - (isInstance(node, Element) && hasOpenreplayAttribute(node, 'masked')) + (isElementNode(node) && hasOpenreplayAttribute(node, 'masked')) ) { this.masked.add(id); } diff --git a/tracker/tracker/src/main/modules/console.ts b/tracker/tracker/src/main/modules/console.ts index b6e95d14f..40925c83c 100644 --- a/tracker/tracker/src/main/modules/console.ts +++ b/tracker/tracker/src/main/modules/console.ts @@ -1,4 +1,5 @@ -import App from "../app/index.js"; +import type App from "../app/index.js"; +import { hasTag } from "../app/guards.js"; import { IN_BROWSER } from "../utils.js"; import { ConsoleLog } from "../../common/messages.js"; @@ -139,7 +140,7 @@ export default function (app: App, opts: Partial): void { patchConsole(window.console); app.nodes.attachNodeCallback(app.safe(node => { - if (node instanceof HTMLIFrameElement) { + if (hasTag(node, "IFRAME")) { // TODO: newContextCallback let context = node.contentWindow if (context) { patchConsole((context as (Window & typeof globalThis)).console) diff --git a/tracker/tracker/src/main/modules/cssrules.ts b/tracker/tracker/src/main/modules/cssrules.ts index 8c75a5366..5007bbd26 100644 --- a/tracker/tracker/src/main/modules/cssrules.ts +++ b/tracker/tracker/src/main/modules/cssrules.ts @@ -1,5 +1,7 @@ -import App from "../app/index.js"; +import type App from "../app/index.js"; import { CSSInsertRuleURLBased, CSSDeleteRule, TechnicalInfo } from "../../common/messages.js"; +import { hasTag } from "../app/guards.js"; + export default function(app: App | null) { if (app === null) { @@ -41,10 +43,7 @@ export default function(app: App | null) { }; app.nodes.attachNodeCallback((node: Node): void => { - if (!(node instanceof HTMLStyleElement)) { - return; - } - if (!(node.sheet instanceof CSSStyleSheet)) { + if (!hasTag(node, "STYLE") || !node.sheet) { return; } if (node.textContent !== null && node.textContent.trim().length > 0) { diff --git a/tracker/tracker/src/main/modules/exception.ts b/tracker/tracker/src/main/modules/exception.ts index be02ca291..03dc84d72 100644 --- a/tracker/tracker/src/main/modules/exception.ts +++ b/tracker/tracker/src/main/modules/exception.ts @@ -1,5 +1,5 @@ +import type App from "../app/index.js"; import type Message from "../../common/messages.js"; -import App from "../app/index.js"; import { JSException } from "../../common/messages.js"; import ErrorStackParser from 'error-stack-parser'; diff --git a/tracker/tracker/src/main/modules/img.ts b/tracker/tracker/src/main/modules/img.ts index da260c414..b5acc6135 100644 --- a/tracker/tracker/src/main/modules/img.ts +++ b/tracker/tracker/src/main/modules/img.ts @@ -1,6 +1,8 @@ +import type App from "../app/index.js"; import { timestamp, isURL } from "../utils.js"; -import App from "../app/index.js"; import { ResourceTiming, SetNodeAttributeURLBased, SetNodeAttribute } from "../../common/messages.js"; +import { hasTag } from "../app/guards.js"; + const PLACEHOLDER_SRC = "https://static.openreplay.com/tracker/placeholder.jpeg"; @@ -51,7 +53,7 @@ export default function (app: App): void { }); app.nodes.attachNodeCallback((node: Node): void => { - if (!(node instanceof HTMLImageElement)) { + if (!hasTag(node, "IMG")) { return; } app.nodes.attachElementListener('error', node, sendImgSrc); diff --git a/tracker/tracker/src/main/modules/input.ts b/tracker/tracker/src/main/modules/input.ts index 546204730..2fb79d2f9 100644 --- a/tracker/tracker/src/main/modules/input.ts +++ b/tracker/tracker/src/main/modules/input.ts @@ -1,19 +1,20 @@ +import type App from "../app/index.js"; import { normSpaces, IN_BROWSER, getLabelAttribute, hasOpenreplayAttribute, } from "../utils.js"; -import App from "../app/index.js"; +import { hasTag } from "../app/guards.js"; import { SetInputTarget, SetInputValue, SetInputChecked } from "../../common/messages.js"; // TODO: take into consideration "contenteditable" attribute type TextEditableElement = HTMLInputElement | HTMLTextAreaElement function isTextEditable(node: any): node is TextEditableElement { - if (node instanceof HTMLTextAreaElement) { + if (hasTag(node, "TEXTAREA")) { return true; } - if (!(node instanceof HTMLInputElement)) { + if (!hasTag(node, "INPUT")) { return false; } const type = node.type; @@ -28,7 +29,7 @@ function isTextEditable(node: any): node is TextEditableElement { } function isCheckable(node: any): node is HTMLInputElement { - if (!(node instanceof HTMLInputElement)) { + if (!hasTag(node, "INPUT")) { return false; } const type = node.type; @@ -42,7 +43,7 @@ const labelElementFor: ( ? (node) => { let p: Node | null = node; while ((p = p.parentNode) !== null) { - if (p instanceof HTMLLabelElement) { + if (hasTag(p, "LABEL")) { return p } } @@ -54,7 +55,7 @@ const labelElementFor: ( : (node) => { let p: Node | null = node; while ((p = p.parentNode) !== null) { - if (p instanceof HTMLLabelElement) { + if (hasTag(p, "LABEL")) { return p as HTMLLabelElement; } } @@ -183,7 +184,7 @@ export default function (app: App, opts: Partial): void { return; } // TODO: support multiple select (?): use selectedOptions; Need send target? - if (node instanceof HTMLSelectElement) { + if (hasTag(node, "SELECT")) { sendInputValue(id, node) app.attachEventListener(node, "change", () => { sendInputValue(id, node) diff --git a/tracker/tracker/src/main/modules/longtasks.ts b/tracker/tracker/src/main/modules/longtasks.ts index 589c73de2..959d748f7 100644 --- a/tracker/tracker/src/main/modules/longtasks.ts +++ b/tracker/tracker/src/main/modules/longtasks.ts @@ -1,4 +1,4 @@ -import App from "../app/index.js"; +import type App from "../app/index.js"; import { LongTask } from "../../common/messages.js"; // https://w3c.github.io/performance-timeline/#the-performanceentry-interface diff --git a/tracker/tracker/src/main/modules/mouse.ts b/tracker/tracker/src/main/modules/mouse.ts index 956108963..09a06e0ca 100644 --- a/tracker/tracker/src/main/modules/mouse.ts +++ b/tracker/tracker/src/main/modules/mouse.ts @@ -1,9 +1,10 @@ +import type App from "../app/index.js"; +import { hasTag, isSVGElement } from "../app/guards.js"; import { normSpaces, hasOpenreplayAttribute, getLabelAttribute, } from "../utils.js"; -import App from "../app/index.js"; import { MouseMove, MouseClick } from "../../common/messages.js"; import { getInputLabel } from "./input.js"; @@ -56,7 +57,7 @@ function _getTarget(target: Element): Element | null { } element = element.parentElement; } - if (target instanceof SVGElement) { + if (isSVGElement(target)) { let owner = target.ownerSVGElement; while (owner !== null) { target = owner; @@ -89,7 +90,7 @@ export default function (app: App): void { if (dl !== null) { return dl; } - if (target instanceof HTMLInputElement) { + if (hasTag(target, "INPUT")) { return getInputLabel(target) } if (isClickable(target)) { diff --git a/tracker/tracker/src/main/modules/performance.ts b/tracker/tracker/src/main/modules/performance.ts index c7d911c9f..c69293225 100644 --- a/tracker/tracker/src/main/modules/performance.ts +++ b/tracker/tracker/src/main/modules/performance.ts @@ -1,4 +1,4 @@ -import App from "../app/index.js"; +import type App from "../app/index.js"; import { IN_BROWSER } from "../utils.js"; import { PerformanceTrack } from "../../common/messages.js"; diff --git a/tracker/tracker/src/main/modules/scroll.ts b/tracker/tracker/src/main/modules/scroll.ts index 9d19ccc24..1290106c4 100644 --- a/tracker/tracker/src/main/modules/scroll.ts +++ b/tracker/tracker/src/main/modules/scroll.ts @@ -1,5 +1,6 @@ -import App from "../app/index.js"; +import type App from "../app/index.js"; import { SetViewportScroll, SetNodeScroll } from "../../common/messages.js"; +import { isElementNode } from "../app/guards.js"; export default function (app: App): void { let documentScroll = false; @@ -34,11 +35,11 @@ export default function (app: App): void { nodeScroll.clear(); }); - app.nodes.attachNodeCallback(node => { - if (node instanceof Element && node.scrollLeft + node.scrollTop > 0) { - nodeScroll.set(node, [node.scrollLeft, node.scrollTop]); - } - }) + // app.nodes.attachNodeCallback(node => { + // if (isElementNode(node) && node.scrollLeft + node.scrollTop > 0) { + // nodeScroll.set(node, [node.scrollLeft, node.scrollTop]); + // } + // }) app.attachEventListener(window, 'scroll', (e: Event): void => { const target = e.target; diff --git a/tracker/tracker/src/main/modules/timing.ts b/tracker/tracker/src/main/modules/timing.ts index 01fbd1d0a..30183dfa6 100644 --- a/tracker/tracker/src/main/modules/timing.ts +++ b/tracker/tracker/src/main/modules/timing.ts @@ -1,8 +1,9 @@ -import type Message from "../../common/messages.js"; +import type App from "../app/index.js"; +import { hasTag } from "../app/guards.js"; import { isURL } from "../utils.js"; -import App from "../app/index.js"; import { ResourceTiming, PageLoadTiming, PageRenderTiming } from "../../common/messages.js"; + // Inspired by https://github.com/WPO-Foundation/RUM-SpeedIndex/blob/master/src/rum-speedindex.js interface ResourcesTimeMap { @@ -21,7 +22,7 @@ function getPaintBlocks(resources: ResourcesTimeMap): Array { for (let i = 0; i < elements.length; i++) { const element = elements[i]; let src = ''; - if (element instanceof HTMLImageElement) { + if (hasTag(element, "IMG")) { src = element.currentSrc || element.src; } if (!src) { diff --git a/tracker/tracker/src/main/modules/viewport.ts b/tracker/tracker/src/main/modules/viewport.ts index 1d70a3ebf..29eac6806 100644 --- a/tracker/tracker/src/main/modules/viewport.ts +++ b/tracker/tracker/src/main/modules/viewport.ts @@ -1,4 +1,4 @@ -import App from "../app/index.js"; +import type App from "../app/index.js"; import { SetPageLocation, SetViewportSize,