Holy-Unblocker/lib/browser/document.js
2022-02-09 22:33:23 -08:00

271 lines
No EOL
13 KiB
JavaScript

function createDocumentRewriter(ctx) {
return function rewriteDocument() {
if (ctx.serviceWorker) return;
const {
HTMLMediaElement,
HTMLScriptElement,
HTMLAudioElement,
HTMLVideoElement,
HTMLInputElement,
HTMLEmbedElement,
HTMLTrackElement,
HTMLAnchorElement,
HTMLIFrameElement,
HTMLAreaElement,
HTMLLinkElement,
HTMLBaseElement,
HTMLFormElement,
HTMLImageElement,
HTMLSourceElement,
} = ctx.window;
const cookie = Object.getOwnPropertyDescriptor(ctx.window.Document.prototype, 'cookie');
const domain = Object.getOwnPropertyDescriptor(ctx.window.Document.prototype, 'domain');
const title = Object.getOwnPropertyDescriptor(ctx.window.Document.prototype, 'title');
const baseURI = Object.getOwnPropertyDescriptor(ctx.window.Node.prototype, 'baseURI');
const cookieEnabled = Object.getOwnPropertyDescriptor(ctx.window.Navigator.prototype, 'cookieEnabled');
let spoofTitle = '';
let spoofDomain = ctx.location.hostname;
if (ctx.window.Document.prototype.write) {
ctx.window.Document.prototype.write = new Proxy(ctx.window.Document.prototype.write, {
apply: (target, that , args) => {
if (args.length) args = [ ctx.html.process(args.join(''), ctx.meta) ];
return Reflect.apply(target, that, args);
},
});
};
if (ctx.window.Document.prototype.hasOwnProperty('cookie')) {
Object.defineProperty(ctx.window.Document.prototype, 'cookie', {
get: new Proxy(cookie.get, {
apply: (target, that, args) => {
const cookies = Reflect.apply(target, that, args);
return ctx.config.cookie ? ctx.cookies.decode(cookies, ctx.meta) : '';
},
}),
set: new Proxy(cookie.set, {
apply: (target, that, [ val ]) => {
return Reflect.apply(target, that, [ ctx.config.cookie ? ctx.cookies.encode(val, ctx.meta) : '' ]);
},
}),
});
};
if (ctx.window.Document.prototype.writeln) {
ctx.window.Document.prototype.writeln = new Proxy(ctx.window.Document.prototype.writeln, {
apply: (target, that , args) => {
if (args.length) args = [ ctx.html.process(args.join(''), ctx.meta) ];
return Reflect.apply(target, that, args);
},
});
};
if (ctx.window.Element.prototype.setAttribute) {
ctx.window.Element.prototype.setAttribute = new Proxy(ctx.window.Element.prototype.setAttribute, {
apply: (target, that, args) => {
if (args[0] && args[1]) {
const handler = ctx.html.attributeRoute({
name: args[0],
value: args[1],
node: that,
});
switch(handler) {
case 'url':
Reflect.apply(target, that, [`corrosion-${args[0]}`, args[1]]);
//if (that.tagName == 'SCRIPT' && args[0] == 'src') flags.push('js');
args[1] = ctx.url.wrap(args[1], ctx.meta);
break;
case 'srcset':
Reflect.apply(target, that, [`corrosion-${args[0]}`, args[1]]);
args[1] = ctx.html.srcset(args[1], ctx.meta);
break;
case 'css':
Reflect.apply(target, that, [`corrosion-${args[0]}`, args[1]]);
args[1] = ctx.css.process(args[1], { ...ctx.meta, context: 'declarationList' });
break;
case 'html':
Reflect.apply(target, that, [`corrosion-${args[0]}`, args[1]]);
args[1] = ctx.html.process(args[1], ctx.meta);
break;
case 'delete':
return Reflect.apply(target, that, [`corrosion-${args[0]}`, args[1]]);
};
};
return Reflect.apply(target, that, args);
},
});
};
if (ctx.window.Element.prototype.getAttribute) {
ctx.window.Element.prototype.getAttribute = new Proxy(ctx.window.Element.prototype.getAttribute, {
apply: (target, that, args) => {
if (args[0] && that.hasAttribute(`corrosion-${args[0]}`)) args[0] = `corrosion-${args[0]}`;
return Reflect.apply(target, that, args);
},
});
};
ctx.window.CSSStyleDeclaration.prototype.setProperty = new Proxy(ctx.window.CSSStyleDeclaration.prototype.setProperty, {
apply: (target, that, args) => {
if (args[1]) args[1] = ctx.css.process(args[1], { context: 'value', ...ctx.meta, });
return Reflect.apply(target, that, args);
},
});
if (ctx.window.Audio) {
ctx.window.Audio = new Proxy(ctx.window.Audio, {
construct: (target, args) => {
if (args[0]) args[0] = ctx.url.wrap(args[0], ctx.meta);
return Reflect.construct(target, args);
},
});
};
[
'innerHTML',
'outerHTML',
].forEach(html => {
const descriptor = Object.getOwnPropertyDescriptor(ctx.window.Element.prototype, html);
Object.defineProperty(ctx.window.Element.prototype, html, {
get: new Proxy(descriptor.get, {
apply: (target, that, args) => {
const body = Reflect.apply(target, that, args);
if (!body || html == 'innerHTML' && that.tagName == 'SCRIPT') return body;
return ctx.html.source(body, ctx.meta);
},
}),
set: new Proxy(descriptor.set, {
apply(target, that, [ val ]) {
return Reflect.apply(target, that, [ val ? ctx.html.process(val.toString(), ctx.meta) : val, ]);
},
}),
});
});
[
['background', 'background'],
['backgroundImage', 'background-image'],
['listStyleImage', 'list-style-image'],
].forEach(([key, cssProperty]) => {
Object.defineProperty(ctx.window.CSS2Properties ? ctx.window.CSS2Properties.prototype : ctx.window.CSSStyleDeclaration.prototype, key, {
get() {
return this.getPropertyValue(cssProperty);
},
set(val) {
return this.setProperty(cssProperty, val);
},
});
});
Object.defineProperty(ctx.window.Document.prototype, 'domain', {
get: new Proxy(domain.get, {
apply: () => spoofDomain,
}),
set: new Proxy(domain.set, {
apply: (target, that, [ val ]) => {
if (!val.toString().endsWith(ctx.location.hostname.split('.').slice(-2).join('.'))) return Reflect.apply(target, that, ['']);
return spoofDomain = val;
},
}),
});
if (ctx.config.title) Object.defineProperty(ctx.window.Document.prototype, 'title', {
get: new Proxy(title.get, {
apply: () => spoofTitle,
}),
set: new Proxy(title.set, {
apply: (target, that, [ val ]) => spoofTitle = val,
}),
});
Object.defineProperty(ctx.window.Navigator.prototype, 'cookieEnabled', {
get: new Proxy(cookieEnabled.get, {
apply: () => ctx.config.cookie,
}),
});
Object.defineProperty(ctx.window.Node.prototype, 'baseURI', {
get: new Proxy(baseURI.get, {
apply: (target, that, args) => {
const val = Reflect.apply(target, that, args);
return val.startsWith(ctx.meta.origin) ? ctx.url.unwrap(val, ctx.meta) : val;
},
}),
});
[
{
elements: [ HTMLScriptElement, HTMLMediaElement, HTMLImageElement, HTMLAudioElement, HTMLVideoElement, HTMLInputElement, HTMLEmbedElement, HTMLIFrameElement, HTMLTrackElement, HTMLSourceElement],
properties: ['src'],
handler: 'url',
},
{
elements: [ HTMLFormElement ],
properties: ['action'],
handler: 'url',
},
{
elements: [ HTMLAnchorElement, HTMLAreaElement, HTMLLinkElement, HTMLBaseElement ],
properties: ['href'],
handler: 'url',
},
{
elements: [ HTMLImageElement, HTMLSourceElement ],
properties: ['srcset'],
handler: 'srcset',
},
{
elements: [ HTMLScriptElement ],
properties: ['integrity'],
handler: 'delete',
},
{
elements: [ HTMLIFrameElement ],
properties: ['contentWindow'],
handler: 'window',
},
].forEach(entry => {
entry.elements.forEach(element => {
if (!element) return;
entry.properties.forEach(property => {
if (!element.prototype.hasOwnProperty(property)) return;
const descriptor = Object.getOwnPropertyDescriptor(element.prototype, property);
Object.defineProperty(element.prototype, property, {
get: descriptor.get ? new Proxy(descriptor.get, {
apply: (target, that, args) => {
let val = Reflect.apply(target, that, args);
let flags = [];
switch(entry.handler) {
case 'url':
//if (that.tagName == 'SCRIPT' && property == 'src') flags.push('js');
val = ctx.url.unwrap(val, ctx.meta);
break;
case 'srcset':
val = ctx.html.unsrcset(val, ctx.meta);
break;
case 'delete':
val = that.getAttribute(`corrosion-${property}`);
break;
case 'window':
try {
if (!val.$corrosion) {
val.$corrosion = new ctx.constructor({ ...ctx.config, window: val, });
val.$corrosion.init();
val.$corrosion.meta = ctx.meta;
};
} catch(e) {};
};
return val;
},
}) : undefined,
set: descriptor.set ? new Proxy(descriptor.set, {
apply(target, that, [ val ]) {
let newVal = val;
switch(entry.handler) {
case 'url':
newVal = ctx.url.wrap(newVal, ctx.meta);
break;
case 'srcset':
newVal = ctx.html.srcset(newVal, ctx.meta);
break;
case 'delete':
that.setAttribute(property, newVal);
return newVal;
};
return Reflect.apply(target, that, [ newVal ]);
},
}) : undefined,
});
});
});
});
};
};
module.exports = createDocumentRewriter;