mirror of
https://github.com/kkapsner/CanvasBlocker
synced 2024-12-22 21:00:23 +01:00
00e60074c3
For #705
330 lines
10 KiB
JavaScript
330 lines
10 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
(function(){
|
|
"use strict";
|
|
|
|
let scope;
|
|
if ((typeof exports) !== "undefined"){
|
|
scope = exports;
|
|
}
|
|
else {
|
|
scope = require.register("./extension", {});
|
|
}
|
|
|
|
const browserAvailable = typeof browser !== "undefined";
|
|
const logging = require("./logging");
|
|
|
|
scope.inBackgroundScript = !!(
|
|
browserAvailable &&
|
|
browser.extension.getBackgroundPage &&
|
|
browser.extension.getBackgroundPage() === window
|
|
);
|
|
|
|
scope.getTranslation = browserAvailable? function getTranslation(id){
|
|
return browser.i18n.getMessage(id);
|
|
}: function(id){
|
|
return id;
|
|
};
|
|
|
|
scope.parseTranslation = function parseTranslation(message, parameters = {}){
|
|
const container = document.createDocumentFragment();
|
|
|
|
message.split(/(\{[^}]+\})/).forEach(function(part){
|
|
if (part.startsWith("{") && part.endsWith("}")){
|
|
part = part.substring(1, part.length - 1);
|
|
const args = part.split(":");
|
|
switch (args[0]){
|
|
case "image": {
|
|
const image = document.createElement("img");
|
|
image.className = "noticeImage";
|
|
image.src = args.slice(1).join(":");
|
|
container.appendChild(image);
|
|
break;
|
|
}
|
|
case "link": {
|
|
const link = document.createElement("a");
|
|
link.target = "_blank";
|
|
link.textContent = args[1];
|
|
link.href = args.slice(2).join(":");
|
|
container.appendChild(link);
|
|
break;
|
|
}
|
|
default:
|
|
if (parameters[args[0]]){
|
|
const parameter = parameters[args[0]];
|
|
if ((typeof parameter) === "function"){
|
|
container.appendChild(parameter(args.slice(1).join(":")));
|
|
}
|
|
else {
|
|
container.appendChild(document.createTextNode(parameter));
|
|
}
|
|
}
|
|
else {
|
|
container.appendChild(document.createTextNode(part));
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
container.appendChild(document.createTextNode(part));
|
|
}
|
|
});
|
|
return container;
|
|
};
|
|
|
|
scope.getURL = function getURL(str){
|
|
return browser.runtime.getURL(str);
|
|
};
|
|
|
|
scope.extensionID = browserAvailable? scope.getURL(""): "extensionID";
|
|
|
|
scope.inIncognitoContext = browserAvailable? browser.extension.inIncognitoContext: false;
|
|
|
|
scope.message = {
|
|
on: browserAvailable? function(callback){
|
|
return browser.runtime.onMessage.addListener(callback);
|
|
}: function(){
|
|
return false;
|
|
},
|
|
send: browserAvailable? function(data){
|
|
return browser.runtime.sendMessage(data);
|
|
}: function(){
|
|
return false;
|
|
}
|
|
};
|
|
Object.seal(scope.message);
|
|
|
|
scope.getWrapped = function getWrapped(obj){
|
|
return obj && (obj.wrappedJSObject || obj);
|
|
};
|
|
|
|
scope.exportFunctionWithName = function exportFunctionWithName(func, context, name){
|
|
const targetObject = scope.getWrapped(context).Object.create(null);
|
|
const exportedTry = exportFunction(func, targetObject, {allowCrossOriginArguments: true, defineAs: name});
|
|
if (exportedTry.name === name){
|
|
return exportedTry;
|
|
}
|
|
else {
|
|
if (func.name === name){
|
|
logging.message(
|
|
"FireFox bug: Need to change name in exportFunction from",
|
|
exportedTry.name,
|
|
"(originally correct) to",
|
|
name
|
|
);
|
|
}
|
|
else {
|
|
logging.error("Wrong name specified for", name, new Error());
|
|
}
|
|
const wrappedContext = scope.getWrapped(context);
|
|
const options = {
|
|
allowCrossOriginArguments: true,
|
|
defineAs: name
|
|
};
|
|
const oldDescriptor = Object.getOwnPropertyDescriptor(wrappedContext, name);
|
|
if (oldDescriptor && !oldDescriptor.configurable){
|
|
logging.error(
|
|
"Unable to export function with the correct name", name,
|
|
"instead we have to use", exportedTry.name
|
|
);
|
|
return exportedTry;
|
|
}
|
|
const exported = exportFunction(func, context, options);
|
|
if (oldDescriptor){
|
|
Object.defineProperty(wrappedContext, name, oldDescriptor);
|
|
}
|
|
else {
|
|
delete wrappedContext[name];
|
|
}
|
|
return exported;
|
|
}
|
|
};
|
|
|
|
const proxies = new Map();
|
|
const changedWindowsForProxies = new WeakMap();
|
|
function setupWindowForProxies(window){
|
|
if (changedWindowsForProxies.get(window)){
|
|
return;
|
|
}
|
|
const wrappedWindow = scope.getWrapped(window);
|
|
|
|
const functionPrototype = wrappedWindow.Function.prototype;
|
|
const originalToString = functionPrototype.toString;
|
|
changedWindowsForProxies.set(window, originalToString);
|
|
const alteredToString = scope.createProxyFunction(
|
|
window,
|
|
originalToString,
|
|
function toString(){
|
|
if (proxies.has(this)){
|
|
return proxies.get(this).string;
|
|
}
|
|
return originalToString.call(scope.getWrapped(this));
|
|
}
|
|
);
|
|
scope.changeProperty(window, "toString", {
|
|
object: functionPrototype,
|
|
name: "toString",
|
|
type: "value",
|
|
changed: alteredToString
|
|
});
|
|
|
|
const wrappedReflect = wrappedWindow.Reflect;
|
|
const originalReflectSetPrototypeOf = wrappedReflect.setPrototypeOf;
|
|
const alteredReflectSetPrototypeOf = scope.exportFunctionWithName(
|
|
function setPrototypeOf(target, prototype){
|
|
target = scope.getWrapped(target);
|
|
if (proxies.has(target)){
|
|
target = proxies.get(target).wrappedOriginal;
|
|
}
|
|
if (proxies.has(prototype)){
|
|
prototype = proxies.get(prototype).wrappedOriginal;
|
|
}
|
|
if (prototype){
|
|
const grandPrototype = wrappedReflect.getPrototypeOf(prototype);
|
|
if (proxies.has(grandPrototype)){
|
|
const testPrototype = wrappedWindow.Object.create(proxies.get(grandPrototype).wrappedOriginal);
|
|
const value = originalReflectSetPrototypeOf.call(wrappedReflect, target, testPrototype);
|
|
if (!value){
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
const value = originalReflectSetPrototypeOf.call(wrappedReflect, target, scope.getWrapped(prototype));
|
|
return value;
|
|
}, window, "setPrototypeOf"
|
|
);
|
|
scope.changeProperty(window, "toString", {
|
|
object: wrappedWindow.Reflect,
|
|
name: "setPrototypeOf",
|
|
type: "value",
|
|
changed: alteredReflectSetPrototypeOf
|
|
});
|
|
}
|
|
scope.createProxyFunction = function createProxyFunction(window, original, replacement){
|
|
setupWindowForProxies(window);
|
|
const wrappedObject = scope.getWrapped(window).Object;
|
|
const handler = wrappedObject.create(null);
|
|
handler.apply = scope.exportFunctionWithName(function(target, thisArg, args){
|
|
try {
|
|
return args.length?
|
|
replacement.call(thisArg, ...args):
|
|
replacement.call(thisArg);
|
|
}
|
|
catch (error){
|
|
try {
|
|
return original.apply(thisArg, args);
|
|
}
|
|
catch (error){
|
|
return scope.getWrapped(target).apply(thisArg, args);
|
|
}
|
|
}
|
|
}, window, "");
|
|
handler.setPrototypeOf = scope.exportFunctionWithName(function(target, prototype){
|
|
target = scope.getWrapped(target);
|
|
if (proxies.has(target)){
|
|
target = proxies.get(target).wrappedOriginal;
|
|
}
|
|
if (proxies.has(prototype)){
|
|
prototype = proxies.get(prototype).wrappedOriginal;
|
|
}
|
|
if (prototype){
|
|
const grandPrototype = wrappedObject.getPrototypeOf(prototype);
|
|
if (proxies.has(grandPrototype)){
|
|
const testPrototype = wrappedObject.create(proxies.get(grandPrototype).wrappedOriginal);
|
|
wrappedObject.setPrototypeOf(target, testPrototype);
|
|
}
|
|
}
|
|
return wrappedObject.setPrototypeOf(target, scope.getWrapped(prototype));
|
|
}, window, "");
|
|
const proxy = new window.Proxy(original, handler);
|
|
const proxyData = {
|
|
original: original,
|
|
wrappedOriginal: scope.getWrapped(original),
|
|
string: changedWindowsForProxies.get(window).call(original),
|
|
};
|
|
proxies.set(proxy, proxyData);
|
|
proxies.set(scope.getWrapped(proxy), proxyData);
|
|
return scope.getWrapped(proxy);
|
|
};
|
|
|
|
const changedPropertiesByWindow = new WeakMap();
|
|
scope.changeProperty = function(window, group, {object, name, type, changed}){
|
|
let changedProperties = changedPropertiesByWindow.get(scope.getWrapped(window));
|
|
if (!changedProperties){
|
|
changedProperties = [];
|
|
changedPropertiesByWindow.set(scope.getWrapped(window), changedProperties);
|
|
}
|
|
const descriptor = Object.getOwnPropertyDescriptor(object, name);
|
|
const original = descriptor[type];
|
|
descriptor[type] = changed;
|
|
Object.defineProperty(object, name, descriptor);
|
|
changedProperties.push({group, object, name, type, original});
|
|
};
|
|
scope.revertProperties = function(window, group){
|
|
window = scope.getWrapped(window);
|
|
let changedProperties = changedPropertiesByWindow.get(window);
|
|
if (!changedProperties){
|
|
return;
|
|
}
|
|
if (group){
|
|
const remainingProperties = changedProperties.filter(function(changedProperty){
|
|
return changedProperty.group !== group;
|
|
});
|
|
changedPropertiesByWindow.set(window, remainingProperties);
|
|
changedProperties = changedProperties.filter(function(changedProperty){
|
|
return changedProperty.group === group;
|
|
});
|
|
}
|
|
else {
|
|
changedPropertiesByWindow.delete(window);
|
|
}
|
|
|
|
for (let i = changedProperties.length - 1; i >= 0; i -= 1){
|
|
const {object, name, type, original} = changedProperties[i];
|
|
logging.verbose("reverting", name, "on", object);
|
|
const descriptor = Object.getOwnPropertyDescriptor(object, name);
|
|
descriptor[type] = original;
|
|
Object.defineProperty(object, name, descriptor);
|
|
}
|
|
};
|
|
|
|
scope.waitSync = function waitSync(reason = "for no reason"){
|
|
logging.message(`Starting synchronous request ${reason}.`);
|
|
try {
|
|
let xhr = new XMLHttpRequest();
|
|
xhr.open("GET", "https://[::]", false);
|
|
xhr.send();
|
|
xhr = null;
|
|
}
|
|
catch (error){
|
|
logging.verbose("Error in XHR:", error);
|
|
}
|
|
};
|
|
|
|
scope.displayVersion = async function displayVersion(node, displayRefresh = false){
|
|
if ("string" === typeof node){
|
|
node = document.getElementById(node);
|
|
}
|
|
if (!node){
|
|
throw "display node not found";
|
|
}
|
|
fetch(scope.getURL("manifest.json")).then(function(response){
|
|
return response.json();
|
|
}).then(function(manifest){
|
|
node.textContent = "Version " + manifest.version;
|
|
return manifest.version;
|
|
}).catch(function(error){
|
|
node.textContent = "Unable to get version: " + error;
|
|
});
|
|
|
|
if (displayRefresh){
|
|
// Workaround to hide the scroll bars
|
|
window.setTimeout(function(){
|
|
node.style.display = "none";
|
|
node.style.display = "";
|
|
}, displayRefresh);
|
|
}
|
|
};
|
|
|
|
Object.seal(scope);
|
|
}()); |