Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: zone detection #1570

Merged
merged 5 commits into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 16 additions & 21 deletions patches/@[email protected]
Original file line number Diff line number Diff line change
@@ -1,37 +1,32 @@
diff --git a/dist/record.js b/dist/record.js
index 46ec389fefb698243008b39db65470dbdf0a3857..70db907755d68b08232e25e1b255a974f56f3c65 100644
index 46ec389fefb698243008b39db65470dbdf0a3857..a18724d8b6ba43a30935daf257127fbb0c898541 100644
--- a/dist/record.js
+++ b/dist/record.js
@@ -26,6 +26,19 @@ const testableMethods$1 = {
@@ -26,6 +26,14 @@ const testableMethods$1 = {
Element: [],
MutationObserver: ["constructor"]
};
+const isFunction = (x) => typeof x === 'function';
+const isAngularZonePatchedFunction = (x) => {
+
+const isAngularZonePresent = () => {
+ try {
+ if (!isFunction(x)) {
+ return false;
+ }
+ const prototypeKeys = Object.getOwnPropertyNames(x.prototype || {});
+ return prototypeKeys.some((key) => key.indexOf('__zone'));
+ return !!globalThis.Zone
+ } catch {
+ // we've seen some intermittent problems in Safari since introducing this check
+ return false
+ }
Comment on lines 11 to 15
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

turns out it is way simpler to just check if the global zone is present
if it's not, then angular didn't taint this

just like with the previous solution, folk can configure this to not be called zone, but then they can run posthog outside of the zone 🤷

+}
const untaintedBasePrototype$1 = {};
function getUntaintedPrototype$1(key) {
if (untaintedBasePrototype$1[key])
@@ -54,7 +67,7 @@ function getUntaintedPrototype$1(key) {
@@ -54,7 +62,7 @@ function getUntaintedPrototype$1(key) {
}
)
);
- if (isUntaintedAccessors && isUntaintedMethods) {
+ if (isUntaintedAccessors && isUntaintedMethods && !isAngularZonePatchedFunction(defaultObj)) {
+ if (isUntaintedAccessors && isUntaintedMethods && !isAngularZonePresent()) {
untaintedBasePrototype$1[key] = defaultObj.prototype;
return defaultObj.prototype;
}
@@ -65,10 +78,10 @@ function getUntaintedPrototype$1(key) {
@@ -65,10 +73,10 @@ function getUntaintedPrototype$1(key) {
if (!win) return defaultObj.prototype;
const untaintedObject = win[key].prototype;
document.body.removeChild(iframeEl);
Expand All @@ -44,7 +39,7 @@ index 46ec389fefb698243008b39db65470dbdf0a3857..70db907755d68b08232e25e1b255a974
}
}
const untaintedAccessorCache$1 = {};
@@ -246,6 +259,9 @@ function isCSSImportRule(rule2) {
@@ -246,6 +254,9 @@ function isCSSImportRule(rule2) {
function isCSSStyleRule(rule2) {
return "selectorText" in rule2;
}
Expand All @@ -54,7 +49,7 @@ index 46ec389fefb698243008b39db65470dbdf0a3857..70db907755d68b08232e25e1b255a974
class Mirror {
constructor() {
__publicField$1(this, "idNodeMap", /* @__PURE__ */ new Map());
@@ -809,9 +825,14 @@ function serializeElementNode(n2, options) {
@@ -809,9 +820,14 @@ function serializeElementNode(n2, options) {
}
}
if (tagName === "link" && inlineStylesheet) {
Expand All @@ -72,7 +67,7 @@ index 46ec389fefb698243008b39db65470dbdf0a3857..70db907755d68b08232e25e1b255a974
let cssText = null;
if (stylesheet) {
cssText = stringifyStylesheet(stylesheet);
@@ -855,7 +876,15 @@ function serializeElementNode(n2, options) {
@@ -855,7 +871,15 @@ function serializeElementNode(n2, options) {
}
}
if (tagName === "dialog" && n2.open) {
Expand All @@ -89,7 +84,7 @@ index 46ec389fefb698243008b39db65470dbdf0a3857..70db907755d68b08232e25e1b255a974
}
if (tagName === "canvas" && recordCanvas) {
if (n2.__context === "2d") {
@@ -1116,7300 +1145,227 @@ function serializeNodeWithId(n2, options) {
@@ -1116,7300 +1140,227 @@ function serializeNodeWithId(n2, options) {
keepIframeSrcFn
};
if (serializedNode.type === NodeType$2.Element && serializedNode.tagName === "textarea" && serializedNode.attributes.value !== void 0) ;
Expand Down Expand Up @@ -7599,16 +7594,16 @@ index 46ec389fefb698243008b39db65470dbdf0a3857..70db907755d68b08232e25e1b255a974
class BaseRRNode {
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
constructor(..._args) {
@@ -8507,7 +1463,7 @@ function getUntaintedPrototype(key) {
@@ -8507,7 +1458,7 @@ function getUntaintedPrototype(key) {
}
)
);
- if (isUntaintedAccessors && isUntaintedMethods) {
+ if (isUntaintedAccessors && isUntaintedMethods && !isAngularZonePatchedFunction(defaultObj)) {
+ if (isUntaintedAccessors && isUntaintedMethods && !isAngularZonePresent()) {
untaintedBasePrototype[key] = defaultObj.prototype;
return defaultObj.prototype;
}
@@ -11382,11 +4338,19 @@ class CanvasManager {
@@ -11382,11 +4333,19 @@ class CanvasManager {
let rafId;
const getCanvas = () => {
const matchedCanvas = [];
Expand All @@ -7633,7 +7628,7 @@ index 46ec389fefb698243008b39db65470dbdf0a3857..70db907755d68b08232e25e1b255a974
return matchedCanvas;
};
const takeCanvasSnapshots = (timestamp) => {
@@ -11407,13 +4371,20 @@ class CanvasManager {
@@ -11407,13 +4366,20 @@ class CanvasManager {
context.clear(context.COLOR_BUFFER_BIT);
}
}
Expand Down
6 changes: 3 additions & 3 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/utils/prototype-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

import { AssignableWindow } from './globals'
import { isAngularZonePatchedFunction, isFunction, isNativeFunction } from './type-utils'
import { isAngularZonePresent, isFunction, isNativeFunction } from './type-utils'
import { logger } from './logger'

interface NativeImplementationsCache {
Expand All @@ -25,7 +25,7 @@ export function getNativeImplementation<T extends keyof NativeImplementationsCac

let impl = assignableWindow[name] as NativeImplementationsCache[T]

if (isNativeFunction(impl) && !isAngularZonePatchedFunction(impl)) {
if (isNativeFunction(impl) && !isAngularZonePresent()) {
return (cachedImplementations[name] = impl.bind(assignableWindow) as NativeImplementationsCache[T])
}

Expand Down
11 changes: 4 additions & 7 deletions src/utils/type-utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { includes } from '.'
import { window } from './globals'
import { knownUnsafeEditableEvent, KnownUnsafeEditableEvent } from '../types'

// eslint-disable-next-line posthog-js/no-direct-array-check
Expand All @@ -24,13 +25,9 @@ export const isFunction = (x: unknown): x is (...args: any[]) => any => {
export const isNativeFunction = (x: unknown): x is (...args: any[]) => any =>
isFunction(x) && x.toString().indexOf('[native code]') !== -1

// When angular patches functions they pass the above `isNativeFunction` check
export const isAngularZonePatchedFunction = (x: unknown): boolean => {
if (!isFunction(x)) {
return false
}
const prototypeKeys = Object.getOwnPropertyNames(x.prototype || {})
return prototypeKeys.some((key) => key.indexOf('__zone'))
// When angular patches functions they pass the above `isNativeFunction` check (at least the MutationObserver)
export const isAngularZonePresent = (): boolean => {
return !!(window as any).Zone
}

// Underscore Addons
Expand Down
Loading