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 import { RemoteSettings } from "resource://services-settings/remote-settings.sys.mjs";
7 const FileUtils = ChromeUtils.importESModule(
8 "resource://gre/modules/FileUtils.sys.mjs"
11 const RECORD_ID = "tld-dafsa";
12 const SIGNAL = "public-suffix-list-updated";
14 export const PublicSuffixList = {
15 CLIENT: RemoteSettings("public-suffix-list"),
18 // Only initialize once.
19 if (this._initialized) {
22 this._initialized = true;
24 this.CLIENT.on("sync", this.onUpdate.bind(this));
25 /* We have a single record for this collection. Let's see if we already have it locally.
26 * Note that on startup, we don't need to synchronize immediately on new profiles.
28 this.CLIENT.get({ syncIfEmpty: false, filters: { id: RECORD_ID } })
29 .then(async records => {
30 if (records.length == 1) {
31 // Get the downloaded file URI (most likely to be a no-op here, since file will exist).
32 const fileURI = await this.CLIENT.attachments.downloadToDisk(
35 // Send a signal so that the C++ code loads the updated list on startup.
36 this.notifyUpdate(fileURI);
39 .catch(err => console.error(err));
43 * This method returns the path to the file based on the file uri received
45 * On windows "file://C:/Users/AppData/main/public-suffix-list/dafsa.bin"
46 * will be converted to "C:\\Users\\main\\public-suffix-list\\dafsa.bin
48 * On macOS/linux "file:///home/main/public-suffix-list/dafsa.bin"
49 * will be converted to "/home/main/public-suffix-list/dafsa.bin"
51 getFilePath(fileURI) {
52 const uri = Services.io.newURI(fileURI);
53 const file = uri.QueryInterface(Ci.nsIFileURL).file;
57 notifyUpdate(fileURI) {
58 if (!Services.prefs.getBoolPref("network.psl.onUpdate_notify", false)) {
59 // Updating the PSL while Firefox is running could cause principals to
60 // have a different base domain before/after the update.
61 // See bug 1582647 comment 30
65 const filePath = this.getFilePath(fileURI);
66 const nsifile = new FileUtils.File(filePath);
67 /* Send a signal to be read by the C++, the method
68 * ::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
69 * at netwerk/dns/nsEffectiveTLDService.cpp
71 Services.obs.notifyObservers(
78 async onUpdate({ data: { created, updated, deleted } }) {
79 // In theory, this will never happen, we will never delete the record.
80 if (deleted.length == 1) {
81 await this.CLIENT.attachments.deleteFromDisk(deleted[0]);
83 // Handle creation and update the same way
84 const changed = created.concat(updated.map(u => u.new));
85 /* In theory, we should never have more than one record. And if we receive
86 * this event, it's because the single record was updated.
88 if (changed.length != 1) {
89 console.warn("Unsupported sync event for Public Suffix List");
92 // Download the updated file.
95 fileURI = await this.CLIENT.attachments.downloadToDisk(changed[0]);
101 // Notify the C++ part to reload it from disk.
102 this.notifyUpdate(fileURI);