Bug 1809851 - Add wpt for overflow:overlay as overflow:auto, r=emilio
[gecko.git] / netwerk / url-classifier / UrlClassifierExceptionListService.jsm
blob29e3bf13d1e3ab3f17d797982528c63c8d864708
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() {}
7 const lazy = {};
9 ChromeUtils.defineModuleGetter(
10   lazy,
11   "RemoteSettings",
12   "resource://services-settings/remote-settings.js"
15 const COLLECTION_NAME = "url-classifier-skip-urls";
17 class Feature {
18   constructor(name, prefName) {
19     this.name = name;
20     this.prefName = prefName;
21     this.observers = new Set();
22     this.prefValue = null;
23     this.remoteEntries = null;
25     if (prefName) {
26       this.prefValue = Services.prefs.getStringPref(this.prefName, null);
27       Services.prefs.addObserver(prefName, this);
28     }
29   }
31   async addAndRunObserver(observer) {
32     this.observers.add(observer);
33     this.notifyObservers(observer);
34   }
36   removeObserver(observer) {
37     this.observers.delete(observer);
38   }
40   observe(subject, topic, data) {
41     if (topic != "nsPref:changed" || data != this.prefName) {
42       console.error(`Unexpected event ${topic} with ${data}`);
43       return;
44     }
46     this.prefValue = Services.prefs.getStringPref(this.prefName, null);
47     this.notifyObservers();
48   }
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());
56       }
57     }
58   }
60   notifyObservers(observer = null) {
61     let entries = [];
62     if (this.prefValue) {
63       entries = this.prefValue.split(",");
64     }
66     if (this.remoteEntries) {
67       for (let entry of this.remoteEntries) {
68         entries.push(entry);
69       }
70     }
72     let entriesAsString = entries.join(",").toLowerCase();
73     if (observer) {
74       observer.onExceptionListUpdate(entriesAsString);
75     } else {
76       for (let obs of this.observers) {
77         obs.onExceptionListUpdate(entriesAsString);
78       }
79     }
80   }
83 UrlClassifierExceptionListService.prototype = {
84   classID: Components.ID("{b9f4fd03-9d87-4bfd-9958-85a821750ddc}"),
85   QueryInterface: ChromeUtils.generateQI([
86     "nsIUrlClassifierExceptionListService",
87   ]),
89   features: {},
90   _initialized: false,
92   async lazyInit() {
93     if (this._initialized) {
94       return;
95     }
97     let rs = lazy.RemoteSettings(COLLECTION_NAME);
98     rs.on("sync", event => {
99       let {
100         data: { current },
101       } = event;
102       this.entries = current || [];
103       this.onUpdateEntries(current);
104     });
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.
113     try {
114       // The data will be initially available from the local DB (via a
115       // resource:// URI).
116       this.entries = await rs.get();
117     } catch (e) {}
119     // RemoteSettings.get() could return null, ensure passing a list to
120     // onUpdateEntries.
121     if (!this.entries) {
122       this.entries = [];
123     }
125     this.onUpdateEntries(this.entries);
126   },
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();
133     }
134   },
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
143     // immediately.
144     this.lazyInit();
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.
151       if (this.entries) {
152         featureObj.onRemoteSettingsUpdate(this.entries);
153       }
154     }
155     this.features[feature].addAndRunObserver(observer);
156   },
158   unregisterExceptionListObserver(feature, observer) {
159     if (!this.features[feature]) {
160       return;
161     }
162     this.features[feature].removeObserver(observer);
163   },
165   clear() {
166     this.features = {};
167     this._initialized = false;
168     this.entries = null;
169   },
172 var EXPORTED_SYMBOLS = ["UrlClassifierExceptionListService"];