feat: remotesettings disable, use offline dumps
This commit is contained in:
@@ -1401,6 +1401,11 @@ BrowserGlue.prototype = {
|
|||||||
lazy.RemoteSecuritySettings.init();
|
lazy.RemoteSecuritySettings.init();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
function RemoteSettingsPollChanges() {
|
||||||
|
// Support clients that use the "sync" event or "remote-settings:changes-poll-end".
|
||||||
|
lazy.RemoteSettings.pollChanges({ trigger: "timer" });
|
||||||
|
},
|
||||||
|
|
||||||
function searchBackgroundChecks() {
|
function searchBackgroundChecks() {
|
||||||
Services.search.runBackgroundChecks();
|
Services.search.runBackgroundChecks();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ ChromeUtils.defineESModuleGetters(this, {
|
|||||||
InitializationTracker: "resource://gre/modules/GeckoViewTelemetry.sys.mjs",
|
InitializationTracker: "resource://gre/modules/GeckoViewTelemetry.sys.mjs",
|
||||||
RemoteSecuritySettings:
|
RemoteSecuritySettings:
|
||||||
"resource://gre/modules/psm/RemoteSecuritySettings.sys.mjs",
|
"resource://gre/modules/psm/RemoteSecuritySettings.sys.mjs",
|
||||||
|
RemoteSettings: "resource://services-settings/remote-settings.sys.mjs",
|
||||||
SafeBrowsing: "resource://gre/modules/SafeBrowsing.sys.mjs",
|
SafeBrowsing: "resource://gre/modules/SafeBrowsing.sys.mjs",
|
||||||
CaptchaDetectionPingUtils:
|
CaptchaDetectionPingUtils:
|
||||||
"resource://gre/modules/CaptchaDetectionPingUtils.sys.mjs",
|
"resource://gre/modules/CaptchaDetectionPingUtils.sys.mjs",
|
||||||
@@ -928,6 +929,8 @@ function startup() {
|
|||||||
// submits the ping if it has data and has been about 24 hours since the
|
// submits the ping if it has data and has been about 24 hours since the
|
||||||
// last submission.
|
// last submission.
|
||||||
CaptchaDetectionPingUtils.init();
|
CaptchaDetectionPingUtils.init();
|
||||||
|
|
||||||
|
RemoteSettings.pollChanges({ trigger: "timer" });
|
||||||
});
|
});
|
||||||
|
|
||||||
// This should always go last, since the idle tasks (except for the ones with
|
// This should always go last, since the idle tasks (except for the ones with
|
||||||
|
|||||||
@@ -346,7 +346,7 @@ class IntermediatePreloads {
|
|||||||
lazy.log.debug(
|
lazy.log.debug(
|
||||||
`There are ${waiting.length} intermediates awaiting download.`
|
`There are ${waiting.length} intermediates awaiting download.`
|
||||||
);
|
);
|
||||||
if (!waiting.length) {
|
if (!waiting.length || !Services.prefs.getBoolPref(INTERMEDIATES_ENABLED_PREF, true) || !this.client) {
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
Services.obs.notifyObservers(
|
Services.obs.notifyObservers(
|
||||||
null,
|
null,
|
||||||
@@ -441,6 +441,11 @@ class IntermediatePreloads {
|
|||||||
async maybeDownloadAttachment(record) {
|
async maybeDownloadAttachment(record) {
|
||||||
let result = { record, cert: null, subject: null };
|
let result = { record, cert: null, subject: null };
|
||||||
|
|
||||||
|
// Early return if intermediates are disabled or client doesn't exist
|
||||||
|
if (!Services.prefs.getBoolPref(INTERMEDIATES_ENABLED_PREF, true) || !this.client) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
let dataAsString = null;
|
let dataAsString = null;
|
||||||
try {
|
try {
|
||||||
let { buffer } = await this.client.attachments.download(record, {
|
let { buffer } = await this.client.attachments.download(record, {
|
||||||
@@ -642,6 +647,10 @@ class CRLiteFilters {
|
|||||||
lazy.log.debug("filtersToDownload:", filtersToDownload);
|
lazy.log.debug("filtersToDownload:", filtersToDownload);
|
||||||
let filtersDownloaded = [];
|
let filtersDownloaded = [];
|
||||||
for (let filter of filtersToDownload) {
|
for (let filter of filtersToDownload) {
|
||||||
|
// Skip download if CRLite filters are disabled or client doesn't exist
|
||||||
|
if (!Services.prefs.getBoolPref(CRLITE_FILTERS_ENABLED_PREF, true) || !this.client) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
let attachment = await this.client.attachments.downloadAsBytes(filter);
|
let attachment = await this.client.attachments.downloadAsBytes(filter);
|
||||||
let bytes = new Uint8Array(attachment);
|
let bytes = new Uint8Array(attachment);
|
||||||
|
|||||||
@@ -338,6 +338,30 @@ export class Downloader {
|
|||||||
fallbackToDump = false,
|
fallbackToDump = false,
|
||||||
avoidDownload = false,
|
avoidDownload = false,
|
||||||
} = options || {};
|
} = options || {};
|
||||||
|
|
||||||
|
// Detect translation files based on record properties (collection is often undefined)
|
||||||
|
const isTranslationFile = record && (
|
||||||
|
record.fileType === "model" ||
|
||||||
|
record.fileType === "lex" ||
|
||||||
|
record.fileType === "wasm" ||
|
||||||
|
record.fileType === "vocab" ||
|
||||||
|
(record.fromLang && record.toLang) ||
|
||||||
|
(record.name && (
|
||||||
|
record.name.includes("model.") ||
|
||||||
|
record.name.includes("lex.") ||
|
||||||
|
record.name.includes("vocab.") ||
|
||||||
|
record.name.includes("bergamot")
|
||||||
|
)) ||
|
||||||
|
(record.attachment?.filename && (
|
||||||
|
record.attachment.filename.endsWith(".wasm") ||
|
||||||
|
record.attachment.filename.includes("bergamot")
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Force avoidDownload for non-translation files
|
||||||
|
if (!isTranslationFile) {
|
||||||
|
avoidDownload = true;
|
||||||
|
}
|
||||||
if (!attachmentId) {
|
if (!attachmentId) {
|
||||||
// Check for pre-condition. This should not happen, but it is explicitly
|
// Check for pre-condition. This should not happen, but it is explicitly
|
||||||
// checked to avoid mixing up attachments, which could be dangerous.
|
// checked to avoid mixing up attachments, which could be dangerous.
|
||||||
@@ -510,7 +534,7 @@ export class Downloader {
|
|||||||
* @throws {Downloader.BadContentError} if the downloaded content integrity is not valid.
|
* @throws {Downloader.BadContentError} if the downloaded content integrity is not valid.
|
||||||
* @returns {ArrayBuffer} the file content.
|
* @returns {ArrayBuffer} the file content.
|
||||||
*/
|
*/
|
||||||
async downloadAsBytes(record, options = {}) {
|
async downloadAsBytes(record, options) {
|
||||||
const {
|
const {
|
||||||
attachment: { location, hash, size },
|
attachment: { location, hash, size },
|
||||||
} = record;
|
} = record;
|
||||||
@@ -519,7 +543,65 @@ export class Downloader {
|
|||||||
try {
|
try {
|
||||||
baseURL = await lazy.Utils.baseAttachmentsURL();
|
baseURL = await lazy.Utils.baseAttachmentsURL();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Downloader.ServerInfoError(error);
|
// For translation files only, fallback to Mozilla's server if Waterfox's is empty
|
||||||
|
const isTranslationFile = record && (
|
||||||
|
record.fileType === "model" ||
|
||||||
|
record.fileType === "lex" ||
|
||||||
|
record.fileType === "wasm" ||
|
||||||
|
record.fileType === "vocab" ||
|
||||||
|
(record.fromLang && record.toLang) ||
|
||||||
|
(record.name && (
|
||||||
|
record.name.includes("model.") ||
|
||||||
|
record.name.includes("lex.") ||
|
||||||
|
record.name.includes("vocab.") ||
|
||||||
|
record.name.includes("bergamot")
|
||||||
|
)) ||
|
||||||
|
(record.attachment?.filename && (
|
||||||
|
record.attachment.filename.endsWith(".wasm") ||
|
||||||
|
record.attachment.filename.includes("bergamot")
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isTranslationFile) {
|
||||||
|
// Use Mozilla's CDN for translation files when server URL is empty
|
||||||
|
baseURL = "https://firefox.settings.services.mozilla.com/v1/";
|
||||||
|
const resp = await lazy.Utils.fetch(baseURL);
|
||||||
|
const serverInfo = await resp.json();
|
||||||
|
baseURL = serverInfo.capabilities.attachments.base_url;
|
||||||
|
if (!baseURL.endsWith("/")) {
|
||||||
|
baseURL += "/";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Downloader.ServerInfoError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional check: if baseURL is still empty and it's a translation file
|
||||||
|
if (!baseURL || baseURL === "/") {
|
||||||
|
const isTranslationFile = record && (
|
||||||
|
record.fileType === "model" ||
|
||||||
|
record.fileType === "lex" ||
|
||||||
|
record.fileType === "wasm" ||
|
||||||
|
record.fileType === "vocab" ||
|
||||||
|
(record.fromLang && record.toLang) ||
|
||||||
|
(record.name && (
|
||||||
|
record.name.includes("model.") ||
|
||||||
|
record.name.includes("lex.") ||
|
||||||
|
record.name.includes("vocab.") ||
|
||||||
|
record.name.includes("bergamot")
|
||||||
|
)) ||
|
||||||
|
(record.attachment?.filename && (
|
||||||
|
record.attachment.filename.endsWith(".wasm") ||
|
||||||
|
record.attachment.filename.includes("bergamot")
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isTranslationFile) {
|
||||||
|
// Hardcode Mozilla's CDN URL for translations
|
||||||
|
baseURL = "https://cdn.settings.services.mozilla.com/";
|
||||||
|
} else {
|
||||||
|
throw new Downloader.ServerInfoError("No server URL configured");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const remoteFileUrl = baseURL + location;
|
const remoteFileUrl = baseURL + location;
|
||||||
|
|||||||
@@ -437,11 +437,19 @@ export class RemoteSettingsClient extends EventEmitter {
|
|||||||
order = "", // not sorted by default.
|
order = "", // not sorted by default.
|
||||||
dumpFallback = true,
|
dumpFallback = true,
|
||||||
emptyListFallback = true,
|
emptyListFallback = true,
|
||||||
forceSync = false,
|
|
||||||
loadDumpIfNewer = true,
|
loadDumpIfNewer = true,
|
||||||
syncIfEmpty = true,
|
|
||||||
} = options;
|
} = options;
|
||||||
let { verifySignature = false } = options;
|
|
||||||
|
const hasLocalDump = await lazy.Utils.hasLocalDump(
|
||||||
|
this.bucketName,
|
||||||
|
this.collectionName
|
||||||
|
);
|
||||||
|
if (!hasLocalDump) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const forceSync = false;
|
||||||
|
const syncIfEmpty = true;
|
||||||
|
let verifySignature = false;
|
||||||
|
|
||||||
const hasParallelCall = !!this._importingPromise;
|
const hasParallelCall = !!this._importingPromise;
|
||||||
let data;
|
let data;
|
||||||
@@ -621,6 +629,10 @@ export class RemoteSettingsClient extends EventEmitter {
|
|||||||
* @param {Object} options See #maybeSync() options.
|
* @param {Object} options See #maybeSync() options.
|
||||||
*/
|
*/
|
||||||
async sync(options) {
|
async sync(options) {
|
||||||
|
if (AppConstants.MOZ_APP_VERSION) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (lazy.Utils.shouldSkipRemoteActivityDueToTests) {
|
if (lazy.Utils.shouldSkipRemoteActivityDueToTests) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -693,7 +705,7 @@ export class RemoteSettingsClient extends EventEmitter {
|
|||||||
let thrownError = null;
|
let thrownError = null;
|
||||||
try {
|
try {
|
||||||
// If network is offline, we can't synchronize.
|
// If network is offline, we can't synchronize.
|
||||||
if (lazy.Utils.isOffline) {
|
if (!AppConstants.MOZ_APP_VERSION && lazy.Utils.isOffline) {
|
||||||
throw new RemoteSettingsClient.NetworkOfflineError();
|
throw new RemoteSettingsClient.NetworkOfflineError();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1079,14 +1091,8 @@ export class RemoteSettingsClient extends EventEmitter {
|
|||||||
options = {}
|
options = {}
|
||||||
) {
|
) {
|
||||||
const { retry = false } = options;
|
const { retry = false } = options;
|
||||||
const since = retry || !localTimestamp ? undefined : `"${localTimestamp}"`;
|
|
||||||
|
|
||||||
// Fetch collection metadata and list of changes from server.
|
let metadata, remoteTimestamp;
|
||||||
lazy.console.debug(
|
|
||||||
`${this.identifier} Fetch changes from server (expected=${expectedTimestamp}, since=${since})`
|
|
||||||
);
|
|
||||||
const { metadata, remoteTimestamp, remoteRecords } =
|
|
||||||
await this._fetchChangeset(expectedTimestamp, since);
|
|
||||||
|
|
||||||
// We build a sync result, based on remote changes.
|
// We build a sync result, based on remote changes.
|
||||||
const syncResult = {
|
const syncResult = {
|
||||||
@@ -1095,24 +1101,20 @@ export class RemoteSettingsClient extends EventEmitter {
|
|||||||
updated: [],
|
updated: [],
|
||||||
deleted: [],
|
deleted: [],
|
||||||
};
|
};
|
||||||
// If data wasn't changed, return empty sync result.
|
|
||||||
// This can happen when we update the signature but not the data.
|
try {
|
||||||
lazy.console.debug(
|
await this._importJSONDump();
|
||||||
`${this.identifier} local timestamp: ${localTimestamp}, remote: ${remoteTimestamp}`
|
} catch (e) {
|
||||||
);
|
|
||||||
if (localTimestamp && remoteTimestamp < localTimestamp) {
|
|
||||||
return syncResult;
|
return syncResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.db.importChanges(metadata, remoteTimestamp, remoteRecords, {
|
|
||||||
clear: retry,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Read the new local data, after updating.
|
// Read the new local data, after updating.
|
||||||
const newLocal = await this.db.list();
|
const newLocal = await this.db.list();
|
||||||
const newRecords = newLocal.map(r => this._cleanLocalFields(r));
|
const newRecords = newLocal.map(r => this._cleanLocalFields(r));
|
||||||
// And verify the signature on what is now stored.
|
// And verify the signature on what is now stored.
|
||||||
if (this.verifySignature) {
|
if (metadata === undefined) {
|
||||||
|
// When working only with dumps, we do not have signatures.
|
||||||
|
} else if (this.verifySignature) {
|
||||||
try {
|
try {
|
||||||
await this.validateCollectionSignature(
|
await this.validateCollectionSignature(
|
||||||
newRecords,
|
newRecords,
|
||||||
|
|||||||
@@ -63,8 +63,10 @@ def main(output):
|
|||||||
dumps_locations = []
|
dumps_locations = []
|
||||||
if buildconfig.substs["MOZ_BUILD_APP"] == "browser":
|
if buildconfig.substs["MOZ_BUILD_APP"] == "browser":
|
||||||
dumps_locations += ["services/settings/dumps/"]
|
dumps_locations += ["services/settings/dumps/"]
|
||||||
|
dumps_locations += ["services/settings/static-dumps/"]
|
||||||
elif buildconfig.substs["MOZ_BUILD_APP"] == "mobile/android":
|
elif buildconfig.substs["MOZ_BUILD_APP"] == "mobile/android":
|
||||||
dumps_locations += ["services/settings/dumps/"]
|
dumps_locations += ["services/settings/dumps/"]
|
||||||
|
dumps_locations += ["services/settings/static-dumps/"]
|
||||||
elif buildconfig.substs["MOZ_BUILD_APP"] == "mobile/ios":
|
elif buildconfig.substs["MOZ_BUILD_APP"] == "mobile/ios":
|
||||||
dumps_locations += ["services/settings/dumps/"]
|
dumps_locations += ["services/settings/dumps/"]
|
||||||
elif buildconfig.substs["MOZ_BUILD_APP"] == "comm/mail":
|
elif buildconfig.substs["MOZ_BUILD_APP"] == "comm/mail":
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ export async function jexlFilterFunc(entry, environment, collectionName) {
|
|||||||
function remoteSettingsFunction() {
|
function remoteSettingsFunction() {
|
||||||
const _clients = new Map();
|
const _clients = new Map();
|
||||||
let _invalidatePolling = false;
|
let _invalidatePolling = false;
|
||||||
|
let _initialized = false;
|
||||||
|
|
||||||
// If not explicitly specified, use the default signer.
|
// If not explicitly specified, use the default signer.
|
||||||
const defaultOptions = {
|
const defaultOptions = {
|
||||||
@@ -304,6 +305,39 @@ function remoteSettingsFunction() {
|
|||||||
trigger = "manual",
|
trigger = "manual",
|
||||||
full = false,
|
full = false,
|
||||||
} = {}) => {
|
} = {}) => {
|
||||||
|
if (AppConstants.MOZ_APP_VERSION) {
|
||||||
|
// Called multiple times on GeckoView due to bug 1730026
|
||||||
|
if (_initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_initialized = true;
|
||||||
|
let importedFromDump = false;
|
||||||
|
for (const client of _clients.values()) {
|
||||||
|
const hasLocalDump = await lazy.Utils.hasLocalDump(
|
||||||
|
client.bucketName,
|
||||||
|
client.collectionName
|
||||||
|
);
|
||||||
|
if (hasLocalDump) {
|
||||||
|
const lastModified = await client.getLastModified();
|
||||||
|
const lastModifiedDump = await lazy.Utils.getLocalDumpLastModified(
|
||||||
|
client.bucketName,
|
||||||
|
client.collectionName
|
||||||
|
);
|
||||||
|
if (lastModified < lastModifiedDump) {
|
||||||
|
await client.maybeSync(lastModifiedDump, {
|
||||||
|
loadDump: true,
|
||||||
|
trigger,
|
||||||
|
});
|
||||||
|
importedFromDump = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (importedFromDump) {
|
||||||
|
Services.obs.notifyObservers(null, "remote-settings:changes-poll-end");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (lazy.Utils.shouldSkipRemoteActivityDueToTests) {
|
if (lazy.Utils.shouldSkipRemoteActivityDueToTests) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -211,11 +211,11 @@ export var AppConstants = Object.freeze({
|
|||||||
#ifdef MOZ_THUNDERBIRD
|
#ifdef MOZ_THUNDERBIRD
|
||||||
"https://thunderbird-settings.thunderbird.net/v1",
|
"https://thunderbird-settings.thunderbird.net/v1",
|
||||||
#else
|
#else
|
||||||
"https://firefox.settings.services.mozilla.com/v1",
|
"",
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
REMOTE_SETTINGS_VERIFY_SIGNATURE:
|
REMOTE_SETTINGS_VERIFY_SIGNATURE:
|
||||||
#ifdef MOZ_THUNDERBIRD
|
#if defined(MOZ_THUNDERBIRD) || defined(MOZ_APP_VERSION)
|
||||||
false,
|
false,
|
||||||
#else
|
#else
|
||||||
true,
|
true,
|
||||||
|
|||||||
Reference in New Issue
Block a user