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 function UrlClassifierExceptionListService() {}
9 ChromeUtils.defineModuleGetter(
12 "resource://services-settings/remote-settings.js"
15 const COLLECTION_NAME = "url-classifier-skip-urls";
18 constructor(name, prefName) {
20 this.prefName = prefName;
21 this.observers = new Set();
22 this.prefValue = null;
23 this.remoteEntries = null;
26 this.prefValue = Services.prefs.getStringPref(this.prefName, null);
27 Services.prefs.addObserver(prefName, this);
31 async addAndRunObserver(observer) {
32 this.observers.add(observer);
33 this.notifyObservers(observer);
36 removeObserver(observer) {
37 this.observers.delete(observer);
40 observe(subject, topic, data) {
41 if (topic != "nsPref:changed" || data != this.prefName) {
42 console.error(`Unexpected event ${topic} with ${data}`);
46 this.prefValue = Services.prefs.getStringPref(this.prefName, null);
47 this.notifyObservers();
50 onRemoteSettingsUpdate(entries) {
51 this.remoteEntries = [];
53 for (let entry of entries) {
54 if (entry.feature == this.name) {
55 this.remoteEntries.push(entry.pattern.toLowerCase());
60 notifyObservers(observer = null) {
63 entries = this.prefValue.split(",");
66 if (this.remoteEntries) {
67 for (let entry of this.remoteEntries) {
72 let entriesAsString = entries.join(",").toLowerCase();
74 observer.onExceptionListUpdate(entriesAsString);
76 for (let obs of this.observers) {
77 obs.onExceptionListUpdate(entriesAsString);
83 UrlClassifierExceptionListService.prototype = {
84 classID: Components.ID("{b9f4fd03-9d87-4bfd-9958-85a821750ddc}"),
85 QueryInterface: ChromeUtils.generateQI([
86 "nsIUrlClassifierExceptionListService",
93 if (this._initialized) {
97 let rs = lazy.RemoteSettings(COLLECTION_NAME);
98 rs.on("sync", event => {
102 this.entries = current || [];
103 this.onUpdateEntries(current);
106 this._initialized = true;
108 // If the remote settings list hasn't been populated yet we have to make sure
109 // to do it before firing the first notification.
110 // This has to be run after _initialized is set because we'll be
111 // blocked while getting entries from RemoteSetting, and we don't want
112 // LazyInit is executed again.
114 // The data will be initially available from the local DB (via a
116 this.entries = await rs.get();
119 // RemoteSettings.get() could return null, ensure passing a list to
125 this.onUpdateEntries(this.entries);
128 onUpdateEntries(entries) {
129 for (let key of Object.keys(this.features)) {
130 let feature = this.features[key];
131 feature.onRemoteSettingsUpdate(entries);
132 feature.notifyObservers();
136 registerAndRunExceptionListObserver(feature, prefName, observer) {
137 // We don't await this; the caller is C++ and won't await this function,
138 // and because we prevent re-entering into this method, once it's been
139 // called once any subsequent calls will early-return anyway - so
140 // awaiting that would be meaningless. Instead, `Feature` implementations
141 // make sure not to call into observers until they have data, and we
142 // make sure to let feature instances know whether we have data
146 if (!this.features[feature]) {
147 let featureObj = new Feature(feature, prefName);
148 this.features[feature] = featureObj;
149 // If we've previously initialized, we need to pass the entries
150 // we already have to the new feature.
152 featureObj.onRemoteSettingsUpdate(this.entries);
155 this.features[feature].addAndRunObserver(observer);
158 unregisterExceptionListObserver(feature, observer) {
159 if (!this.features[feature]) {
162 this.features[feature].removeObserver(observer);
167 this._initialized = false;
172 var EXPORTED_SYMBOLS = ["UrlClassifierExceptionListService"];