2019-03-14 16:51:20 +01:00
|
|
|
/* 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";
|
|
|
|
|
2019-11-28 01:26:35 +01:00
|
|
|
let scope;
|
2019-03-14 16:51:20 +01:00
|
|
|
if ((typeof exports) !== "undefined"){
|
|
|
|
scope = exports;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
scope = require.register("./extension", {});
|
|
|
|
}
|
|
|
|
|
2019-04-19 14:09:56 +02:00
|
|
|
const browserAvailable = typeof browser !== "undefined";
|
2020-01-19 01:27:26 +01:00
|
|
|
const logging = require("./logging");
|
2019-04-19 14:09:56 +02:00
|
|
|
|
|
|
|
scope.inBackgroundScript = !!(
|
|
|
|
browserAvailable &&
|
|
|
|
browser.extension.getBackgroundPage &&
|
|
|
|
browser.extension.getBackgroundPage() === window
|
|
|
|
);
|
|
|
|
|
2019-04-30 23:39:40 +02:00
|
|
|
scope.getTranslation = browserAvailable? function getTranslation(id){
|
2019-03-14 16:51:20 +01:00
|
|
|
return browser.i18n.getMessage(id);
|
2019-04-30 23:39:40 +02:00
|
|
|
}: function(id){
|
|
|
|
return id;
|
2019-03-14 16:51:20 +01:00
|
|
|
};
|
|
|
|
|
2020-01-25 00:42:16 +01:00
|
|
|
scope.parseTranslation = function parseTranslation(message, parameters = {}){
|
2020-01-23 15:23:56 +01:00
|
|
|
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;
|
|
|
|
}
|
2020-01-25 00:42:16 +01:00
|
|
|
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));
|
|
|
|
}
|
2020-01-23 15:23:56 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
container.appendChild(document.createTextNode(part));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return container;
|
|
|
|
};
|
|
|
|
|
2021-11-01 13:18:38 +01:00
|
|
|
scope.getURL = function getURL(str){
|
|
|
|
return (browser.runtime.getURL? browser.runtime.getURL: browser.extension.getURL)(str);
|
|
|
|
};
|
|
|
|
|
|
|
|
scope.extensionID = browserAvailable? scope.getURL(""): "extensionID";
|
2019-03-14 16:51:20 +01:00
|
|
|
|
2019-04-30 23:39:40 +02:00
|
|
|
scope.inIncognitoContext = browserAvailable? browser.extension.inIncognitoContext: false;
|
2019-03-14 16:51:20 +01:00
|
|
|
|
|
|
|
scope.message = {
|
2019-04-30 23:39:40 +02:00
|
|
|
on: browserAvailable? function(callback){
|
2019-03-14 16:51:20 +01:00
|
|
|
return browser.runtime.onMessage.addListener(callback);
|
2019-04-30 23:39:40 +02:00
|
|
|
}: function(){
|
|
|
|
return false;
|
2019-03-14 16:51:20 +01:00
|
|
|
},
|
2019-04-30 23:39:40 +02:00
|
|
|
send: browserAvailable? function(data){
|
2019-03-14 16:51:20 +01:00
|
|
|
return browser.runtime.sendMessage(data);
|
2019-04-30 23:39:40 +02:00
|
|
|
}: function(){
|
|
|
|
return false;
|
2019-03-14 16:51:20 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
Object.seal(scope.message);
|
|
|
|
|
2019-12-12 00:09:53 +01:00
|
|
|
scope.getWrapped = function getWrapped(obj){
|
|
|
|
return obj && (obj.wrappedJSObject || obj);
|
|
|
|
};
|
|
|
|
|
|
|
|
scope.exportFunctionWithName = function exportFunctionWithName(func, context, name){
|
2020-11-02 13:11:50 +01:00
|
|
|
const targetObject = scope.getWrapped(context).Object.create(null);
|
|
|
|
const exportedTry = exportFunction(func, targetObject, {allowCrossOriginArguments: true, defineAs: name});
|
2019-12-12 00:09:53 +01:00
|
|
|
if (exportedTry.name === name){
|
|
|
|
return exportedTry;
|
|
|
|
}
|
|
|
|
else {
|
2020-01-22 13:38:24 +01:00
|
|
|
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());
|
|
|
|
}
|
2019-12-12 00:09:53 +01:00
|
|
|
const wrappedContext = scope.getWrapped(context);
|
|
|
|
const options = {
|
2019-12-29 23:40:39 +01:00
|
|
|
allowCrossOriginArguments: true,
|
2019-12-12 00:09:53 +01:00
|
|
|
defineAs: name
|
|
|
|
};
|
|
|
|
const oldDescriptor = Object.getOwnPropertyDescriptor(wrappedContext, name);
|
2020-01-19 01:27:26 +01:00
|
|
|
if (oldDescriptor && !oldDescriptor.configurable){
|
|
|
|
logging.error(
|
|
|
|
"Unable to export function with the correct name", name,
|
|
|
|
"instead we have to use", exportedTry.name
|
|
|
|
);
|
|
|
|
return exportedTry;
|
|
|
|
}
|
2019-12-12 00:09:53 +01:00
|
|
|
const exported = exportFunction(func, context, options);
|
|
|
|
if (oldDescriptor){
|
|
|
|
Object.defineProperty(wrappedContext, name, oldDescriptor);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
delete wrappedContext[name];
|
|
|
|
}
|
|
|
|
return exported;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-02-21 11:00:33 +01:00
|
|
|
const proxies = new Map();
|
|
|
|
const changedToStrings = new WeakMap();
|
2021-01-26 13:47:59 +01:00
|
|
|
scope.createProxyFunction = function createProxyFunction(window, original, replacement){
|
2021-02-21 11:00:33 +01:00
|
|
|
if (!changedToStrings.get(window)){
|
|
|
|
changedToStrings.set(window, true);
|
|
|
|
const functionPrototype = scope.getWrapped(window).Function.prototype;
|
|
|
|
const toString = functionPrototype.toString;
|
|
|
|
scope.changeProperty(window, "toString", {
|
|
|
|
object: functionPrototype,
|
|
|
|
name: "toString",
|
|
|
|
type: "value",
|
|
|
|
changed: scope.createProxyFunction(
|
|
|
|
window,
|
|
|
|
toString,
|
|
|
|
function(){
|
|
|
|
return proxies.get(this) || toString.call(this);
|
|
|
|
}
|
|
|
|
)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
const handler = scope.getWrapped(window).Object.create(null);
|
2021-06-21 20:41:33 +02:00
|
|
|
handler.apply = scope.exportFunctionWithName(function(target, thisArgs, args){
|
2021-02-21 11:00:33 +01:00
|
|
|
try {
|
|
|
|
return args.length?
|
|
|
|
replacement.call(thisArgs, ...args):
|
|
|
|
replacement.call(thisArgs);
|
|
|
|
}
|
|
|
|
catch (error){
|
2021-06-21 20:41:33 +02:00
|
|
|
try {
|
|
|
|
return original.apply(thisArgs, args);
|
|
|
|
}
|
|
|
|
catch (error){
|
|
|
|
return target.apply(thisArgs, args);
|
|
|
|
}
|
2021-02-21 11:00:33 +01:00
|
|
|
}
|
2021-01-26 13:47:59 +01:00
|
|
|
}, window, "");
|
2021-02-21 11:00:33 +01:00
|
|
|
const proxy = new window.Proxy(original, handler);
|
|
|
|
proxies.set(proxy, original.toString());
|
|
|
|
return scope.getWrapped(proxy);
|
2021-01-26 13:47:59 +01:00
|
|
|
};
|
|
|
|
|
2020-01-23 13:56:14 +01:00
|
|
|
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){
|
2020-01-25 10:04:54 +01:00
|
|
|
const remainingProperties = changedProperties.filter(function(changedProperty){
|
|
|
|
return changedProperty.group !== group;
|
2020-01-23 13:56:14 +01:00
|
|
|
});
|
|
|
|
changedPropertiesByWindow.set(window, remainingProperties);
|
2020-01-25 10:04:54 +01:00
|
|
|
changedProperties = changedProperties.filter(function(changedProperty){
|
|
|
|
return changedProperty.group === group;
|
2020-01-23 13:56:14 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-01-26 01:11:18 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-03-14 16:51:20 +01:00
|
|
|
Object.seal(scope);
|
|
|
|
}());
|