Skip to content

Commit 7c0efac

Browse files
committed
fix: improve PowerLink skeleton detection and cleanup
- add `Object.defineProperty` proxy for `renderer.call` restoration - support additional PowerLink function call patterns - traverse parent vnode to find PowerLink container more reliably
1 parent 106a969 commit 7c0efac

1 file changed

Lines changed: 79 additions & 2 deletions

File tree

userscript/source/index.ts

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,19 @@ const Win = typeof unsafeWindow !== 'undefined' ? unsafeWindow : window
1717
export function RunNamuLinkUserscript(BrowserWindow: typeof window, UserscriptName: string = 'NamuLink'): void {
1818
const OriginalFunctionPrototypeCall = BrowserWindow.Function.prototype.call
1919
const OriginalReflectApply = BrowserWindow.Reflect.apply
20+
const OriginalObjectDefineProperty = BrowserWindow.Object.defineProperty
2021

2122
const PL2MajorFuncCallPatterns: RegExp[][] = [[
2223
/function *\( *[A-Za-z0-9]+ *, *[A-Za-z0-9]+ *, *[A-Za-z0-9]+ *, *[A-Za-z0-9]+ *, *[A-Za-z0-9]+ *, *[A-Za-z0-9]+ *\) *{ *return *[A-Za-z.-9]+/,
2324
/, *[A-Za-z0-9]+ *, *[A-Za-z0-9]+ *\) *{ *return *[A-Za-z.-9]+ *\( *[0-9a-fx *+-]+ *, *[A-Za-z.-9]+ *, *[A-Za-z.-9]+ *, *[0-9a-fx *+-]+/,
2425
/return *[A-Za-z.-9]+ *\( *[0-9a-fx *+-]+ *, *[A-Za-z.-9]+ *, *[A-Za-z.-9]+ *, *[0-9a-fx *+-]+ *,[A-Za-z.-9]+ *, *[A-Za-z.-9]+ * *\) *; *}/
26+
], [
27+
/function *[A-Za-z0-9]+ *\( *[A-Za-z0-9]+ *, *[A-Za-z0-9]+ *, *[A-Za-z0-9]+ *, *[A-Za-z0-9]+ *, *[A-Za-z0-9]+ *, *[A-Za-z0-9]+ *\) *{ *return *[A-Za-z.-9]+/,
28+
/, *[A-Za-z0-9]+ *, *[A-Za-z0-9]+ *\) *{ *return *[A-Za-z.-9]+ *\( *[0-9a-fx *+-]+ *, *[A-Za-z.-9]+ *, *[A-Za-z.-9]+ *, *[0-9a-fx *+-]+/,
29+
/return *[A-Za-z.-9]+ *\( *[0-9a-fx *+-]+ *, *[A-Za-z.-9]+ *, *[A-Za-z.-9]+ *, *[0-9a-fx *+-]+ *,[A-Za-z.-9]+ *, *[A-Za-z.-9]+ * *\) *; *}/
2530
]]
2631

27-
function GetPowerLinkElementFromArg(Arg: unknown): HTMLElement | null {
32+
function PowerLinkElementFromArg(Arg: unknown): HTMLElement | null {
2833
if (typeof Arg !== 'object' || Arg === null) return null
2934

3035
const Visited = new Set<object>()
@@ -45,6 +50,54 @@ export function RunNamuLinkUserscript(BrowserWindow: typeof window, UserscriptNa
4550

4651
return null
4752
}
53+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
54+
function PowerLinkRenderFromArg(Arg: unknown): Function | null {
55+
if (typeof Arg !== 'object' || Arg === null) return null
56+
57+
const Visited = new Set<object>()
58+
let Current: unknown = (Arg as Record<string, unknown>)['_']
59+
60+
while (typeof Current === 'object' && Current !== null) {
61+
if (Visited.has(Current)) break
62+
Visited.add(Current)
63+
64+
const Render = (Current as Record<string, unknown>)['render']
65+
if (typeof Render === 'function') return Render
66+
67+
Current = (Current as Record<string, unknown>)['parent']
68+
}
69+
70+
return null
71+
}
72+
function PowerLinkElementFromArgParent(Arg: unknown): HTMLElement | null {
73+
if (typeof Arg !== 'object' || Arg === null) return null
74+
75+
const Visited = new Set<object>()
76+
let Current = (Arg as Record<string, unknown>)['_']
77+
78+
while (typeof Current === 'object' && Current !== null) {
79+
if (Visited.has(Current)) break
80+
Visited.add(Current)
81+
82+
const Parent = (Current as Record<string, unknown>)['parent']
83+
if (typeof Parent === 'object' && Parent !== null) {
84+
const ParentVNode = (Parent as Record<string, unknown>)['vnode']
85+
if (typeof ParentVNode === 'object' && ParentVNode !== null) {
86+
const ParentElement = (ParentVNode as Record<string, unknown>)['el']
87+
if (ParentElement instanceof HTMLElement) return ParentElement
88+
}
89+
}
90+
91+
const VNode = (Current as Record<string, unknown>)['vnode']
92+
if (typeof VNode === 'object' && VNode !== null) {
93+
const Element = (VNode as Record<string, unknown>)['el']
94+
if (Element instanceof HTMLElement) return Element
95+
}
96+
Current = Parent
97+
}
98+
return null
99+
}
100+
48101
const MinRatio = 0.35
49102
const MaxRatio = 0.75
50103
const EpsilonRatio = 0.04
@@ -62,7 +115,7 @@ export function RunNamuLinkUserscript(BrowserWindow: typeof window, UserscriptNa
62115

63116
const Stringified = String(ThisArg)
64117
if (Stringified.length < 500 && PL2MajorFuncCallPatterns.filter(Patterns => Patterns.filter(Pattern => Pattern.test(Stringified)).length === Patterns.length).length === 1) {
65-
let PL2Element: HTMLElement | null = GetPowerLinkElementFromArg(Args[6])
118+
let PL2Element: HTMLElement | null = PowerLinkElementFromArgParent(Args[6])
66119
if (PL2Element !== null && [...PL2Element.querySelectorAll('*')].filter(Child => {
67120
if (!(Child instanceof HTMLElement)) return false
68121
let PL2TitleHeight = Child.getClientRects()[0]?.height ?? 0
@@ -84,6 +137,30 @@ export function RunNamuLinkUserscript(BrowserWindow: typeof window, UserscriptNa
84137
}
85138
})
86139

140+
BrowserWindow.Object.defineProperty = new Proxy(OriginalObjectDefineProperty, {
141+
apply(Target: typeof Object.defineProperty, ThisArg: undefined, Args: Parameters<typeof Object.defineProperty>) {
142+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
143+
let VuejsRenderer: Function | null = PowerLinkRenderFromArg(Args[0])
144+
let PL2Element: HTMLElement | null = PowerLinkElementFromArgParent(Args[0])
145+
let Stringified = String(VuejsRenderer ?? '')
146+
if (VuejsRenderer !== null && PL2Element !== null && Stringified.length < 500 &&
147+
PL2MajorFuncCallPatterns.filter(Patterns => Patterns.filter(Pattern => Pattern.test(Stringified)).length === Patterns.length).length === 1 &&
148+
[...PL2Element.querySelectorAll('*')].filter(Child => {
149+
if (!(Child instanceof HTMLElement)) return false
150+
let PL2TitleHeight = Child.getClientRects()[0]?.height ?? 0
151+
let PL2TitleMarginBottom = Math.max(Number(getComputedStyle(Child).getPropertyValue('padding-bottom').replaceAll(/px/g, '')),
152+
Number(getComputedStyle(Child).getPropertyValue('margin-bottom').replaceAll(/px/g, '')))
153+
return PL2TitleHeight > 0 && PL2TitleMarginBottom >= PL2TitleHeight * (MinRatio - EpsilonRatio) && PL2TitleMarginBottom <= PL2TitleHeight * (MaxRatio + EpsilonRatio)
154+
}).length >= 1
155+
) {
156+
console.debug(`[${UserscriptName}]: Restoring renderer.call for detected PowerLink skeleton:`, Args[0])
157+
VuejsRenderer.call = Function.prototype.call
158+
return
159+
}
160+
return OriginalReflectApply(Target, ThisArg, Args)
161+
}
162+
})
163+
87164
let PL2AfterLoadInitTimerPatterns: RegExp[][] = [[
88165
/\( *\) *=> *{ *var *_0x[0-9a-z]+ *= *a0_0x[0-9a-f]+ *; *this\[ *_0x[a-z0-9]+\( *0x[0-9a-f]+ *\) *\]\(\); *}/,
89166
/\( *\) *=> *{ *var *_0x[0-9a-z]+ *= *a0_0x[0-9a-f]+ *; *this\[ *_0x[a-z0-9]+\( *0x[0-9a-f]+ *\) *\]\(\); *}/

0 commit comments

Comments
 (0)