forked from mirrors/gecko-dev
Backed out changeset 17d4c013ed92 (bug 1817183) Backed out changeset cfed8d9c23f3 (bug 1817183) Backed out changeset 62fe2f589efe (bug 1817182) Backed out changeset 557bd773fb85 (bug 1817179) Backed out changeset 7f8a7865868b (bug 1816934) Backed out changeset d6c1d4c0d2a0 (bug 1816934)
172 lines
4.6 KiB
JavaScript
172 lines
4.6 KiB
JavaScript
/* 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/. */
|
|
|
|
function UrlClassifierExceptionListService() {}
|
|
|
|
const lazy = {};
|
|
|
|
ChromeUtils.defineModuleGetter(
|
|
lazy,
|
|
"RemoteSettings",
|
|
"resource://services-settings/remote-settings.js"
|
|
);
|
|
|
|
const COLLECTION_NAME = "url-classifier-skip-urls";
|
|
|
|
class Feature {
|
|
constructor(name, prefName) {
|
|
this.name = name;
|
|
this.prefName = prefName;
|
|
this.observers = new Set();
|
|
this.prefValue = null;
|
|
this.remoteEntries = null;
|
|
|
|
if (prefName) {
|
|
this.prefValue = Services.prefs.getStringPref(this.prefName, null);
|
|
Services.prefs.addObserver(prefName, this);
|
|
}
|
|
}
|
|
|
|
async addAndRunObserver(observer) {
|
|
this.observers.add(observer);
|
|
this.notifyObservers(observer);
|
|
}
|
|
|
|
removeObserver(observer) {
|
|
this.observers.delete(observer);
|
|
}
|
|
|
|
observe(subject, topic, data) {
|
|
if (topic != "nsPref:changed" || data != this.prefName) {
|
|
console.error(`Unexpected event ${topic} with ${data}`);
|
|
return;
|
|
}
|
|
|
|
this.prefValue = Services.prefs.getStringPref(this.prefName, null);
|
|
this.notifyObservers();
|
|
}
|
|
|
|
onRemoteSettingsUpdate(entries) {
|
|
this.remoteEntries = [];
|
|
|
|
for (let entry of entries) {
|
|
if (entry.feature == this.name) {
|
|
this.remoteEntries.push(entry.pattern.toLowerCase());
|
|
}
|
|
}
|
|
}
|
|
|
|
notifyObservers(observer = null) {
|
|
let entries = [];
|
|
if (this.prefValue) {
|
|
entries = this.prefValue.split(",");
|
|
}
|
|
|
|
if (this.remoteEntries) {
|
|
for (let entry of this.remoteEntries) {
|
|
entries.push(entry);
|
|
}
|
|
}
|
|
|
|
let entriesAsString = entries.join(",").toLowerCase();
|
|
if (observer) {
|
|
observer.onExceptionListUpdate(entriesAsString);
|
|
} else {
|
|
for (let obs of this.observers) {
|
|
obs.onExceptionListUpdate(entriesAsString);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
UrlClassifierExceptionListService.prototype = {
|
|
classID: Components.ID("{b9f4fd03-9d87-4bfd-9958-85a821750ddc}"),
|
|
QueryInterface: ChromeUtils.generateQI([
|
|
"nsIUrlClassifierExceptionListService",
|
|
]),
|
|
|
|
features: {},
|
|
_initialized: false,
|
|
|
|
async lazyInit() {
|
|
if (this._initialized) {
|
|
return;
|
|
}
|
|
|
|
let rs = lazy.RemoteSettings(COLLECTION_NAME);
|
|
rs.on("sync", event => {
|
|
let {
|
|
data: { current },
|
|
} = event;
|
|
this.entries = current || [];
|
|
this.onUpdateEntries(current);
|
|
});
|
|
|
|
this._initialized = true;
|
|
|
|
// If the remote settings list hasn't been populated yet we have to make sure
|
|
// to do it before firing the first notification.
|
|
// This has to be run after _initialized is set because we'll be
|
|
// blocked while getting entries from RemoteSetting, and we don't want
|
|
// LazyInit is executed again.
|
|
try {
|
|
// The data will be initially available from the local DB (via a
|
|
// resource:// URI).
|
|
this.entries = await rs.get();
|
|
} catch (e) {}
|
|
|
|
// RemoteSettings.get() could return null, ensure passing a list to
|
|
// onUpdateEntries.
|
|
if (!this.entries) {
|
|
this.entries = [];
|
|
}
|
|
|
|
this.onUpdateEntries(this.entries);
|
|
},
|
|
|
|
onUpdateEntries(entries) {
|
|
for (let key of Object.keys(this.features)) {
|
|
let feature = this.features[key];
|
|
feature.onRemoteSettingsUpdate(entries);
|
|
feature.notifyObservers();
|
|
}
|
|
},
|
|
|
|
registerAndRunExceptionListObserver(feature, prefName, observer) {
|
|
// We don't await this; the caller is C++ and won't await this function,
|
|
// and because we prevent re-entering into this method, once it's been
|
|
// called once any subsequent calls will early-return anyway - so
|
|
// awaiting that would be meaningless. Instead, `Feature` implementations
|
|
// make sure not to call into observers until they have data, and we
|
|
// make sure to let feature instances know whether we have data
|
|
// immediately.
|
|
this.lazyInit();
|
|
|
|
if (!this.features[feature]) {
|
|
let featureObj = new Feature(feature, prefName);
|
|
this.features[feature] = featureObj;
|
|
// If we've previously initialized, we need to pass the entries
|
|
// we already have to the new feature.
|
|
if (this.entries) {
|
|
featureObj.onRemoteSettingsUpdate(this.entries);
|
|
}
|
|
}
|
|
this.features[feature].addAndRunObserver(observer);
|
|
},
|
|
|
|
unregisterExceptionListObserver(feature, observer) {
|
|
if (!this.features[feature]) {
|
|
return;
|
|
}
|
|
this.features[feature].removeObserver(observer);
|
|
},
|
|
|
|
clear() {
|
|
this.features = {};
|
|
this._initialized = false;
|
|
this.entries = null;
|
|
},
|
|
};
|
|
|
|
var EXPORTED_SYMBOLS = ["UrlClassifierExceptionListService"];
|