/* 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/. */ "use strict"; /* Unit tests for the nsIUrlClassifierExceptionListService implementation. */ const { RemoteSettings } = ChromeUtils.importESModule( "resource://services-settings/remote-settings.sys.mjs" ); const COLLECTION_NAME = "url-classifier-skip-urls"; const FEATURE_TRACKING_NAME = "tracking-annotation-test"; const FEATURE_TRACKING_PREF_NAME = "urlclassifier.tracking-annotation-test"; const FEATURE_SOCIAL_NAME = "socialtracking-annotation-test"; const FEATURE_SOCIAL_PREF_NAME = "urlclassifier.socialtracking-annotation-test"; const FEATURE_FINGERPRINTING_NAME = "fingerprinting-annotation-test"; const FEATURE_FINGERPRINTING_PREF_NAME = "urlclassifier.fingerprinting-annotation-test"; do_get_profile(); class UpdateEvent extends EventTarget {} function waitForEvent(element, eventName) { return new Promise(function (resolve) { element.addEventListener(eventName, e => resolve(e.detail), { once: true }); }); } add_task(async function test_list_changes() { let exceptionListService = Cc[ "@mozilla.org/url-classifier/exception-list-service;1" ].getService(Ci.nsIUrlClassifierExceptionListService); // Make sure we have a pref initially, since the exception list service // requires it. Services.prefs.setStringPref(FEATURE_TRACKING_PREF_NAME, ""); let updateEvent = new UpdateEvent(); let obs = data => { let event = new CustomEvent("update", { detail: data }); updateEvent.dispatchEvent(event); }; let records = [ { id: "1", last_modified: 1000000000000001, feature: FEATURE_TRACKING_NAME, pattern: "example.com", }, ]; // Add some initial data. let db = RemoteSettings(COLLECTION_NAME).db; await db.importChanges({}, Date.now(), records); let promise = waitForEvent(updateEvent, "update"); exceptionListService.registerAndRunExceptionListObserver( FEATURE_TRACKING_NAME, FEATURE_TRACKING_PREF_NAME, obs ); Assert.equal(await promise, "", "No items in the list"); // Second event is from the RemoteSettings record. let list = await waitForEvent(updateEvent, "update"); Assert.equal(list, "example.com", "Has one item in the list"); records.push( { id: "2", last_modified: 1000000000000002, feature: FEATURE_TRACKING_NAME, pattern: "MOZILLA.ORG", }, { id: "3", last_modified: 1000000000000003, feature: "some-other-feature", pattern: "noinclude.com", }, { last_modified: 1000000000000004, feature: FEATURE_TRACKING_NAME, pattern: "*.example.org", } ); promise = waitForEvent(updateEvent, "update"); await RemoteSettings(COLLECTION_NAME).emit("sync", { data: { current: records }, }); list = await promise; Assert.equal( list, "example.com,mozilla.org,*.example.org", "Has several items in the list" ); promise = waitForEvent(updateEvent, "update"); Services.prefs.setStringPref(FEATURE_TRACKING_PREF_NAME, "test.com"); list = await promise; Assert.equal( list, "test.com,example.com,mozilla.org,*.example.org", "Has several items in the list" ); promise = waitForEvent(updateEvent, "update"); Services.prefs.setStringPref( FEATURE_TRACKING_PREF_NAME, "test.com,whatever.com,*.abc.com" ); list = await promise; Assert.equal( list, "test.com,whatever.com,*.abc.com,example.com,mozilla.org,*.example.org", "Has several items in the list" ); exceptionListService.unregisterExceptionListObserver( FEATURE_TRACKING_NAME, obs ); exceptionListService.clear(); await db.clear(); }); /** * This test make sure when a feature registers itself to exceptionlist service, * it can get the correct initial data. */ add_task(async function test_list_init_data() { let exceptionListService = Cc[ "@mozilla.org/url-classifier/exception-list-service;1" ].getService(Ci.nsIUrlClassifierExceptionListService); // Make sure we have a pref initially, since the exception list service // requires it. Services.prefs.setStringPref(FEATURE_TRACKING_PREF_NAME, ""); let updateEvent = new UpdateEvent(); let records = [ { id: "1", last_modified: 1000000000000001, feature: FEATURE_TRACKING_NAME, pattern: "tracking.example.com", }, { id: "2", last_modified: 1000000000000002, feature: FEATURE_SOCIAL_NAME, pattern: "social.example.com", }, { id: "3", last_modified: 1000000000000003, feature: FEATURE_TRACKING_NAME, pattern: "*.tracking.org", }, { id: "4", last_modified: 1000000000000004, feature: FEATURE_SOCIAL_NAME, pattern: "MOZILLA.ORG", }, ]; // Add some initial data. let db = RemoteSettings(COLLECTION_NAME).db; await db.importChanges({}, Date.now(), records); // The first registered feature make ExceptionListService get the initial data // from remote setting. let promise = waitForEvent(updateEvent, "update"); let obs = data => { let event = new CustomEvent("update", { detail: data }); updateEvent.dispatchEvent(event); }; exceptionListService.registerAndRunExceptionListObserver( FEATURE_TRACKING_NAME, FEATURE_TRACKING_PREF_NAME, obs ); let list = await promise; Assert.equal(list, "", "Empty list initially"); Assert.equal( await waitForEvent(updateEvent, "update"), "tracking.example.com,*.tracking.org", "Has several items in the list" ); // Register another feature after ExceptionListService got the initial data. promise = waitForEvent(updateEvent, "update"); exceptionListService.registerAndRunExceptionListObserver( FEATURE_SOCIAL_NAME, FEATURE_SOCIAL_PREF_NAME, obs ); list = await promise; Assert.equal( list, "social.example.com,mozilla.org", "Has several items in the list" ); // Test registering a feature after ExceptionListService recieved the synced data. records.push( { id: "5", last_modified: 1000000000000002, feature: FEATURE_FINGERPRINTING_NAME, pattern: "fingerprinting.example.com", }, { id: "6", last_modified: 1000000000000002, feature: "other-fature", pattern: "not-a-fingerprinting.example.com", }, { id: "7", last_modified: 1000000000000002, feature: FEATURE_FINGERPRINTING_NAME, pattern: "*.fingerprinting.org", } ); await RemoteSettings(COLLECTION_NAME).emit("sync", { data: { current: records }, }); promise = waitForEvent(updateEvent, "update"); exceptionListService.registerAndRunExceptionListObserver( FEATURE_FINGERPRINTING_NAME, FEATURE_FINGERPRINTING_PREF_NAME, obs ); list = await promise; Assert.equal( list, "fingerprinting.example.com,*.fingerprinting.org", "Has several items in the list" ); exceptionListService.unregisterExceptionListObserver( FEATURE_TRACKING_NAME, obs ); exceptionListService.unregisterExceptionListObserver( FEATURE_SOCIAL_NAME, obs ); exceptionListService.unregisterExceptionListObserver( FEATURE_FINGERPRINTING_NAME, obs ); exceptionListService.clear(); await db.clear(); });