// -*- indent-tabs-mode: nil; js-indent-level: 2 -*- /* 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 https://mozilla.org/MPL/2.0/. */ const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { JsonSchema: "resource://gre/modules/JsonSchema.sys.mjs", RemoteSettings: "resource://services-settings/remote-settings.sys.mjs", }); import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; ChromeUtils.defineLazyGetter(lazy, "logConsole", () => { return console.createInstance({ prefix: "FingerprintingWebCompatService", maxLogLevelPref: "privacy.fingerprintingProtection.WebCompatService.logLevel", }); }); const SCHEMA = `{ "type": "object", "title": "Fingerprinting Overrides", "$schema": "http://json-schema.org/draft-07/schema#", "required": [ "firstPartyDomain", "overrides" ], "properties": { "overrides": { "type": "string", "pattern": "^[+-][A-Za-z]+(?:,[+-][A-Za-z]+)*$", "description": "The fingerprinting overrides. See https://searchfox.org/mozilla-central/source/toolkit/components/resistfingerprinting/RFPTargets.inc for details." }, "firstPartyDomain": { "type": "string", "pattern": "^(\\*|(?!-)[A-Za-z0-9-]{1,63}(? { let { data: { current }, } = event; this.#onRemoteUpdate(current); this.#populateOverrides(); }); // Get the remote overrides from the remote settings. await this.#importRemoteSettingsOverrides(); // Get the granular overrides from the pref. this.#importPrefOverrides(); // Populate the overrides to the nsRFPService. this.#populateOverrides(); lazy.logConsole.debug("Init completes"); } // Import fingerprinting overrides from the local granular pref. #importPrefOverrides() { lazy.logConsole.debug("importLocalGranularOverrides"); // Clear overrides before we update. this.#granularOverrides.clear(); let overrides; try { overrides = JSON.parse(lazy.granularOverridesPref || "[]"); } catch (error) { lazy.logConsole.error( `Failed to parse granular override JSON string: Not a valid JSON.`, error ); return; } // Ensure we have an array we can iterate over and not an object. if (!Array.isArray(overrides)) { lazy.logConsole.error( "Failed to parse granular overrides JSON String: Not an array." ); return; } for (let override of overrides) { // Validate the override. let { valid, errors } = this.#validator.validate(override); if (!valid) { lazy.logConsole.debug("Override validation error", override, errors); continue; } this.#granularOverrides.add( this.#createFingerprintingOverrideFrom(override) ); } } // Import fingerprinting overrides from the remote settings. async #importRemoteSettingsOverrides() { lazy.logConsole.debug("importRemoteSettingsOverrides"); let entries; try { entries = await this.#rs.get(); } catch (e) {} this.#onRemoteUpdate(entries || []); } #onRemoteUpdate(entries) { lazy.logConsole.debug("onUpdateEntries", { entries }); // Clear all overrides before we update the overrides. this.#remoteOverrides.clear(); for (let entry of entries) { this.#remoteOverrides.add(this.#createFingerprintingOverrideFrom(entry)); } } #createFingerprintingOverrideFrom(entry) { return new FingerprintingOverride( entry.firstPartyDomain, entry.thirdPartyDomain, entry.overrides ); } #populateOverrides() { lazy.logConsole.debug("populateOverrides"); // Create the array that contains all overrides. We explicitly concat the // overrides from testing pref after the ones from remote settings to ensure // that the testing pref will take precedence. let overrides = Array.from(this.#remoteOverrides).concat( Array.from(this.#granularOverrides) ); // Set the remote override to the RFP service. Services.rfp.setFingerprintingOverrides(Array.from(overrides)); } observe(subject, topic, prefName) { if (prefName != PREF_GRANULAR_OVERRIDES) { return; } this.#importPrefOverrides(); this.#populateOverrides(); } shutdown() { lazy.logConsole.debug("shutdown"); Services.prefs.removeObserver(PREF_GRANULAR_OVERRIDES, this); } }