1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 export function UrlClassifierExceptionListService() {}
9 ChromeUtils.defineESModuleGetters(lazy, {
10 RemoteSettings: "resource://services-settings/remote-settings.sys.mjs",
13 const COLLECTION_NAME = "url-classifier-skip-urls";
16 constructor(name, prefName) {
18 this.prefName = prefName;
19 this.observers = new Set();
20 this.prefValue = null;
21 this.remoteEntries = null;
24 this.prefValue = Services.prefs.getStringPref(this.prefName, null);
25 Services.prefs.addObserver(prefName, this);
29 async addAndRunObserver(observer) {
30 this.observers.add(observer);
31 this.notifyObservers(observer);
34 removeObserver(observer) {
35 this.observers.delete(observer);
38 observe(subject, topic, data) {
39 if (topic != "nsPref:changed" || data != this.prefName) {
40 console.error(`Unexpected event ${topic} with ${data}`);
44 this.prefValue = Services.prefs.getStringPref(this.prefName, null);
45 this.notifyObservers();
48 onRemoteSettingsUpdate(entries) {
49 this.remoteEntries = [];
51 for (let entry of entries) {
52 if (entry.feature == this.name) {
53 this.remoteEntries.push(entry.pattern.toLowerCase());
58 notifyObservers(observer = null) {
61 entries = this.prefValue.split(",");
64 if (this.remoteEntries) {
65 for (let entry of this.remoteEntries) {
70 let entriesAsString = entries.join(",").toLowerCase();
72 observer.onExceptionListUpdate(entriesAsString);
74 for (let obs of this.observers) {
75 obs.onExceptionListUpdate(entriesAsString);
81 UrlClassifierExceptionListService.prototype = {
82 classID: Components.ID("{b9f4fd03-9d87-4bfd-9958-85a821750ddc}"),
83 QueryInterface: ChromeUtils.generateQI([
84 "nsIUrlClassifierExceptionListService",
91 if (this._initialized) {
95 let rs = lazy.RemoteSettings(COLLECTION_NAME);
96 rs.on("sync", event => {
100 this.entries = current || [];
101 this.onUpdateEntries(current);
104 this._initialized = true;
106 // If the remote settings list hasn't been populated yet we have to make sure
107 // to do it before firing the first notification.
108 // This has to be run after _initialized is set because we'll be
109 // blocked while getting entries from RemoteSetting, and we don't want
110 // LazyInit is executed again.
112 // The data will be initially available from the local DB (via a
114 this.entries = await rs.get();
117 // RemoteSettings.get() could return null, ensure passing a list to
123 this.onUpdateEntries(this.entries);
126 onUpdateEntries(entries) {
127 for (let key of Object.keys(this.features)) {
128 let feature = this.features[key];
129 feature.onRemoteSettingsUpdate(entries);
130 feature.notifyObservers();
134 registerAndRunExceptionListObserver(feature, prefName, observer) {
135 // We don't await this; the caller is C++ and won't await this function,
136 // and because we prevent re-entering into this method, once it's been
137 // called once any subsequent calls will early-return anyway - so
138 // awaiting that would be meaningless. Instead, `Feature` implementations
139 // make sure not to call into observers until they have data, and we
140 // make sure to let feature instances know whether we have data
144 if (!this.features[feature]) {
145 let featureObj = new Feature(feature, prefName);
146 this.features[feature] = featureObj;
147 // If we've previously initialized, we need to pass the entries
148 // we already have to the new feature.
150 featureObj.onRemoteSettingsUpdate(this.entries);
153 this.features[feature].addAndRunObserver(observer);
156 unregisterExceptionListObserver(feature, observer) {
157 if (!this.features[feature]) {
160 this.features[feature].removeObserver(observer);
165 this._initialized = false;