/** \file
* \brief Operations and data structures connected to protection levels
*
* \author Copyright (C) 2019-2021 Libor Polcak
* \author Copyright (C) 2019 Martin Timko
* \author Copyright (C) 2021 Matus Svancar
* \author Copyright (C) 2022 Marek Salon
* \author Copyright (C) 2022 Martin Bednar
* \author Copyright (C) 2023 Martin Zmitko
*
* \license SPDX-License-Identifier: GPL-3.0-or-later
*/
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//
/**
* Wrapping groups
*
* Used to control the built-in levels and GUI (e.g. level tweaks).
*/
var wrapping_groups = {
empty_level: { /// Automatically populated
level_text: "",
level_id: "",
level_description: "",
},
group_map: {}, ///Automatically populated
wrapper_map: {}, ///Automatically populated
group_names: [], ///Automatically populated
get_wrappers: function(level) {
wrappers = [];
for (group of wrapping_groups.groups) {
if ((level[group.id] !== undefined) && level[group.id] !== 0) {
let arg_values = group.params[level[group.id] - 1].config;
group.wrappers.forEach((w) => wrappers.push([w, ...arg_values]));
}
}
return wrappers;
},
groups: [
{
name: "time_precision",
label: browser.i18n.getMessage("jssgroupTimePrecision"),
description: browser.i18n.getMessage("jssgroupTimePrecision"),
description2: [browser.i18n.getMessage("jssgroupTimePrecisionDescription2", browser.i18n.getMessage("jssgroupPhysicalLocationGeolocation"))],
params: [
{
short: browser.i18n.getMessage("jssgroupPoor"),
description: browser.i18n.getMessage("jssgroupTimePoorDescription"),
config: [10, false],
},
{
short: browser.i18n.getMessage("jssgroupLow"),
description: browser.i18n.getMessage("jssgroupTimeLowDescription"),
config: [100, false],
},
{
short: browser.i18n.getMessage("jssgroupHigh"),
description: browser.i18n.getMessage("jssgroupTimeHighDescription"),
config: [1000, true],
},
],
wrappers: [
// HRT
"Performance.prototype.now",
// PT2
"PerformanceEntry.prototype",
// ECMA
"window.Date",
// TEMPORAL
"Temporal.Now.instant",
// DOM
"Event.prototype.timeStamp",
// GP
"Gamepad.prototype.timestamp",
// VR
"VRFrameData.prototype.timestamp",
// SENSOR
"Sensor.prototype.timestamp",
],
},
{
name: "htmlcanvaselement",
label: browser.i18n.getMessage("jssgroupLocallyRenderedImages"),
description: browser.i18n.getMessage("jssgroupLocallyRenderedImagesDescription"),
description2: [
browser.i18n.getMessage("jssgroupCanvasLocallyRenderedImagesDescription2"),
browser.i18n.getMessage("jssgroupCanvasLocallyRenderedImagesDescription3")
],
params: [
{
short: browser.i18n.getMessage("jssgroupLittleLies"),
description: browser.i18n.getMessage("jssgroupLocallyRenderedImagesLittleLiesDescription"),
config: [0],
},
{
short: browser.i18n.getMessage("jssgroupStrict"),
description: browser.i18n.getMessage("jssgroupLocallyRenderedImagesStrictDescription"),
config: [1],
},
],
wrappers: [
// H-C
"CanvasRenderingContext2D.prototype.getImageData",
"HTMLCanvasElement.prototype.toBlob",
"HTMLCanvasElement.prototype.toDataURL",
"OffscreenCanvas.prototype.convertToBlob",
"CanvasRenderingContext2D.prototype.isPointInStroke",
"CanvasRenderingContext2D.prototype.isPointInPath",
"WebGLRenderingContext.prototype.readPixels",
"WebGL2RenderingContext.prototype.readPixels",
],
},
{
name: "audiobuffer",
label: browser.i18n.getMessage("jssgroupLocallyGeneratedAudio"),
description: browser.i18n.getMessage("jssgroupLocallyGeneratedAudioDescription"),
description2: [
browser.i18n.getMessage("jssgroupLocallyGeneratedAudioDescription2"),
],
params: [
{
short: browser.i18n.getMessage("jssgroupLittleLies"),
description: browser.i18n.getMessage("jssgroupLocallyGeneratedAudioLittleLiesDescription"),
config: [0],
},
{
short: browser.i18n.getMessage("jssgroupStrict"),
description: browser.i18n.getMessage("jssgroupLocallyGeneratedAudioStrictDescription"),
config: [1],
},
],
wrappers: [
// AUDIO
"AudioBuffer.prototype.getChannelData",
"AudioBuffer.prototype.copyFromChannel",
"AnalyserNode.prototype.getByteTimeDomainData",
"AnalyserNode.prototype.getFloatTimeDomainData",
"AnalyserNode.prototype.getByteFrequencyData",
"AnalyserNode.prototype.getFloatFrequencyData"
],
},
{
name: "webgl",
label: browser.i18n.getMessage("jssgroupGraphicCardInformation"),
description: browser.i18n.getMessage("jssgroupGraphicCardInformationDescription"),
description2: [
browser.i18n.getMessage("jssgroupGraphicCardInformationDescription2"),
browser.i18n.getMessage("jssgroupGraphicCardInformationDescription3"),
],
params: [
{
short: browser.i18n.getMessage("jssgroupLittleLies"),
description: browser.i18n.getMessage("jssgroupGraphicCardInformationLittleLiesDescription"),
config: [0],
},
{
short: browser.i18n.getMessage("jssgroupStrict"),
description: browser.i18n.getMessage("jssgroupGraphicCardInformationStrictDescription"),
config: [1],
},
],
wrappers: [
// WEBGL
"WebGLRenderingContext.prototype.getParameter",
"WebGL2RenderingContext.prototype.getParameter",
"WebGLRenderingContext.prototype.getFramebufferAttachmentParameter",
"WebGL2RenderingContext.prototype.getFramebufferAttachmentParameter",
"WebGLRenderingContext.prototype.getActiveAttrib",
"WebGL2RenderingContext.prototype.getActiveAttrib",
"WebGLRenderingContext.prototype.getActiveUniform",
"WebGL2RenderingContext.prototype.getActiveUniform",
"WebGLRenderingContext.prototype.getAttribLocation",
"WebGL2RenderingContext.prototype.getAttribLocation",
"WebGLRenderingContext.prototype.getBufferParameter",
"WebGL2RenderingContext.prototype.getBufferParameter",
"WebGLRenderingContext.prototype.getProgramParameter",
"WebGL2RenderingContext.prototype.getProgramParameter",
"WebGLRenderingContext.prototype.getRenderbufferParameter",
"WebGL2RenderingContext.prototype.getRenderbufferParameter",
"WebGLRenderingContext.prototype.getShaderParameter",
"WebGL2RenderingContext.prototype.getShaderParameter",
"WebGLRenderingContext.prototype.getShaderPrecisionFormat",
"WebGL2RenderingContext.prototype.getShaderPrecisionFormat",
"WebGLRenderingContext.prototype.getTexParameter",
"WebGL2RenderingContext.prototype.getTexParameter",
"WebGLRenderingContext.prototype.getUniformLocation",
"WebGL2RenderingContext.prototype.getUniformLocation",
"WebGLRenderingContext.prototype.getVertexAttribOffset",
"WebGL2RenderingContext.prototype.getVertexAttribOffset",
"WebGLRenderingContext.prototype.getSupportedExtensions",
"WebGL2RenderingContext.prototype.getSupportedExtensions",
"WebGLRenderingContext.prototype.getExtension",
"WebGL2RenderingContext.prototype.getExtension",
],
},
{
name: "plugins",
label: browser.i18n.getMessage("jssgroupInstalledBrowserPlugins"),
description: browser.i18n.getMessage("jssgroupInstalledBrowserPluginsDescription"),
description2: [browser.i18n.getMessage("jssgroupInstalledBrowserPluginsDescription2")],
params: [
{
short: browser.i18n.getMessage("jssgroupLittleLies"),
description: browser.i18n.getMessage("jssgroupInstalledBrowserPluginsLittleLiesDescription"),
config: [0],
},
{
short: browser.i18n.getMessage("jssgroupFake"),
description: browser.i18n.getMessage("jssgroupInstalledBrowserPluginsFakeDescription"),
config: [1],
},
{
short: browser.i18n.getMessage("jssgroupStrict"),
description: browser.i18n.getMessage("jssgroupInstalledBrowserPluginsStrictDescription"),
config: [2],
},
],
wrappers: [
// NP
"Navigator.prototype.plugins", // also modifies "Navigator.prototype.mimeTypes",
],
},
{
name: "enumerateDevices",
label: browser.i18n.getMessage("jssgroupConnectedCamerasAndMicrophones"),
description: browser.i18n.getMessage("jssgroupConnectedCamerasAndMicrophonesDescription"),
description2: [
browser.i18n.getMessage("jssgroupConnectedCamerasAndMicrophonesDescription2"),
],
params: [
{
short: browser.i18n.getMessage("jssgroupLittleLies"),
description: browser.i18n.getMessage("jssgroupConnectedCamerasAndMicrophonesLittleLiesDescription"),
config: [0],
},
{
short: browser.i18n.getMessage("jssgroupAddFake"),
description: browser.i18n.getMessage("jssgroupConnectedCamerasAndMicrophonesAddFakeDescription"),
config: [1],
},
{
short: browser.i18n.getMessage("jssgroupStrict"),
description: browser.i18n.getMessage("jssgroupConnectedCamerasAndMicrophonesStrictDescription"),
config: [2],
},
],
wrappers: [
// MCS
"MediaDevices.prototype.enumerateDevices",
],
},
{
name: "hardware",
label: browser.i18n.getMessage("jssgroupHardware"),
description: browser.i18n.getMessage("jssgroupHardwareDescription"),
description2: [
browser.i18n.getMessage("jssgroupHardwareDescription2"),
],
params: [
{
short: browser.i18n.getMessage("jssgroupLow"),
description: browser.i18n.getMessage("jssgroupHardwareLowDescription"),
config: [0],
},
{
short: browser.i18n.getMessage("jssgroupMedium"),
description: browser.i18n.getMessage("jssgroupHardwareMediumDescription"),
config: [1],
},
{
short: browser.i18n.getMessage("jssgroupHigh"),
description: browser.i18n.getMessage("jssgroupHardwareHighDescription"),
config: [2],
},
],
wrappers: [
// HTML-LS
"Navigator.prototype.hardwareConcurrency",
// DM
"Navigator.prototype.deviceMemory",
],
},
{
name: "net",
label: browser.i18n.getMessage("jssgroupNetworkConditions"),
description: browser.i18n.getMessage("jssgroupNetworkConditionsDescription"),
description2: [],
params: [
{
short: browser.i18n.getMessage("jssgroupRemove"),
description: browser.i18n.getMessage("jssgroupNetworkConditionsRemoveDescription"),
config: [0],
},
],
wrappers: [
// NET
"Navigator.prototype.connection",
"window.NetworkInformation",
],
},
{
name: "xhr",
label: browser.i18n.getMessage("jssgroupXMLHttpRequestRequests"),
description: browser.i18n.getMessage("jssgroupXMLHttpRequestRequestsDescription"),
description2: [browser.i18n.getMessage("jssgroupXMLHttpRequestRequestsDescription2")],
params: [
{
short: browser.i18n.getMessage("jssgroupAsk"),
description: browser.i18n.getMessage("jssgroupXMLHttpRequestRequestsAskDescription"),
config: [false, true],
},
{
short: browser.i18n.getMessage("jssgroupBlock"),
description: browser.i18n.getMessage("jssgroupXMLHttpRequestRequestsBlockDescription"),
config: [true, false],
},
],
wrappers: [
// AJAX
"XMLHttpRequest.prototype.open",
"XMLHttpRequest.prototype.send",
],
},
{
name: "arrays",
label: browser.i18n.getMessage("jssgroupArrays"),
description: browser.i18n.getMessage("jssgroupArraysDescription"),
description2: [],
params: [
{
short: browser.i18n.getMessage("jssgroupArraysShift"),
description: browser.i18n.getMessage("jssgroupArraysShiftDescription"),
config: [false],
},
{
short: browser.i18n.getMessage("jssgroupArraysRandomize"),
description: browser.i18n.getMessage("jssgroupArraysRandomizeDescription"),
config: [true],
},
],
wrappers: [
"window.DataView",
"window.Uint8Array",
"window.Int8Array",
"window.Uint8ClampedArray",
"window.Int16Array",
"window.Uint16Array",
"window.Int32Array",
"window.Uint32Array",
"window.Float32Array",
"window.Float64Array",
'window.BigInt64Array',
'window.BigUint64Array',
],
},
{
name: "shared_array",
label: browser.i18n.getMessage("jssgroupSharedArraysBuffer"),
description: browser.i18n.getMessage("jssgroupSharedArraysBufferDescription"),
description2: [],
params: [
{
short: browser.i18n.getMessage("jssgroupMedium"),
description: browser.i18n.getMessage("jssgroupSharedArraysBufferMediumDescription"),
config: [false],
},
{
short: browser.i18n.getMessage("jssgroupStrict"),
description: browser.i18n.getMessage("jssgroupSharedArraysBufferStrictDescription"),
config: [true],
},
],
wrappers: [
// SHARED
"window.SharedArrayBuffer"
],
},
{
name: "webworker",
label: browser.i18n.getMessage("jssgroupWebWorker"),
description: browser.i18n.getMessage("jssgroupWebWorkerDescription"),
description2: [browser.i18n.getMessage("jssgroupWebWorkerDescription2")],
params: [
{
short: browser.i18n.getMessage("jssgroupLow"),
description: browser.i18n.getMessage("jssgroupWebWorkerLowDescription"),
config: [false, false],
},
{
short: browser.i18n.getMessage("jssgroupStrict"),
description: browser.i18n.getMessage("jssgroupWebWorkerStrictDescription"),
config: [true, false],
},
{
short: browser.i18n.getMessage("jssgroupRemove"),
description: browser.i18n.getMessage("jssgroupWebWorkerRemoveDescription"),
config: [false, true],
},
],
wrappers: [
"window.Worker",
],
},
{
name: "geolocation",
label: browser.i18n.getMessage("jssgroupPhysicalLocationGeolocation"),
description: browser.i18n.getMessage("jssgroupPhysicalLocationGeolocationDescription"),
description2: [browser.i18n.getMessage("jssgroupPhysicalLocationGeolocationDescription2")],
params: [
{
short: browser.i18n.getMessage("jssgroupGeolocationTimestampOnly"),
description: browser.i18n.getMessage("jssgroupGeolocationTimestampOnlyDescription", browser.i18n.getMessage("jssgroupTimePrecision")),
config: [-1],
},
{
short: browser.i18n.getMessage("jssgroupGeolocationVillage"),
description: browser.i18n.getMessage("jssgroupGeolocationVillageDescription"),
config: [2],
},
{
short: browser.i18n.getMessage("jssgroupGeolocationTown"),
description: browser.i18n.getMessage("jssgroupGeolocationTownDescription"),
config: [3],
},
{
short: browser.i18n.getMessage("jssgroupGeolocationRegion"),
description: browser.i18n.getMessage("jssgroupGeolocationRegionDescription"),
config: [4],
},
{
short: browser.i18n.getMessage("jssgroupGeolocationLongDistance"),
description: browser.i18n.getMessage("jssgroupGeolocationLongDistanceDescription"),
config: [5],
},
{
short: browser.i18n.getMessage("jssgroupRemove"),
description: browser.i18n.getMessage("jssgroupGeolocationRemoveDescription"),
config: [0],
},
],
wrappers: [
// GPS
"Navigator.prototype.geolocation",
"window.Geolocation",
"window.GeolocationCoordinates",
"window.GeolocationPosition",
"window.GeolocationPositionError",
"Geolocation.prototype.getCurrentPosition",
"Geolocation.prototype.watchPosition",
"Geolocation.prototype.clearWatch"
],
},
{
name: "physical_environment",
label: browser.i18n.getMessage("jssgroupPhysicalEnvironmentSensors"),
description: browser.i18n.getMessage("jssgroupPhysicalEnvironmentSensorsDescription"),
description2: [browser.i18n.getMessage("jssgroupPhysicalEnvironmentSensorsDescription2")],
params: [
{
short: browser.i18n.getMessage("jssgroupHigh"),
description: browser.i18n.getMessage("jssgroupPhysicalEnvironmentSensorsHighDescription"),
config: [true],
},
],
wrappers: [
// GENERIC SENSOR API Sensors
// Magnetometer
"Magnetometer.prototype.x",
"Magnetometer.prototype.y",
"Magnetometer.prototype.z",
// Accelerometer, LinearAccelerationSensor, and GravitySensor
"Accelerometer.prototype.x",
"Accelerometer.prototype.y",
"Accelerometer.prototype.z",
// Gyroscope
"Gyroscope.prototype.x",
"Gyroscope.prototype.y",
"Gyroscope.prototype.z",
// AbsoluteOrientationSensor and RelativeOrientationSensor
"OrientationSensor.prototype.quaternion",
// AmbientLightSensor
"AmbientLightSensor.prototype.illuminance"
],
},
{
name: "useridle",
label: browser.i18n.getMessage("jssgroupUserIdleDetection"),
description: browser.i18n.getMessage("jssgroupUserIdleDetectionDescription"),
description2: [browser.i18n.getMessage("jssgroupUserIdleDetectionDescription2")],
params: [
{
short: browser.i18n.getMessage("jssgroupConfuse"),
description: browser.i18n.getMessage("jssgroupConfuseDescription"),
config: [0],
},
{
short: browser.i18n.getMessage("jssgroupBlock"),
description: browser.i18n.getMessage("jssgroupBlockDescription"),
config: [1],
},
{
short: browser.i18n.getMessage("jssgroupRemove"),
description: browser.i18n.getMessage("jssgroupRemoveDescription"),
config: [2],
},
],
wrappers: [
// COOP-SCHEDULING
"window.IdleDetector",
"IdleDetector.requestPermission",
"IdleDetector.prototype.screenState",
"IdleDetector.prototype.userState",
],
},
{
name: "coopschedule",
label: browser.i18n.getMessage("jssgroupCoopschedule"),
description: browser.i18n.getMessage("jssgroupCoopscheduleDescription"),
description2: [browser.i18n.getMessage("jssgroupCoopscheduleDescription2")],
params: [
{
short: browser.i18n.getMessage("jssgroupConfuse"),
description: browser.i18n.getMessage("jssgroupCoopscheduleConfuseDescription"),
config: [],
},
],
wrappers: [
// COOP-SCHEDULING
"IdleDeadline.prototype.didTimeout",
"IdleDeadline.prototype.timeRemaining",
],
},
{
name: "gamepads",
label: browser.i18n.getMessage("jssgroupGamepads"),
description: browser.i18n.getMessage("jssgroupGamepadsDescription"),
description2: [],
params: [
{
short: browser.i18n.getMessage("jssgroupStrict"),
description: browser.i18n.getMessage("jssgroupGamepadsStrictDescription"),
config: [true],
},
],
wrappers: [
// GAMEPAD
"Navigator.prototype.getGamepads",
],
},
{
name: "vr",
label: browser.i18n.getMessage("jssgroupVirtualAndAugmentedRealityDevices"),
description: browser.i18n.getMessage("jssgroupVirtualAndAugmentedRealityDevicesDescription"),
description2: [browser.i18n.getMessage("jssgroupVirtualAndAugmentedRealityDevicesDescription2")],
params: [
{
short: browser.i18n.getMessage("jssgroupStrict"),
description: browser.i18n.getMessage("jssgroupVirtualAndAugmentedRealityDevicesStrictDescription"),
config: [],
},
],
wrappers: [
// VR
"Navigator.prototype.activeVRDisplays",
// XR
"Navigator.prototype.xr",
],
},
{
name: "playback",
label: browser.i18n.getMessage("jssgroupMultimediaPlayback"),
description: browser.i18n.getMessage("jssgroupMultimediaPlaybackDescription"),
description2: [browser.i18n.getMessage("jssgroupMultimediaPlaybackDescription2")],
params: [
{
short: browser.i18n.getMessage("jssgroupLittleLies"),
description: browser.i18n.getMessage("jssgroupMultimediaPlaybackLittleLiesDescription"),
config: [0],
},
{
short: browser.i18n.getMessage("jssgroupStrict"),
description: browser.i18n.getMessage("jssgroupMultimediaPlaybackStrictDescription"),
config: [1],
},
{
short: browser.i18n.getMessage("jssgroupBlock"),
description: browser.i18n.getMessage("jssgroupMultimediaPlaybackBlockDescription"),
config: [2],
},
],
wrappers: [
// EME
"Navigator.prototype.requestMediaKeySystemAccess",
// MEDIA-CAPABILITIES
"MediaCapabilities.prototype.encodingInfo",
"MediaCapabilities.prototype.decodingInfo",
// HTML5
"HTMLMediaElement.prototype.canPlayType",
],
},
{
name: "analytics",
label: browser.i18n.getMessage("jssgroupUnreliableTransfersToServerBeacons"),
description: browser.i18n.getMessage("jssgroupUnreliableTransfersToServerBeaconsDescription"),
description2: [browser.i18n.getMessage("jssgroupUnreliableTransfersToServerBeaconsDescription2"), browser.i18n.getMessage("jssgroupUnreliableTransfersToServerBeaconsDescription3")],
params: [
{
short: browser.i18n.getMessage("jssgroupBlock"),
description: browser.i18n.getMessage("jssgroupUnreliableTransfersToServerBeaconsBlockDescription"),
config: [],
},
],
wrappers: [
// BEACON
"Navigator.prototype.sendBeacon",
],
},
{
name: "battery",
label: browser.i18n.getMessage("jssgroupHardwareBattery"),
description: browser.i18n.getMessage("jssgroupHardwareBatteryDescription"),
description2: [],
params: [
{
short: browser.i18n.getMessage("jssgroupRemove"),
description: browser.i18n.getMessage("jssgroupHardwareBatteryRemoveDescription"),
config: [],
},
],
wrappers: [
// BATTERY
"Navigator.prototype.getBattery",
"window.BatteryManager",
],
},
{
name: "windowname",
label: browser.i18n.getMessage("jssgroupPersistentIdentifierOfTheBrowserTab"),
description: browser.i18n.getMessage("jssgroupPersistentIdentifierOfTheBrowserTabDescription"),
description2: [browser.i18n.getMessage("jssgroupPersistentIdentifierOfTheBrowserTabDescription2")],
params: [
{
short: browser.i18n.getMessage("jssgroupStrict"),
description: browser.i18n.getMessage("jssgroupPersistentIdentifierOfTheBrowserTabStrictDescription"),
config: [],
},
],
wrappers: [
// WINDOW-NAME
"window.name",
],
},
{
name: "nfc",
label: browser.i18n.getMessage("jssgroupNFC"),
description: browser.i18n.getMessage("jssgroupNFCDescription"),
description2: [browser.i18n.getMessage("jssgroupNFCDescription2")],
params: [
{
short: browser.i18n.getMessage("jssgroupRemove"),
description: browser.i18n.getMessage("jssgroupNFCRemoveDescription"),
config: [],
},
],
wrappers: [
// BATTERY
"window.NDEFMessage",
"window.NDEFReader",
"window.NDEFRecord",
],
},
{
name: "wasm",
label: browser.i18n.getMessage("jssgroupWASM"),
description: browser.i18n.getMessage("jssgroupWASMDescription"),
description2: [
browser.i18n.getMessage("jssgroupWASMDescription2", [browser.i18n.getMessage("jssgroupLocallyRenderedImages"), browser.i18n.getMessage("jssgroupLocallyGeneratedAudio"), browser.i18n.getMessage("jssgroupLittleLies")]),
browser.i18n.getMessage("jssgroupWASMDescription3"),
],
params: [
{
short: browser.i18n.getMessage("jssgroupWASMDisabled"),
description: browser.i18n.getMessage("jssgroupWASMDisabledDescription"),
config: [0],
},
{
short: browser.i18n.getMessage("jssgroupWASMPassive"),
description: browser.i18n.getMessage("jssgroupWASMPassiveDescription"),
config: [1],
},
{
short: browser.i18n.getMessage("jssgroupWASMActive"),
description: browser.i18n.getMessage("jssgroupWASMActiveDescription"),
description2: [
"jssgroupWASMActiveDescription2","jssgroupWASMActiveDescription3","jssgroupWASMActiveDescription4"
],
config: [2],
},
],
wrappers: [], // Special case with no wrappers, this group is for modifying htmlcanvaselement, webgl and audiobuffer farbling behaviour
},
],
}
var modify_wrapping_groups = modify_wrapping_groups || (() => null); // Give other scripts the possibility to modify the wrapping_groups objects
modify_wrapping_groups();
/**
* Check if the given API is supported by the browser
* @param String to the object which presence to check.
*/
function is_api_undefined(api) {
if (!self.window) return false; // mv3 service worker?
let s = api.split(".");
let last = self;
for (p of s) {
try {
if (last[p] === undefined) {
return true;
}
}
catch (e) {
// We may have encountered something like
// TypeError: 'get startTime' called on an object that does not implement interface PerformanceEntry.
break;
}
last = last[p];
}
return false;
}
/**
* Returns true if all given API wrappers are unsuported.
*/
function are_all_api_unsupported(wrappers) {
for (wrapper of wrappers) {
if (!is_api_undefined(wrapper)) {
return false;
}
}
return true;
}
/// Automatically populate infered metadata in wrapping_groups.
wrapping_groups.groups.forEach(function (group) {
group.id = group.name;
if (!are_all_api_unsupported(group.wrappers) || group.wrappers.length === 0) {
wrapping_groups.group_names.push(group.name);
wrapping_groups.empty_level[group.id] = 0;
}
wrapping_groups.group_map[group.id] = group
for (wrapper_name of group.wrappers) {
wrapping_groups.wrapper_map[wrapper_name] = group.name;
}
});
// *****************************************************************************
// levels of protection
// Level aliases
const L0 = "0";
const L1 = "1";
const L2 = "2";
const L3 = "3";
const L_EXPERIMENTAL = "Experiment"; // Use a long ID so that it is not in conflict with pre0.7 user-defined levels
/// Built-in levels
var level_0 = {
"builtin": true,
"level_id": L0,
"level_text": browser.i18n.getMessage("JSSL0Name"),
"level_description": browser.i18n.getMessage("JSSL0Description"),
};
var level_1 = {
"builtin": true,
"level_id": L1,
"level_text": browser.i18n.getMessage("JSSL1Name"),
"level_description": browser.i18n.getMessage("JSSL1Description"),
"time_precision": 3,
"net": 1,
"webworker": 3,
"geolocation": 3,
"physical_environment": 1,
"useridle": 1,
"coopschedule": 1,
"gamepads": 1,
"vr": 1,
"analytics": 1,
"battery": 1,
"nfc": 1,
};
var level_2 = {
"builtin": true,
"level_id": L2,
"level_text": browser.i18n.getMessage("JSSL2Name"),
"level_description": browser.i18n.getMessage("JSSL2Description"),
"time_precision": 3,
"htmlcanvaselement": 1,
"audiobuffer": 1,
"webgl": 1,
"plugins": 2,
"enumerateDevices": 2,
"hardware": 1,
"net": 1,
"webworker": 3,
"geolocation": 3,
"physical_environment": 1,
"useridle": 2,
"coopschedule": 1,
"gamepads": 1,
"vr": 1,
"analytics": 1,
"battery": 1,
"windowname": 1,
"nfc": 1,
"wasm": 1,
};
var level_3 = {
"builtin": true,
"level_id": L3,
"level_text": browser.i18n.getMessage("JSSL3Name"),
"level_description": browser.i18n.getMessage("JSSL3Description"),
"time_precision": 3,
"htmlcanvaselement": 2,
"audiobuffer": 2,
"webgl": 2,
"plugins": 3,
"enumerateDevices": 3,
"hardware": 3,
"net": 1,
"webworker": 3,
"geolocation": 6,
"physical_environment": 1,
"useridle": 3,
"coopschedule": 1,
"gamepads": 1,
"vr": 1,
"playback": 2,
"analytics": 1,
"battery": 1,
"windowname": 1,
"nfc": 1,
};
var level_experimental = {
"builtin": true,
"level_id": L_EXPERIMENTAL,
"level_text": browser.i18n.getMessage("JSSLexperimentalName"),
"level_description": browser.i18n.getMessage("JSSLexperimentalDescription"),
"time_precision": 3,
"htmlcanvaselement": 2,
"audiobuffer": 2,
"webgl": 2,
"plugins": 3,
"enumerateDevices": 3,
"hardware": 3,
"net": 1,
"xhr": 1,
"arrays": 2,
"shared_array": 2,
"webworker": 2,
"geolocation": 6,
"physical_environment": 1,
"useridle": 3,
"coopschedule": 1,
"gamepads": 1,
"vr": 1,
"playback": 3,
"analytics": 1,
"battery": 1,
"windowname": 1,
"nfc": 1,
};
var modify_builtin_levels = modify_builtin_levels || (() => null); // Give other scripts the possibility to modify builtin levels
modify_builtin_levels();
var levels = {};
var default_level = {};
var domains = {};
function init_levels() {
levels = {
[level_0.level_id]: level_0,
[level_1.level_id]: level_1,
[level_2.level_id]: level_2,
[level_3.level_id]: level_3,
[level_experimental.level_id]: level_experimental
};
default_level = Object.create(levels[L2]);
default_level.level_text = "Default";
domains = {};
}
init_levels();
let levels_initialised = false; // Initialized in updateLevels()
let fp_levels_initialised = false; // Initialized in fp_levels.js/loadFpdConfig()
let levels_updated_callbacks = [];
var tweak_domains = tweak_domains || {};
async function updateLevels(res) {
init_levels();
custom_levels = res["custom_levels"] || {};
for (let key in custom_levels) {
levels[key] = custom_levels[key];
}
for (let key in levels) {
levels[key].wrappers = wrapping_groups.get_wrappers(levels[key]);
}
var new_default_level = res["__default__"];
if (new_default_level === undefined || new_default_level === null || !(new_default_level in levels)) {
default_level = Object.assign({}, levels[L2]);
setDefaultLevel(L2);
}
else {
default_level = Object.assign({}, levels[new_default_level]);
}
default_level.is_default = true;
var redefined_domains = res["domains"] || {};
for (let [d, settings] of Object.entries(tweak_domains)) {
if ((default_level.level_id in settings.level_id) && !(d in redefined_domains)) {
redefined_domains[d] = {level_id: default_level.level_id, tweaks: settings.tweaks};
}
}
for (let [d, {level_id, tweaks, restore, restore_tweaks}] of Object.entries(redefined_domains)) {
let level = levels[level_id];
if (level === undefined) {
domains[d] = default_level;
}
else {
if (tweaks) {
// this domain has "tweaked" wrapper groups from other levels, let's merge them
level = Object.assign({}, level, tweaks);
level.tweaks = tweaks;
delete level.wrappers; // we will lazy instantiate them on demand in getCurrentLevelJSON()
}
if (restore) {
level.restore = restore;
if (restore_tweaks) {
level.restore_tweaks = restore_tweaks;
}
}
}
domains[d] = level;
}
levels_initialised = true;
if (fp_levels_initialised) { // Wait for both levels_initialised and fp_levels_initialised
var orig_levels_updated_callbacks = levels_updated_callbacks;
levels_updated_callbacks = [];
orig_levels_updated_callbacks.forEach((it) => it());
await updateUserScripts();
}
}
browser.storage.sync.get(null).then(updateLevels);
var wrappersPortId = null;
var cachedSiteSettings = null;
async function updateUserScripts() {
if (browser.tabs.executeScript || !("userScripts" in browser) || self.window) {
return;
}
if (!cachedSiteSettings) {
({wrappersPortId, cachedSiteSettings} = await
browser.storage.local.get(["wrappersPortId", "cachedSiteSettings"]));
}
siteSettings = JSON.stringify({domains, fpdWhitelist, fpdOn: fpDetectionOn && fpdSettings.detection});
if (siteSettings == cachedSiteSettings) {
return;
}
cachedSiteSettings = siteSettings;
const confCache = new Map();
const allGlobs = new Set();
const entries = Object.entries(domains);
entries.push(["*", default_level]);
let count = 0;
for (const [domain, l] of entries) {
if (l.tweaks && !l.wrappers) {
l.wrappers = wrapping_groups.get_wrappers(l);
}
const fpdOn = fpDetectionOn && !isFpdWhitelisted(domain);
const confKey = JSON.stringify([l.wrappers, fpdOn]);
let conf = confCache.get(confKey);
const glob = `*.${domain}`;
if (!conf) {
const fpdWrappers = fpdOn ? fp_levels.page_wrappers[fpdSettings.detection] : [];
const injection = fp_assemble_injection(l, fpdWrappers);
conf = {
id: `L:${l.level_id}-FPD:${fpdOn}-C:${count++}`,
injection,
includeGlobs: [glob],
excludeGlobs: [],
};
confCache.set(confKey, conf);
} else {
conf.includeGlobs.push(glob);
}
if (domain == '*') { // default level
conf.excludeGlobs = [... allGlobs].filter(g => !conf.includeGlobs.includes(g));
conf.includeGlobs = [];
conf.matches = [""];
break;
}
allGlobs.add(glob);
const imply = `.${domain}`;
for (const otherConf of confCache.values()) {
if (otherConf === conf) continue;
const otherImplied = otherConf.includeGlobs.filter(g => g !== glob && g.endsWith(imply));
for (const impliedGlob of otherImplied) {
conf.excludeGlobs.push(impliedGlob);
}
const otherImplies = otherConf.includeGlobs.filter(g => glob.endsWith(g.replace('*', '')));
for (const implyingGlob of otherImplies) {
otherConf.excludeGlobs.push(implyingGlob);
}
}
}
const usTemplate = {
runAt: "document_start",
allFrames: true,
world: "MAIN",
};
const globs2matches = globs => [... new Set(globs)].map(g => `*://${g}/*`);
const scripts = [];
for (const conf of confCache.values()) {
const {portId, code} = patchWindow({
portId: wrappersPortId,
code: conf.injection,
});
if (wrappersPortId !== portId) {
wrappersPortId = portId;
}
browser.storage.local.set({cachedSiteSettings, wrappersPortId});
const {id, includeGlobs, excludeGlobs} = conf;
const opts = {
id,
matches: conf.matches || globs2matches(includeGlobs),
excludeMatches: globs2matches(excludeGlobs),
};
const debugCode = `console.info("Injecting wrapper for " + document.URL + ": " + JSON.stringify(${JSON.stringify(opts)}) + ", ${portId}.");`;
opts.js = [{code: debugCode}, {code}];
scripts.push(
Object.assign(opts, usTemplate)
);
}
await browser.userScripts.unregister();
if (scripts.length) {
await browser.userScripts.register(scripts);
}
}
function changedLevels(changed, area) {
if (area !== "sync") return;
browser.storage.sync.get(null).then(updateLevels);
}
browser.storage.onChanged.addListener(changedLevels);
function setDefaultLevel(level) {
browser.storage.sync.set({__default__: level});
}
function saveDomainLevels() {
tobesaved = {};
for (k in domains) {
let {level_id, tweaks, restore, restore_tweaks} = domains[k];
if (k[k.length - 1] === ".") {
k = k.substring(0, k.length-1);
}
// Tweaks not contain the new value defined by the user. However, that might
// be the value of the level. Remove such tweaks as they to not change the
// level of protection
if (tweaks) {
for (let [group, param] of Object.entries(tweaks)) {
if (param === (levels[level_id][group] || 0)) {
delete tweaks[group]; // remove redundant entries
}
}
if (Object.keys(tweaks).length === 0) {
tweaks = undefined;
}
// Do not save built-in tweaks as they are automatically added in updateLevels
if (k in tweak_domains) {
// Skip further check if the user has a different default_level
if (default_level.level_id in tweak_domains[k].level_id) {
tdb_tweaks = Object.entries(tweak_domains[k].tweaks);
current_tweaks = Object.entries(tweaks || {});
if (tdb_tweaks.length === current_tweaks.length) {
var equal = true;
for ([name, val] of tdb_tweaks) {
if (tweaks[name] !== val) {
equal = false;
break;
}
}
if (equal) {
// This entry should not be saved
continue;
}
}
}
}
}
tobesaved[k] = tweaks ? {level_id, tweaks} : {level_id};
if (restore) {
tobesaved[k].restore = restore;
if (restore_tweaks) {
tobesaved[k].restore_tweaks = restore_tweaks;
}
}
}
browser.storage.sync.set({domains: tobesaved});
}
function getCurrentLevelJSON(url) {
var subDomains = extractSubDomains(getEffectiveDomain(url));
for (let domain of subDomains.reverse()) {
if (domain in domains) {
let l = domains[domain];
if (l.tweaks && !l.wrappers) {
l.wrappers = wrapping_groups.get_wrappers(l);
}
return l;
}
}
return default_level;
}
function getTweaksForLevel(level_id, tweaks_obj) {
tweaks_obj = tweaks_obj || {}; // Make sure that tweaks_obj is an object
let working = Object.assign({}, wrapping_groups.empty_level, levels[level_id], tweaks_obj);
Object.keys(working).forEach(function(key) {
if (!wrapping_groups.group_names.includes(key)) {
delete working[key];
}
});
return working;
}