Last active
August 18, 2022 05:40
-
-
Save Ceiridge/0f38c8c91c5c56781f8140e80f3a391c to your computer and use it in GitHub Desktop.
Log/Hook all native JavaScript functions recursively
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This hooks all functions recursively from window to log all native JavaScript functions | |
// The best way to use this is to inject this script at document-start via TamperMonkey. | |
// Default ignores (should be most likely extended): | |
window.__IGNORE_CALLTREE = new Set(["::requestAnimationFrame", "::getComputedStyle", "document::getElementsByTagName", "::setTimeout", "::clearTimeout", "document::getElementById", "Math::random", "Element.prototype::getAttribute", "::parseFloat", "::parseInt", "CSSStyleDeclaration.prototype::getPropertyValue"]); | |
(function () { | |
// You can ignore methods the following way: | |
// window.__IGNORE_CALLTREE = new Set(); | |
// window.__IGNORE_CALLTREE.add("document::yourMethod"); | |
const conLog = console.log; | |
const conTrace = console.trace; | |
const conGroupOpen = console.group; | |
const conGroup = console.groupCollapsed; | |
const conGroupEnd = console.groupEnd; | |
const isNative = (fn) => /\{\s+\[native code\]/.test(Function.prototype.toString.call(fn)); | |
const hookFunction = (obj, name, calltree) => { | |
const original = obj[name]; | |
if (calltree.endsWith(".")) { | |
calltree = calltree.substring(0, calltree.length - 1); | |
} | |
const callTreeName = calltree + "::" + name; | |
obj[name] = function () { | |
let result; | |
let errResult = null; | |
try { | |
if (calltree.endsWith(".prototype.constructor.")) { | |
result = new original(...arguments); | |
} else { | |
result = original.apply(this, arguments); | |
} | |
} catch (err) { | |
if (err.message.includes("Failed to construct") && err.message.includes("Please use the 'new' operator")) { | |
try { | |
result = new original(...arguments); | |
} catch (err2) { | |
errResult = err2; | |
} | |
} else { | |
console.error(err); | |
errResult = err; | |
} | |
} | |
if (window.__IGNORE_CALLTREE ? (!window.__IGNORE_CALLTREE.has(callTreeName)) : true) { | |
conGroup(calltree + "::%c" + name + ` %c[${arguments.length}]${result === undefined ? "" : (" => " + (result === null ? "null" : (typeof result)))}`, "color: orange", "color: gray"); | |
conGroupOpen("%cArguments:", "color: gray"); | |
for (const arg of arguments) { | |
conLog(arg); | |
} | |
conGroupEnd(); | |
conGroupOpen("%cReturn Value:", "color: gray"); | |
conLog(result); | |
conGroupEnd(); | |
conGroup("%cStacktrace:", "color: gray"); | |
conTrace(); | |
conGroupEnd(); | |
conGroupEnd(); | |
} | |
if (errResult) { | |
throw errResult; | |
} | |
return result; | |
}; | |
}; | |
const WIN_FORBIDDEN = new Set(["window", "frames", "frameElement", "opener", "parent", "top", "self", "Object", "Function", "Array", "String", "Symbol", "this", "RegExp", "Number", "Intl", "Boolean", "Set"]); | |
// const DOC_FORBIDDEN = new Set(["doctype", "documentElement", "activeElement", "anchors", "applets", "body", "embeds", "forms", "head", "images", "implementation", "links", "scripts", "title", "scrollingElement", "children"]); | |
const hookAll = (obj, calltree, previous) => { | |
if (previous.some(prev => prev === obj)) { | |
return; | |
} | |
// conLog(calltree); | |
previous = [...previous, obj]; | |
for (const name of Object.getOwnPropertyNames(obj)) { | |
try { | |
if (obj === window && (WIN_FORBIDDEN.has(name) || (typeof name === "number"))) { | |
continue; | |
} | |
const found = obj[name]; | |
const typ = typeof found; | |
if (found === null || found === undefined) { | |
continue; | |
} | |
if (obj === document && (typ !== "function")) { | |
continue; | |
} | |
if (typ === "function") { | |
if (isNative(found) && (found.prototype ? (found.prototype.constructor !== found) : true)) { | |
hookFunction(obj, name, calltree); | |
} | |
hookAll(found, calltree + name + ".", previous); | |
} else if (typ === "object") { | |
if (found.prototype) { | |
// conLog(found); | |
hookAll(found.prototype, calltree + "_", previous); | |
} else { | |
hookAll(found, calltree + name + ".", previous); | |
} | |
} | |
} catch (_) {} | |
} | |
}; | |
hookAll(window, "", []); | |
}()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment