Bug 1772097 - Part 5: Use plain object for lazy getter in services/settings/. r=markh

Differential Revision: https://phabricator.services.mozilla.com/D147920
This commit is contained in:
Tooru Fujisawa 2022-06-02 12:27:44 +00:00
parent b868c9f585
commit 4f3eafb63e
8 changed files with 292 additions and 247 deletions

View File

@ -7,12 +7,13 @@ var EXPORTED_SYMBOLS = ["Downloader"];
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
XPCOMUtils.defineLazyModuleGetters(this, {
const lazy = {};
XPCOMUtils.defineLazyModuleGetters(lazy, {
RemoteSettingsWorker: "resource://services-settings/RemoteSettingsWorker.jsm",
Utils: "resource://services-settings/Utils.jsm",
});
ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]);
ChromeUtils.defineModuleGetter(lazy, "OS", "resource://gre/modules/osfile.jsm");
XPCOMUtils.defineLazyGlobalGetters(lazy, ["fetch"]);
class DownloadError extends Error {
constructor(url, resp) {
@ -304,13 +305,13 @@ class Downloader {
const {
attachment: { filename, size, hash },
} = record;
const localFilePath = OS.Path.join(
OS.Constants.Path.localProfileDir,
const localFilePath = lazy.OS.Path.join(
lazy.OS.Constants.Path.localProfileDir,
...this.folders,
filename
);
const localFileUrl = `file://${[
...OS.Path.split(OS.Constants.Path.localProfileDir).components,
...lazy.OS.Path.split(lazy.OS.Constants.Path.localProfileDir).components,
...this.folders,
filename,
].join("/")}`;
@ -319,7 +320,9 @@ class Downloader {
let retried = 0;
while (true) {
if (await RemoteSettingsWorker.checkFileHash(localFileUrl, size, hash)) {
if (
await lazy.RemoteSettingsWorker.checkFileHash(localFileUrl, size, hash)
) {
return localFileUrl;
}
// File does not exist or is corrupted.
@ -370,7 +373,9 @@ class Downloader {
if (!checkHash) {
return buffer;
}
if (await RemoteSettingsWorker.checkContentHash(buffer, size, hash)) {
if (
await lazy.RemoteSettingsWorker.checkContentHash(buffer, size, hash)
) {
return buffer;
}
// Content is corrupted.
@ -400,8 +405,8 @@ class Downloader {
const {
attachment: { filename },
} = record;
const path = OS.Path.join(
OS.Constants.Path.localProfileDir,
const path = lazy.OS.Path.join(
lazy.OS.Constants.Path.localProfileDir,
...this.folders,
filename
);
@ -411,7 +416,7 @@ class Downloader {
async _baseAttachmentsURL() {
if (!this._cdnURL) {
const resp = await Utils.fetch(`${Utils.SERVER_URL}/`);
const resp = await lazy.Utils.fetch(`${lazy.Utils.SERVER_URL}/`);
let serverInfo;
try {
serverInfo = await resp.json();
@ -433,7 +438,7 @@ class Downloader {
async _fetchAttachment(url) {
const headers = new Headers();
headers.set("Accept-Encoding", "gzip");
const resp = await Utils.fetch(url, { headers });
const resp = await lazy.Utils.fetch(url, { headers });
if (!resp.ok) {
throw new Downloader.DownloadError(url, resp);
}
@ -450,7 +455,9 @@ class Downloader {
async readBuffer() {
const buffer = await cached.blob.arrayBuffer();
const { size, hash } = cached.record.attachment;
if (await RemoteSettingsWorker.checkContentHash(buffer, size, hash)) {
if (
await lazy.RemoteSettingsWorker.checkContentHash(buffer, size, hash)
) {
return buffer;
}
// Really unexpected, could indicate corruption in IndexedDB.
@ -462,7 +469,7 @@ class Downloader {
async _readAttachmentDump(attachmentId) {
async function fetchResource(resourceUrl) {
try {
return await fetch(resourceUrl);
return await lazy.fetch(resourceUrl);
} catch (e) {
throw new Downloader.DownloadError(resourceUrl);
}
@ -484,8 +491,8 @@ class Downloader {
static _RESOURCE_BASE_URL = "resource://app/defaults";
async _makeDirs() {
const dirPath = OS.Path.join(
OS.Constants.Path.localProfileDir,
const dirPath = lazy.OS.Path.join(
lazy.OS.Constants.Path.localProfileDir,
...this.folders
);
await IOUtils.makeDirectory(dirPath, { createAncestors: true });
@ -493,8 +500,8 @@ class Downloader {
async _rmDirs() {
for (let i = this.folders.length; i > 0; i--) {
const dirPath = OS.Path.join(
OS.Constants.Path.localProfileDir,
const dirPath = lazy.OS.Path.join(
lazy.OS.Constants.Path.localProfileDir,
...this.folders.slice(0, i)
);
try {

View File

@ -7,14 +7,16 @@ const { XPCOMUtils } = ChromeUtils.import(
);
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetters(this, {
const lazy = {};
XPCOMUtils.defineLazyModuleGetters(lazy, {
AsyncShutdown: "resource://gre/modules/AsyncShutdown.jsm",
IDBHelpers: "resource://services-settings/IDBHelpers.jsm",
Utils: "resource://services-settings/Utils.jsm",
CommonUtils: "resource://services-common/utils.js",
ObjectUtils: "resource://gre/modules/ObjectUtils.jsm",
});
XPCOMUtils.defineLazyGetter(this, "console", () => Utils.log);
XPCOMUtils.defineLazyGetter(lazy, "console", () => lazy.Utils.log);
var EXPORTED_SYMBOLS = ["Database"];
@ -37,7 +39,7 @@ class Database {
"records",
(store, rejectTransaction) => {
// Fast-path the (very common) no-filters case
if (ObjectUtils.isEmpty(filters)) {
if (lazy.ObjectUtils.isEmpty(filters)) {
const range = IDBKeyRange.only(this.identifier);
const request = store.index("cid").getAll(range);
request.onsuccess = e => {
@ -54,7 +56,7 @@ class Database {
const cursor = event.target.result;
if (cursor) {
const { value } = cursor;
if (Utils.filterObject(objFilters, value)) {
if (lazy.Utils.filterObject(objFilters, value)) {
results.push(value);
}
cursor.continue();
@ -67,13 +69,13 @@ class Database {
{ mode: "readonly" }
);
} catch (e) {
throw new IDBHelpers.IndexedDBError(e, "list()", this.identifier);
throw new lazy.IDBHelpers.IndexedDBError(e, "list()", this.identifier);
}
// Remove IDB key field from results.
for (const result of results) {
delete result._cid;
}
return order ? Utils.sortObjects(order, results) : results;
return order ? lazy.Utils.sortObjects(order, results) : results;
}
async importChanges(metadata, timestamp, records = [], options = {}) {
@ -129,17 +131,17 @@ class Database {
// Separate tombstones from creations/updates.
const toDelete = records.filter(r => r.deleted);
const toInsert = records.filter(r => !r.deleted);
console.debug(
lazy.console.debug(
`${_cid} ${toDelete.length} to delete, ${toInsert.length} to insert`
);
// Delete local records for each tombstone.
IDBHelpers.bulkOperationHelper(
lazy.IDBHelpers.bulkOperationHelper(
storeRecords,
{
reject: rejectTransaction,
completion() {
// Overwrite all other data.
IDBHelpers.bulkOperationHelper(
lazy.IDBHelpers.bulkOperationHelper(
storeRecords,
{
reject: rejectTransaction,
@ -156,7 +158,7 @@ class Database {
{ desc: "importChanges() in " + _cid }
);
} catch (e) {
throw new IDBHelpers.IndexedDBError(e, "importChanges()", _cid);
throw new lazy.IDBHelpers.IndexedDBError(e, "importChanges()", _cid);
}
}
@ -171,7 +173,7 @@ class Database {
{ mode: "readonly" }
);
} catch (e) {
throw new IDBHelpers.IndexedDBError(
throw new lazy.IDBHelpers.IndexedDBError(
e,
"getLastModified()",
this.identifier
@ -184,7 +186,7 @@ class Database {
// contain timestamps for last_modified. Work around this here, and return
// the timestamp as zero, so that the entries should get updated.
if (isNaN(entry.value)) {
console.warn(`Local timestamp is NaN for ${this.identifier}`);
lazy.console.warn(`Local timestamp is NaN for ${this.identifier}`);
return 0;
}
return entry.value;
@ -201,7 +203,11 @@ class Database {
{ mode: "readonly" }
);
} catch (e) {
throw new IDBHelpers.IndexedDBError(e, "getMetadata()", this.identifier);
throw new lazy.IDBHelpers.IndexedDBError(
e,
"getMetadata()",
this.identifier
);
}
return entry ? entry.metadata : null;
}
@ -219,7 +225,7 @@ class Database {
{ mode: "readonly" }
);
} catch (e) {
throw new IDBHelpers.IndexedDBError(
throw new lazy.IDBHelpers.IndexedDBError(
e,
"getAttachment()",
this.identifier
@ -242,7 +248,7 @@ class Database {
{ desc: "saveAttachment(" + attachmentId + ") in " + this.identifier }
);
} catch (e) {
throw new IDBHelpers.IndexedDBError(
throw new lazy.IDBHelpers.IndexedDBError(
e,
"saveAttachment()",
this.identifier
@ -254,7 +260,7 @@ class Database {
try {
await this.importChanges(null, null, [], { clear: true });
} catch (e) {
throw new IDBHelpers.IndexedDBError(e, "clear()", this.identifier);
throw new lazy.IDBHelpers.IndexedDBError(e, "clear()", this.identifier);
}
}
@ -264,7 +270,7 @@ class Database {
async create(record) {
if (!("id" in record)) {
record = { ...record, id: CommonUtils.generateUUID() };
record = { ...record, id: lazy.CommonUtils.generateUUID() };
}
try {
await executeIDB(
@ -275,7 +281,7 @@ class Database {
{ desc: "create() in " + this.identifier }
);
} catch (e) {
throw new IDBHelpers.IndexedDBError(e, "create()", this.identifier);
throw new lazy.IDBHelpers.IndexedDBError(e, "create()", this.identifier);
}
return record;
}
@ -290,7 +296,7 @@ class Database {
{ desc: "update() in " + this.identifier }
);
} catch (e) {
throw new IDBHelpers.IndexedDBError(e, "update()", this.identifier);
throw new lazy.IDBHelpers.IndexedDBError(e, "update()", this.identifier);
}
}
@ -304,7 +310,7 @@ class Database {
{ desc: "delete() in " + this.identifier }
);
} catch (e) {
throw new IDBHelpers.IndexedDBError(e, "delete()", this.identifier);
throw new lazy.IDBHelpers.IndexedDBError(e, "delete()", this.identifier);
}
}
}
@ -324,7 +330,7 @@ async function openIDB() {
// IndexedDB work more than once.
if (!gDBPromise) {
// Open and initialize/upgrade if needed.
gDBPromise = IDBHelpers.openIDB();
gDBPromise = lazy.IDBHelpers.openIDB();
}
let db = await gDBPromise;
if (!gDB) {
@ -351,7 +357,7 @@ async function executeIDB(storeNames, callback, options = {}) {
// both that and a bool we set ourselves when `profile-before-change`
// starts.
if (gShutdownStarted || Services.startup.shuttingDown) {
throw new IDBHelpers.ShutdownError(
throw new lazy.IDBHelpers.ShutdownError(
"The application is shutting down",
"execute()"
);
@ -365,7 +371,7 @@ async function executeIDB(storeNames, callback, options = {}) {
// Check for shutdown again as we've await'd something...
if (!gDB && (gShutdownStarted || Services.startup.shuttingDown)) {
throw new IDBHelpers.ShutdownError(
throw new lazy.IDBHelpers.ShutdownError(
"The application is shutting down",
"execute()"
);
@ -373,7 +379,7 @@ async function executeIDB(storeNames, callback, options = {}) {
// Start the actual transaction:
const { mode = "readwrite", desc = "" } = options;
let { promise, transaction } = IDBHelpers.executeIDB(
let { promise, transaction } = lazy.IDBHelpers.executeIDB(
gDB,
storeNames,
mode,
@ -469,7 +475,7 @@ function ensureShutdownBlocker() {
return;
}
gShutdownBlocker = true;
AsyncShutdown.profileBeforeChange.addBlocker(
lazy.AsyncShutdown.profileBeforeChange.addBlocker(
"RemoteSettingsClient - finish IDB access.",
Database._shutdownHandler,
{

View File

@ -11,7 +11,9 @@ const { XPCOMUtils } = ChromeUtils.import(
);
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetters(this, {
const lazy = {};
XPCOMUtils.defineLazyModuleGetters(lazy, {
AppConstants: "resource://gre/modules/AppConstants.jsm",
ClientEnvironmentBase:
"resource://gre/modules/components-utils/ClientEnvironment.jsm",
@ -28,7 +30,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
const TELEMETRY_COMPONENT = "remotesettings";
XPCOMUtils.defineLazyGetter(this, "console", () => Utils.log);
XPCOMUtils.defineLazyGetter(lazy, "console", () => lazy.Utils.log);
/**
* cacheProxy returns an object Proxy that will memoize properties of the target.
@ -145,7 +147,7 @@ class UnknownCollectionError extends Error {
}
}
class AttachmentDownloader extends Downloader {
class AttachmentDownloader extends lazy.Downloader {
constructor(client) {
super(client.bucketName, client.collectionName);
this._client = client;
@ -178,14 +180,14 @@ class AttachmentDownloader extends Downloader {
return await super.download(record, options);
} catch (err) {
// Report download error.
let status = UptakeTelemetry.STATUS.DOWNLOAD_ERROR;
if (Utils.isOffline) {
status = UptakeTelemetry.STATUS.NETWORK_OFFLINE_ERROR;
let status = lazy.UptakeTelemetry.STATUS.DOWNLOAD_ERROR;
if (lazy.Utils.isOffline) {
status = lazy.UptakeTelemetry.STATUS.NETWORK_OFFLINE_ERROR;
} else if (/NetworkError/.test(err.message)) {
status = UptakeTelemetry.STATUS.NETWORK_ERROR;
status = lazy.UptakeTelemetry.STATUS.NETWORK_ERROR;
}
// If the file failed to be downloaded, report it as such in Telemetry.
await UptakeTelemetry.report(TELEMETRY_COMPONENT, status, {
await lazy.UptakeTelemetry.report(TELEMETRY_COMPONENT, status, {
source: this._client.identifier,
});
throw err;
@ -230,7 +232,7 @@ class RemoteSettingsClient extends EventEmitter {
constructor(
collectionName,
{
bucketName = AppConstants.REMOTE_SETTINGS_DEFAULT_BUCKET,
bucketName = lazy.AppConstants.REMOTE_SETTINGS_DEFAULT_BUCKET,
signerName,
filterFunc,
localFields = [],
@ -242,7 +244,7 @@ class RemoteSettingsClient extends EventEmitter {
this.collectionName = collectionName;
// Client is constructed with the raw bucket name (eg. "main", "security-state", "blocklist")
// The `bucketName` will contain the `-preview` suffix if the preview mode is enabled.
this.bucketName = Utils.actualBucketName(bucketName);
this.bucketName = lazy.Utils.actualBucketName(bucketName);
this.signerName = signerName;
this.filterFunc = filterFunc;
this.localFields = localFields;
@ -252,12 +254,12 @@ class RemoteSettingsClient extends EventEmitter {
// This attribute allows signature verification to be disabled, when running tests
// or when pulling data from a dev server.
this.verifySignature = AppConstants.REMOTE_SETTINGS_VERIFY_SIGNATURE;
this.verifySignature = lazy.AppConstants.REMOTE_SETTINGS_VERIFY_SIGNATURE;
XPCOMUtils.defineLazyGetter(
this,
"db",
() => new Database(this.identifier)
() => new lazy.Database(this.identifier)
);
XPCOMUtils.defineLazyGetter(
@ -274,7 +276,7 @@ class RemoteSettingsClient extends EventEmitter {
* See `RemoteSettings.enabledPreviewMode()`.
*/
refreshBucketName() {
this.bucketName = Utils.actualBucketName(this.bucketName);
this.bucketName = lazy.Utils.actualBucketName(this.bucketName);
this.db.identifier = this.identifier;
}
@ -290,8 +292,8 @@ class RemoteSettingsClient extends EventEmitter {
}
httpClient() {
const api = new KintoHttpClient(Utils.SERVER_URL, {
fetchFunc: Utils.fetch, // Use fetch() wrapper.
const api = new lazy.KintoHttpClient(lazy.Utils.SERVER_URL, {
fetchFunc: lazy.Utils.fetch, // Use fetch() wrapper.
});
return api.bucket(this.bucketName).collection(this.collectionName);
}
@ -310,7 +312,7 @@ class RemoteSettingsClient extends EventEmitter {
try {
timestamp = await this.db.getLastModified();
} catch (err) {
console.warn(
lazy.console.warn(
`Error retrieving the getLastModified timestamp from ${this.identifier} RemoteSettingsClient`,
err
);
@ -352,14 +354,14 @@ class RemoteSettingsClient extends EventEmitter {
if (!this._importingPromise) {
// Prevent parallel loading when .get() is called multiple times.
this._importingPromise = (async () => {
const importedFromDump = Utils.LOAD_DUMPS
const importedFromDump = lazy.Utils.LOAD_DUMPS
? await this._importJSONDump()
: -1;
if (importedFromDump < 0) {
// There is no JSON dump to load, force a synchronization from the server.
// We don't want the "sync" event to be sent, since some consumers use `.get()`
// in "sync" callbacks. See Bug 1761953
console.debug(
lazy.console.debug(
`${this.identifier} Local DB is empty, pull data from server`
);
await this.sync({ loadDump: false, sendEvents: false });
@ -370,17 +372,17 @@ class RemoteSettingsClient extends EventEmitter {
return true;
})();
} else {
console.debug(`${this.identifier} Awaiting existing import.`);
lazy.console.debug(`${this.identifier} Awaiting existing import.`);
}
} else if (hasLocalData && loadDumpIfNewer) {
// Check whether the local data is older than the packaged dump.
// If it is, load the packaged dump (which overwrites the local data).
let lastModifiedDump = await Utils.getLocalDumpLastModified(
let lastModifiedDump = await lazy.Utils.getLocalDumpLastModified(
this.bucketName,
this.collectionName
);
if (lastModified < lastModifiedDump) {
console.debug(
lazy.console.debug(
`${this.identifier} Local DB is stale (${lastModified}), using dump instead (${lastModifiedDump})`
);
if (!this._importingPromise) {
@ -394,7 +396,7 @@ class RemoteSettingsClient extends EventEmitter {
return importedFromDump >= 0;
})();
} else {
console.debug(`${this.identifier} Awaiting existing import.`);
lazy.console.debug(`${this.identifier} Awaiting existing import.`);
}
}
}
@ -425,21 +427,23 @@ class RemoteSettingsClient extends EventEmitter {
throw e;
}
Cu.reportError(e);
let { data } = await SharedUtils.loadJSONDump(
let { data } = await lazy.SharedUtils.loadJSONDump(
this.bucketName,
this.collectionName
);
if (data !== null) {
console.info(`${this.identifier} falling back to JSON dump`);
lazy.console.info(`${this.identifier} falling back to JSON dump`);
} else {
console.info(`${this.identifier} no dump fallback, return empty list`);
lazy.console.info(
`${this.identifier} no dump fallback, return empty list`
);
data = [];
}
if (!ObjectUtils.isEmpty(filters)) {
data = data.filter(r => Utils.filterObject(filters, r));
if (!lazy.ObjectUtils.isEmpty(filters)) {
data = data.filter(r => lazy.Utils.filterObject(filters, r));
}
if (order) {
data = Utils.sortObjects(order, data);
data = lazy.Utils.sortObjects(order, data);
}
// No need to verify signature on JSON dumps.
// If local DB cannot be read, then we don't even try to do anything,
@ -447,21 +451,21 @@ class RemoteSettingsClient extends EventEmitter {
return this._filterEntries(data);
}
console.debug(
lazy.console.debug(
`${this.identifier} ${data.length} records before filtering.`
);
if (verifySignature) {
console.debug(
lazy.console.debug(
`${this.identifier} verify signature of local data on read`
);
const allData = ObjectUtils.isEmpty(filters)
const allData = lazy.ObjectUtils.isEmpty(filters)
? data
: await this.db.list();
const localRecords = allData.map(r => this._cleanLocalFields(r));
const timestamp = await this.db.getLastModified();
let metadata = await this.db.getMetadata();
if (syncIfEmpty && ObjectUtils.isEmpty(metadata)) {
if (syncIfEmpty && lazy.ObjectUtils.isEmpty(metadata)) {
// No sync occured yet, may have records from dump but no metadata.
// We don't want the "sync" event to be sent, since some consumers use `.get()`
// in "sync" callbacks. See Bug 1761953
@ -478,7 +482,7 @@ class RemoteSettingsClient extends EventEmitter {
// Filter the records based on `this.filterFunc` results.
const final = await this._filterEntries(data);
console.debug(
lazy.console.debug(
`${this.identifier} ${final.length} records after filtering.`
);
return final;
@ -492,12 +496,15 @@ class RemoteSettingsClient extends EventEmitter {
async sync(options) {
// We want to know which timestamp we are expected to obtain in order to leverage
// cache busting. We don't provide ETag because we don't want a 304.
const { changes } = await Utils.fetchLatestChanges(Utils.SERVER_URL, {
filters: {
collection: this.collectionName,
bucket: this.bucketName,
},
});
const { changes } = await lazy.Utils.fetchLatestChanges(
lazy.Utils.SERVER_URL,
{
filters: {
collection: this.collectionName,
bucket: this.bucketName,
},
}
);
if (changes.length === 0) {
throw new RemoteSettingsClient.UnknownCollectionError(this.identifier);
}
@ -522,7 +529,7 @@ class RemoteSettingsClient extends EventEmitter {
async maybeSync(expectedTimestamp, options = {}) {
// Should the clients try to load JSON dump? (mainly disabled in tests)
const {
loadDump = Utils.LOAD_DUMPS,
loadDump = lazy.Utils.LOAD_DUMPS,
trigger = "manual",
sendEvents = true,
} = options;
@ -530,14 +537,14 @@ class RemoteSettingsClient extends EventEmitter {
// Make sure we don't run several synchronizations in parallel, mainly
// in order to avoid race conditions in "sync" events listeners.
if (this._syncRunning) {
console.warn(`${this.identifier} sync already running`);
lazy.console.warn(`${this.identifier} sync already running`);
return;
}
// Prevent network requests and IndexedDB calls to be initiated
// during shutdown.
if (Services.startup.shuttingDown) {
console.warn(`${this.identifier} sync interrupted by shutdown`);
lazy.console.warn(`${this.identifier} sync interrupted by shutdown`);
return;
}
@ -549,7 +556,7 @@ class RemoteSettingsClient extends EventEmitter {
let thrownError = null;
try {
// If network is offline, we can't synchronize.
if (Utils.isOffline) {
if (lazy.Utils.isOffline) {
throw new RemoteSettingsClient.NetworkOfflineError();
}
@ -569,7 +576,7 @@ class RemoteSettingsClient extends EventEmitter {
const imported = await this._importJSONDump();
// The worker only returns an integer. List the imported records to build the sync event.
if (imported > 0) {
console.debug(
lazy.console.debug(
`${this.identifier} ${imported} records loaded from JSON dump`
);
importedFromDump = await this.db.list();
@ -587,13 +594,13 @@ class RemoteSettingsClient extends EventEmitter {
try {
// Is local timestamp up to date with the server?
if (expectedTimestamp == collectionLastModified) {
console.debug(`${this.identifier} local data is up-to-date`);
reportStatus = UptakeTelemetry.STATUS.UP_TO_DATE;
lazy.console.debug(`${this.identifier} local data is up-to-date`);
reportStatus = lazy.UptakeTelemetry.STATUS.UP_TO_DATE;
// If the data is up-to-date but don't have metadata (records loaded from dump),
// we fetch them and validate the signature immediately.
if (this.verifySignature && ObjectUtils.isEmpty(localMetadata)) {
console.debug(`${this.identifier} pull collection metadata`);
if (this.verifySignature && lazy.ObjectUtils.isEmpty(localMetadata)) {
lazy.console.debug(`${this.identifier} pull collection metadata`);
const metadata = await this.httpClient().getData({
query: { _expected: expectedTimestamp },
});
@ -601,7 +608,7 @@ class RemoteSettingsClient extends EventEmitter {
// We don't bother validating the signature if the dump was just loaded. We do
// if the dump was loaded at some other point (eg. from .get()).
if (this.verifySignature && importedFromDump.length == 0) {
console.debug(
lazy.console.debug(
`${this.identifier} verify signature of local data`
);
await this._validateCollectionSignature(
@ -659,14 +666,14 @@ class RemoteSettingsClient extends EventEmitter {
// Signature verification failed during synchronization.
reportStatus =
e instanceof CorruptedDataError
? UptakeTelemetry.STATUS.CORRUPTION_ERROR
: UptakeTelemetry.STATUS.SIGNATURE_ERROR;
? lazy.UptakeTelemetry.STATUS.CORRUPTION_ERROR
: lazy.UptakeTelemetry.STATUS.SIGNATURE_ERROR;
// If sync fails with a signature error, it's likely that our
// local data has been modified in some way.
// We will attempt to fix this by retrieving the whole
// remote collection.
try {
console.warn(
lazy.console.warn(
`${this.identifier} Signature verified failed. Retry from scratch`
);
syncResult = await this._importChanges(
@ -679,14 +686,14 @@ class RemoteSettingsClient extends EventEmitter {
} catch (e) {
// If the signature fails again, or if an error occured during wiping out the
// local data, then we report it as a *signature retry* error.
reportStatus = UptakeTelemetry.STATUS.SIGNATURE_RETRY_ERROR;
reportStatus = lazy.UptakeTelemetry.STATUS.SIGNATURE_RETRY_ERROR;
throw e;
}
} else {
// The sync has thrown for other reason than signature verification.
// Default status for errors at this step is SYNC_ERROR.
reportStatus = this._telemetryFromError(e, {
default: UptakeTelemetry.STATUS.SYNC_ERROR,
default: lazy.UptakeTelemetry.STATUS.SYNC_ERROR,
});
throw e;
}
@ -699,11 +706,11 @@ class RemoteSettingsClient extends EventEmitter {
try {
await this.emit("sync", { data: filteredSyncResult });
} catch (e) {
reportStatus = UptakeTelemetry.STATUS.APPLY_ERROR;
reportStatus = lazy.UptakeTelemetry.STATUS.APPLY_ERROR;
throw e;
}
} else {
console.info(
lazy.console.info(
`All changes are filtered by JEXL expressions for ${this.identifier}`
);
}
@ -713,13 +720,13 @@ class RemoteSettingsClient extends EventEmitter {
// If browser is shutting down, then we can report a specific status.
// (eg. IndexedDB will abort transactions)
if (Services.startup.shuttingDown) {
reportStatus = UptakeTelemetry.STATUS.SHUTDOWN_ERROR;
reportStatus = lazy.UptakeTelemetry.STATUS.SHUTDOWN_ERROR;
}
// If no Telemetry status was determined yet (ie. outside sync step),
// then introspect error, default status at this step is UNKNOWN.
else if (reportStatus == null) {
reportStatus = this._telemetryFromError(e, {
default: UptakeTelemetry.STATUS.UNKNOWN_ERROR,
default: lazy.UptakeTelemetry.STATUS.UNKNOWN_ERROR,
});
}
throw e;
@ -727,7 +734,7 @@ class RemoteSettingsClient extends EventEmitter {
const durationMilliseconds = new Date() - startedAt;
// No error was reported, this is a success!
if (reportStatus === null) {
reportStatus = UptakeTelemetry.STATUS.SUCCESS;
reportStatus = lazy.UptakeTelemetry.STATUS.SUCCESS;
}
// Report success/error status to Telemetry.
let reportArgs = {
@ -738,15 +745,15 @@ class RemoteSettingsClient extends EventEmitter {
// In Bug 1617133, we will try to break down specific errors into
// more precise statuses by reporting the JavaScript error name
// ("TypeError", etc.) to Telemetry on Nightly.
const channel = UptakeTelemetry.Policy.getChannel();
const channel = lazy.UptakeTelemetry.Policy.getChannel();
if (
thrownError !== null &&
channel == "nightly" &&
[
UptakeTelemetry.STATUS.SYNC_ERROR,
UptakeTelemetry.STATUS.CUSTOM_1_ERROR, // IndexedDB.
UptakeTelemetry.STATUS.UNKNOWN_ERROR,
UptakeTelemetry.STATUS.SHUTDOWN_ERROR,
lazy.UptakeTelemetry.STATUS.SYNC_ERROR,
lazy.UptakeTelemetry.STATUS.CUSTOM_1_ERROR, // IndexedDB.
lazy.UptakeTelemetry.STATUS.UNKNOWN_ERROR,
lazy.UptakeTelemetry.STATUS.SHUTDOWN_ERROR,
].includes(reportStatus)
) {
// List of possible error names for IndexedDB:
@ -754,13 +761,13 @@ class RemoteSettingsClient extends EventEmitter {
reportArgs = { ...reportArgs, errorName: thrownError.name };
}
await UptakeTelemetry.report(
await lazy.UptakeTelemetry.report(
TELEMETRY_COMPONENT,
reportStatus,
reportArgs
);
console.debug(`${this.identifier} sync status is ${reportStatus}`);
lazy.console.debug(`${this.identifier} sync status is ${reportStatus}`);
this._syncRunning = false;
}
}
@ -773,26 +780,26 @@ class RemoteSettingsClient extends EventEmitter {
let reportStatus = options.default;
if (e instanceof RemoteSettingsClient.NetworkOfflineError) {
reportStatus = UptakeTelemetry.STATUS.NETWORK_OFFLINE_ERROR;
} else if (e instanceof IDBHelpers.ShutdownError) {
reportStatus = UptakeTelemetry.STATUS.SHUTDOWN_ERROR;
reportStatus = lazy.UptakeTelemetry.STATUS.NETWORK_OFFLINE_ERROR;
} else if (e instanceof lazy.IDBHelpers.ShutdownError) {
reportStatus = lazy.UptakeTelemetry.STATUS.SHUTDOWN_ERROR;
} else if (/unparseable/.test(e.message)) {
reportStatus = UptakeTelemetry.STATUS.PARSE_ERROR;
reportStatus = lazy.UptakeTelemetry.STATUS.PARSE_ERROR;
} else if (/NetworkError/.test(e.message)) {
reportStatus = UptakeTelemetry.STATUS.NETWORK_ERROR;
reportStatus = lazy.UptakeTelemetry.STATUS.NETWORK_ERROR;
} else if (/Timeout/.test(e.message)) {
reportStatus = UptakeTelemetry.STATUS.TIMEOUT_ERROR;
reportStatus = lazy.UptakeTelemetry.STATUS.TIMEOUT_ERROR;
} else if (/HTTP 5??/.test(e.message)) {
reportStatus = UptakeTelemetry.STATUS.SERVER_ERROR;
reportStatus = lazy.UptakeTelemetry.STATUS.SERVER_ERROR;
} else if (/Backoff/.test(e.message)) {
reportStatus = UptakeTelemetry.STATUS.BACKOFF;
reportStatus = lazy.UptakeTelemetry.STATUS.BACKOFF;
} else if (
// Errors from kinto.js IDB adapter.
e instanceof IDBHelpers.IndexedDBError ||
e instanceof lazy.IDBHelpers.IndexedDBError ||
// Other IndexedDB errors (eg. RemoteSettingsWorker).
/IndexedDB/.test(e.message)
) {
reportStatus = UptakeTelemetry.STATUS.CUSTOM_1_ERROR;
reportStatus = lazy.UptakeTelemetry.STATUS.CUSTOM_1_ERROR;
}
return reportStatus;
@ -802,15 +809,17 @@ class RemoteSettingsClient extends EventEmitter {
* Import the JSON files from services/settings/dump into the local DB.
*/
async _importJSONDump() {
console.info(`${this.identifier} try to restore dump`);
const result = await RemoteSettingsWorker.importJSONDump(
lazy.console.info(`${this.identifier} try to restore dump`);
const result = await lazy.RemoteSettingsWorker.importJSONDump(
this.bucketName,
this.collectionName
);
if (result < 0) {
console.debug(`${this.identifier} no dump available`);
lazy.console.debug(`${this.identifier} no dump available`);
} else {
console.info(`${this.identifier} imported ${result} records from dump`);
lazy.console.info(
`${this.identifier} imported ${result} records from dump`
);
}
return result;
}
@ -839,9 +848,9 @@ class RemoteSettingsClient extends EventEmitter {
const {
signature: { x5u, signature },
} = metadata;
const certChain = await (await Utils.fetch(x5u)).text();
const certChain = await (await lazy.Utils.fetch(x5u)).text();
// Merge remote records with local ones and serialize as canonical JSON.
const serialized = await RemoteSettingsWorker.canonicalStringify(
const serialized = await lazy.RemoteSettingsWorker.canonicalStringify(
records,
timestamp
);
@ -883,7 +892,7 @@ class RemoteSettingsClient extends EventEmitter {
const since = retry || !localTimestamp ? undefined : `"${localTimestamp}"`;
// Fetch collection metadata and list of changes from server.
console.debug(
lazy.console.debug(
`${this.identifier} Fetch changes from server (expected=${expectedTimestamp}, since=${since})`
);
const {
@ -901,7 +910,7 @@ class RemoteSettingsClient extends EventEmitter {
};
// If data wasn't changed, return empty sync result.
// This can happen when we update the signature but not the data.
console.debug(
lazy.console.debug(
`${this.identifier} local timestamp: ${localTimestamp}, remote: ${remoteTimestamp}`
);
if (localTimestamp && remoteTimestamp < localTimestamp) {
@ -924,7 +933,7 @@ class RemoteSettingsClient extends EventEmitter {
metadata
);
} catch (e) {
console.error(
lazy.console.error(
`${this.identifier} Signature failed ${retry ? "again" : ""} ${e}`
);
if (!(e instanceof InvalidSignatureError)) {
@ -937,7 +946,7 @@ class RemoteSettingsClient extends EventEmitter {
// during sync, from hijacks of local DBs, we will verify
// the signature on the data that we had before syncing.
let localTrustworthy = false;
console.debug(`${this.identifier} verify data before sync`);
lazy.console.debug(`${this.identifier} verify data before sync`);
try {
await this._validateCollectionSignature(
localRecords,
@ -950,16 +959,16 @@ class RemoteSettingsClient extends EventEmitter {
// If it fails for other reason, keep original error and give up.
throw sigerr;
}
console.debug(`${this.identifier} previous data was invalid`);
lazy.console.debug(`${this.identifier} previous data was invalid`);
}
if (!localTrustworthy && !retry) {
// Signature failed, clear local DB because it contains
// bad data (local + remote changes).
console.debug(`${this.identifier} clear local data`);
lazy.console.debug(`${this.identifier} clear local data`);
await this.db.clear();
// Local data was tampered, throw and it will retry from empty DB.
console.error(`${this.identifier} local data was corrupted`);
lazy.console.error(`${this.identifier} local data was corrupted`);
throw new CorruptedDataError(this.identifier);
} else if (retry) {
// We retried already, we will restore the previous local data
@ -986,7 +995,7 @@ class RemoteSettingsClient extends EventEmitter {
throw e;
}
} else {
console.warn(`${this.identifier} has signature disabled`);
lazy.console.warn(`${this.identifier} has signature disabled`);
}
if (this.hasListeners("sync")) {
@ -1008,7 +1017,7 @@ class RemoteSettingsClient extends EventEmitter {
syncResult.deleted = syncResult.deleted.concat(
Array.from(oldById.values())
);
console.debug(
lazy.console.debug(
`${this.identifier} ${syncResult.created.length} created. ${syncResult.updated.length} updated. ${syncResult.deleted.length} deleted.`
);
}
@ -1096,7 +1105,7 @@ class RemoteSettingsClient extends EventEmitter {
if (!this.filterFunc) {
return data;
}
const environment = cacheProxy(ClientEnvironmentBase);
const environment = cacheProxy(lazy.ClientEnvironmentBase);
const dataPromises = data.map(e => this.filterFunc(e, environment));
const results = await Promise.all(dataPromises);
return results.filter(Boolean);

View File

@ -5,8 +5,10 @@
var EXPORTED_SYMBOLS = ["RemoteSettingsTimer"];
const lazy = {};
ChromeUtils.defineModuleGetter(
this,
lazy,
"RemoteSettings",
"resource://services-settings/remote-settings.js"
);
@ -19,7 +21,7 @@ RemoteSettingsTimer.prototype = {
// By default, this timer fires once every 24 hours. See the "services.settings.poll_interval" pref.
notify(timer) {
RemoteSettings.pollChanges({ trigger: "timer" }).catch(e =>
lazy.RemoteSettings.pollChanges({ trigger: "timer" }).catch(e =>
Cu.reportError(e)
);
},

View File

@ -16,21 +16,23 @@ const { setTimeout, clearTimeout } = ChromeUtils.import(
var EXPORTED_SYMBOLS = ["RemoteSettingsWorker"];
const lazy = {};
XPCOMUtils.defineLazyPreferenceGetter(
this,
lazy,
"gMaxIdleMilliseconds",
"services.settings.worker_idle_max_milliseconds",
30 * 1000 // Default of 30 seconds.
);
ChromeUtils.defineModuleGetter(
this,
lazy,
"AsyncShutdown",
"resource://gre/modules/AsyncShutdown.jsm"
);
ChromeUtils.defineModuleGetter(
this,
lazy,
"SharedUtils",
"resource://services-settings/SharedUtils.jsm"
);
@ -128,7 +130,7 @@ class Worker {
} else {
this.idleTimeoutId = setTimeout(() => {
this.stop();
}, gMaxIdleMilliseconds);
}, lazy.gMaxIdleMilliseconds);
}
}
}
@ -187,7 +189,7 @@ class Worker {
async checkContentHash(buffer, size, hash) {
// The implementation does little work on the current thread, so run the
// task on the current thread instead of the worker thread.
return SharedUtils.checkContentHash(buffer, size, hash);
return lazy.SharedUtils.checkContentHash(buffer, size, hash);
}
}
@ -198,7 +200,7 @@ class Worker {
// fine. If we ever start creating more than one Worker instance, this
// code will need adjusting to deal with that.
try {
AsyncShutdown.profileBeforeChange.addBlocker(
lazy.AsyncShutdown.profileBeforeChange.addBlocker(
"Remote Settings profile-before-change",
async () => {
// First, indicate we've shut down.

View File

@ -6,7 +6,9 @@ const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
XPCOMUtils.defineLazyModuleGetters(this, {
const lazy = {};
XPCOMUtils.defineLazyModuleGetters(lazy, {
KeyValueService: "resource://gre/modules/kvstore.jsm",
});
@ -109,7 +111,7 @@ class SyncHistory {
// Get and cache a handle to the kvstore.
const dir = PathUtils.join(PathUtils.profileDir, "settings");
await IOUtils.makeDirectory(dir);
this.#store = await KeyValueService.getOrCreate(dir, "synchistory");
this.#store = await lazy.KeyValueService.getOrCreate(dir, "synchistory");
}
return this.#store;
}

View File

@ -12,29 +12,31 @@ const { ServiceRequest } = ChromeUtils.import(
"resource://gre/modules/ServiceRequest.jsm"
);
XPCOMUtils.defineLazyModuleGetters(this, {
const lazy = {};
XPCOMUtils.defineLazyModuleGetters(lazy, {
SharedUtils: "resource://services-settings/SharedUtils.jsm",
AppConstants: "resource://gre/modules/AppConstants.jsm",
});
XPCOMUtils.defineLazyServiceGetter(
this,
lazy,
"CaptivePortalService",
"@mozilla.org/network/captive-portal-service;1",
"nsICaptivePortalService"
);
XPCOMUtils.defineLazyServiceGetter(
this,
lazy,
"gNetworkLinkService",
"@mozilla.org/network/network-link-service;1",
"nsINetworkLinkService"
);
XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]);
XPCOMUtils.defineLazyGlobalGetters(lazy, ["fetch"]);
// Create a new instance of the ConsoleAPI so we can control the maxLogLevel with a pref.
// See LOG_LEVELS in Console.jsm. Common examples: "all", "debug", "info", "warn", "error".
XPCOMUtils.defineLazyGetter(this, "log", () => {
XPCOMUtils.defineLazyGetter(lazy, "log", () => {
const { ConsoleAPI } = ChromeUtils.import(
"resource://gre/modules/Console.jsm"
);
@ -45,7 +47,7 @@ XPCOMUtils.defineLazyGetter(this, "log", () => {
});
});
XPCOMUtils.defineLazyGetter(this, "isRunningTests", () => {
XPCOMUtils.defineLazyGetter(lazy, "isRunningTests", () => {
const env = Cc["@mozilla.org/process/environment;1"].getService(
Ci.nsIEnvironment
);
@ -59,13 +61,13 @@ XPCOMUtils.defineLazyGetter(this, "isRunningTests", () => {
// Overriding the server URL is normally disabled on Beta and Release channels,
// except under some conditions.
XPCOMUtils.defineLazyGetter(this, "allowServerURLOverride", () => {
if (!AppConstants.RELEASE_OR_BETA) {
XPCOMUtils.defineLazyGetter(lazy, "allowServerURLOverride", () => {
if (!lazy.AppConstants.RELEASE_OR_BETA) {
// Always allow to override the server URL on Nightly/DevEdition.
return true;
}
if (isRunningTests) {
if (lazy.isRunningTests) {
return true;
}
@ -82,14 +84,14 @@ XPCOMUtils.defineLazyGetter(this, "allowServerURLOverride", () => {
});
XPCOMUtils.defineLazyPreferenceGetter(
this,
lazy,
"gServerURL",
"services.settings.server",
AppConstants.REMOTE_SETTINGS_SERVER_URL
lazy.AppConstants.REMOTE_SETTINGS_SERVER_URL
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
lazy,
"gPreviewEnabled",
"services.settings.preview_enabled",
false
@ -101,9 +103,9 @@ function _isUndefined(value) {
var Utils = {
get SERVER_URL() {
return allowServerURLOverride
? gServerURL
: AppConstants.REMOTE_SETTINGS_SERVER_URL;
return lazy.allowServerURLOverride
? lazy.gServerURL
: lazy.AppConstants.REMOTE_SETTINGS_SERVER_URL;
},
CHANGES_PATH: "/buckets/monitor/collections/changes/changeset",
@ -111,21 +113,21 @@ var Utils = {
/**
* Logger instance.
*/
log,
log: lazy.log,
get LOAD_DUMPS() {
// Load dumps only if pulling data from the production server, or in tests.
return (
this.SERVER_URL == AppConstants.REMOTE_SETTINGS_SERVER_URL ||
isRunningTests
this.SERVER_URL == lazy.AppConstants.REMOTE_SETTINGS_SERVER_URL ||
lazy.isRunningTests
);
},
get PREVIEW_MODE() {
// We want to offer the ability to set preview mode via a preference
// for consumers who want to pull from the preview bucket on startup.
if (_isUndefined(this._previewModeEnabled) && allowServerURLOverride) {
return gPreviewEnabled;
if (_isUndefined(this._previewModeEnabled) && lazy.allowServerURLOverride) {
return lazy.gPreviewEnabled;
}
return !!this._previewModeEnabled;
},
@ -176,11 +178,12 @@ var Utils = {
try {
return (
Services.io.offline ||
CaptivePortalService.state == CaptivePortalService.LOCKED_PORTAL ||
!gNetworkLinkService.isLinkUp
lazy.CaptivePortalService.state ==
lazy.CaptivePortalService.LOCKED_PORTAL ||
!lazy.gNetworkLinkService.isLinkUp
);
} catch (ex) {
log.warn("Could not determine network status.", ex);
lazy.log.warn("Could not determine network status.", ex);
}
return false;
},
@ -279,7 +282,7 @@ var Utils = {
*/
async hasLocalDump(bucket, collection) {
try {
await fetch(
await lazy.fetch(
`resource://app/defaults/settings/${bucket}/${collection}.json`,
{
method: "HEAD",
@ -303,12 +306,12 @@ var Utils = {
if (!this._dumpStatsInitPromise) {
this._dumpStatsInitPromise = (async () => {
try {
let res = await fetch(
let res = await lazy.fetch(
"resource://app/defaults/settings/last_modified.json"
);
this._dumpStats = await res.json();
} catch (e) {
log.warn(`Failed to load last_modified.json: ${e}`);
lazy.log.warn(`Failed to load last_modified.json: ${e}`);
this._dumpStats = {};
}
delete this._dumpStatsInitPromise;
@ -319,7 +322,7 @@ var Utils = {
const identifier = `${bucket}/${collection}`;
let lastModified = this._dumpStats[identifier];
if (lastModified === undefined) {
const { timestamp: dumpTimestamp } = await SharedUtils.loadJSONDump(
const { timestamp: dumpTimestamp } = await lazy.SharedUtils.loadJSONDump(
bucket,
collection
);

View File

@ -17,7 +17,9 @@ const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
XPCOMUtils.defineLazyModuleGetters(this, {
const lazy = {};
XPCOMUtils.defineLazyModuleGetters(lazy, {
AppConstants: "resource://gre/modules/AppConstants.jsm",
UptakeTelemetry: "resource://services-common/uptake-telemetry.js",
pushBroadcastService: "resource://gre/modules/PushBroadcastService.jsm",
@ -29,7 +31,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
RemoteSettingsWorker: "resource://services-settings/RemoteSettingsWorker.jsm",
});
XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]);
XPCOMUtils.defineLazyGlobalGetters(lazy, ["fetch"]);
const PREF_SETTINGS_BRANCH = "services.settings.";
const PREF_SETTINGS_SERVER_BACKOFF = "server.backoff";
@ -51,19 +53,19 @@ const BROADCAST_ID = "remote-settings/monitor_changes";
// Signer to be used when not specified (see Ci.nsIContentSignatureVerifier).
const DEFAULT_SIGNER = "remote-settings.content-signature.mozilla.org";
XPCOMUtils.defineLazyGetter(this, "gPrefs", () => {
XPCOMUtils.defineLazyGetter(lazy, "gPrefs", () => {
return Services.prefs.getBranch(PREF_SETTINGS_BRANCH);
});
XPCOMUtils.defineLazyGetter(this, "console", () => Utils.log);
XPCOMUtils.defineLazyGetter(lazy, "console", () => lazy.Utils.log);
XPCOMUtils.defineLazyGetter(this, "gSyncHistory", () => {
const prefSize = gPrefs.getIntPref(PREF_SETTINGS_SYNC_HISTORY_SIZE, 100);
XPCOMUtils.defineLazyGetter(lazy, "gSyncHistory", () => {
const prefSize = lazy.gPrefs.getIntPref(PREF_SETTINGS_SYNC_HISTORY_SIZE, 100);
const size = Math.min(Math.max(prefSize, 1000), 10);
return new SyncHistory(TELEMETRY_SOURCE_SYNC, { size });
return new lazy.SyncHistory(TELEMETRY_SOURCE_SYNC, { size });
});
XPCOMUtils.defineLazyPreferenceGetter(
this,
lazy,
"gPrefBrokenSyncThreshold",
PREF_SETTINGS_BRANCH + PREF_SETTINGS_SYNC_HISTORY_ERROR_THRESHOLD,
10
@ -86,7 +88,7 @@ async function jexlFilterFunc(entry, environment) {
const context = {
env: environment,
};
result = await FilterExpressions.eval(filter_expression, context);
result = await lazy.FilterExpressions.eval(filter_expression, context);
} catch (e) {
Cu.reportError(e);
}
@ -114,7 +116,7 @@ function remoteSettingsFunction() {
// Get or instantiate a remote settings client.
if (!_clients.has(collectionName)) {
// Register a new client!
const c = new RemoteSettingsClient(collectionName, {
const c = new lazy.RemoteSettingsClient(collectionName, {
...defaultOptions,
...options,
});
@ -123,7 +125,7 @@ function remoteSettingsFunction() {
// Invalidate the polling status, since we want the new collection to
// be taken into account.
_invalidatePolling = true;
console.debug(`Instantiated new client ${c.identifier}`);
lazy.console.debug(`Instantiated new client ${c.identifier}`);
}
return _clients.get(collectionName);
};
@ -145,12 +147,14 @@ function remoteSettingsFunction() {
// this client is known but it was not registered yet (eg. calling module not "imported" yet).
if (
bucketName ==
Utils.actualBucketName(AppConstants.REMOTE_SETTINGS_DEFAULT_BUCKET)
lazy.Utils.actualBucketName(
lazy.AppConstants.REMOTE_SETTINGS_DEFAULT_BUCKET
)
) {
const c = new RemoteSettingsClient(collectionName, defaultOptions);
const c = new lazy.RemoteSettingsClient(collectionName, defaultOptions);
const [dbExists, localDump] = await Promise.all([
Utils.hasLocalData(c),
Utils.hasLocalDump(bucketName, collectionName),
lazy.Utils.hasLocalData(c),
lazy.Utils.hasLocalDump(bucketName, collectionName),
]);
if (dbExists || localDump) {
return c;
@ -160,7 +164,7 @@ function remoteSettingsFunction() {
// Mainly because we cannot guess which `signerName` has to be used for example.
// And we don't want to synchronize data for collections in the main bucket that are
// completely unknown (ie. no database and no JSON dump).
console.debug(`No known client for ${bucketName}/${collectionName}`);
lazy.console.debug(`No known client for ${bucketName}/${collectionName}`);
return null;
}
@ -171,11 +175,11 @@ function remoteSettingsFunction() {
*/
async function isSynchronizationBroken() {
// The minimum number of errors is customizable, but with a maximum.
const threshold = Math.min(gPrefBrokenSyncThreshold, 20);
const threshold = Math.min(lazy.gPrefBrokenSyncThreshold, 20);
// Read history of synchronization past statuses.
const pastEntries = await gSyncHistory.list();
const pastEntries = await lazy.gSyncHistory.list();
const lastSuccessIdx = pastEntries.findIndex(
e => e.status == UptakeTelemetry.STATUS.SUCCESS
e => e.status == lazy.UptakeTelemetry.STATUS.SUCCESS
);
return (
// Only errors since last success.
@ -201,9 +205,9 @@ function remoteSettingsFunction() {
} = {}) => {
// When running in full mode, we ignore last polling status.
if (full) {
gPrefs.clearUserPref(PREF_SETTINGS_SERVER_BACKOFF);
gPrefs.clearUserPref(PREF_SETTINGS_LAST_UPDATE);
gPrefs.clearUserPref(PREF_SETTINGS_LAST_ETAG);
lazy.gPrefs.clearUserPref(PREF_SETTINGS_SERVER_BACKOFF);
lazy.gPrefs.clearUserPref(PREF_SETTINGS_LAST_UPDATE);
lazy.gPrefs.clearUserPref(PREF_SETTINGS_LAST_ETAG);
}
let pollTelemetryArgs = {
@ -211,11 +215,11 @@ function remoteSettingsFunction() {
trigger,
};
if (Utils.isOffline) {
console.info("Network is offline. Give up.");
await UptakeTelemetry.report(
if (lazy.Utils.isOffline) {
lazy.console.info("Network is offline. Give up.");
await lazy.UptakeTelemetry.report(
TELEMETRY_COMPONENT,
UptakeTelemetry.STATUS.NETWORK_OFFLINE_ERROR,
lazy.UptakeTelemetry.STATUS.NETWORK_OFFLINE_ERROR,
pollTelemetryArgs
);
return;
@ -224,17 +228,17 @@ function remoteSettingsFunction() {
const startedAt = new Date();
// Check if the server backoff time is elapsed.
if (gPrefs.prefHasUserValue(PREF_SETTINGS_SERVER_BACKOFF)) {
const backoffReleaseTime = gPrefs.getCharPref(
if (lazy.gPrefs.prefHasUserValue(PREF_SETTINGS_SERVER_BACKOFF)) {
const backoffReleaseTime = lazy.gPrefs.getCharPref(
PREF_SETTINGS_SERVER_BACKOFF
);
const remainingMilliseconds =
parseInt(backoffReleaseTime, 10) - Date.now();
if (remainingMilliseconds > 0) {
// Backoff time has not elapsed yet.
await UptakeTelemetry.report(
await lazy.UptakeTelemetry.report(
TELEMETRY_COMPONENT,
UptakeTelemetry.STATUS.BACKOFF,
lazy.UptakeTelemetry.STATUS.BACKOFF,
pollTelemetryArgs
);
throw new Error(
@ -243,11 +247,11 @@ function remoteSettingsFunction() {
)}s.`
);
} else {
gPrefs.clearUserPref(PREF_SETTINGS_SERVER_BACKOFF);
lazy.gPrefs.clearUserPref(PREF_SETTINGS_SERVER_BACKOFF);
}
}
console.info("Start polling for changes");
lazy.console.info("Start polling for changes");
Services.obs.notifyObservers(
null,
"remote-settings:changes-poll-start",
@ -258,11 +262,11 @@ function remoteSettingsFunction() {
// Every time we register a new client, we have to fetch the whole list again.
const lastEtag = _invalidatePolling
? ""
: gPrefs.getCharPref(PREF_SETTINGS_LAST_ETAG, "");
: lazy.gPrefs.getCharPref(PREF_SETTINGS_LAST_ETAG, "");
let pollResult;
try {
pollResult = await Utils.fetchLatestChanges(Utils.SERVER_URL, {
pollResult = await lazy.Utils.fetchLatestChanges(lazy.Utils.SERVER_URL, {
expectedTimestamp,
lastEtag,
});
@ -270,19 +274,19 @@ function remoteSettingsFunction() {
// Report polling error to Uptake Telemetry.
let reportStatus;
if (/JSON\.parse/.test(e.message)) {
reportStatus = UptakeTelemetry.STATUS.PARSE_ERROR;
reportStatus = lazy.UptakeTelemetry.STATUS.PARSE_ERROR;
} else if (/content-type/.test(e.message)) {
reportStatus = UptakeTelemetry.STATUS.CONTENT_ERROR;
reportStatus = lazy.UptakeTelemetry.STATUS.CONTENT_ERROR;
} else if (/Server/.test(e.message)) {
reportStatus = UptakeTelemetry.STATUS.SERVER_ERROR;
reportStatus = lazy.UptakeTelemetry.STATUS.SERVER_ERROR;
} else if (/Timeout/.test(e.message)) {
reportStatus = UptakeTelemetry.STATUS.TIMEOUT_ERROR;
reportStatus = lazy.UptakeTelemetry.STATUS.TIMEOUT_ERROR;
} else if (/NetworkError/.test(e.message)) {
reportStatus = UptakeTelemetry.STATUS.NETWORK_ERROR;
reportStatus = lazy.UptakeTelemetry.STATUS.NETWORK_ERROR;
} else {
reportStatus = UptakeTelemetry.STATUS.UNKNOWN_ERROR;
reportStatus = lazy.UptakeTelemetry.STATUS.UNKNOWN_ERROR;
}
await UptakeTelemetry.report(
await lazy.UptakeTelemetry.report(
TELEMETRY_COMPONENT,
reportStatus,
pollTelemetryArgs
@ -305,9 +309,9 @@ function remoteSettingsFunction() {
// Report polling success to Uptake Telemetry.
const reportStatus =
changes.length === 0
? UptakeTelemetry.STATUS.UP_TO_DATE
: UptakeTelemetry.STATUS.SUCCESS;
await UptakeTelemetry.report(
? lazy.UptakeTelemetry.STATUS.UP_TO_DATE
: lazy.UptakeTelemetry.STATUS.SUCCESS;
await lazy.UptakeTelemetry.report(
TELEMETRY_COMPONENT,
reportStatus,
pollTelemetryArgs
@ -315,20 +319,23 @@ function remoteSettingsFunction() {
// Check if the server asked the clients to back off (for next poll).
if (backoffSeconds) {
console.info(
lazy.console.info(
"Server asks clients to backoff for ${backoffSeconds} seconds"
);
const backoffReleaseTime = Date.now() + backoffSeconds * 1000;
gPrefs.setCharPref(PREF_SETTINGS_SERVER_BACKOFF, backoffReleaseTime);
lazy.gPrefs.setCharPref(PREF_SETTINGS_SERVER_BACKOFF, backoffReleaseTime);
}
// Record new update time and the difference between local and server time.
// Negative clockDifference means local time is behind server time
// by the absolute of that value in seconds (positive means it's ahead)
const clockDifference = Math.floor((Date.now() - serverTimeMillis) / 1000);
gPrefs.setIntPref(PREF_SETTINGS_CLOCK_SKEW_SECONDS, clockDifference);
lazy.gPrefs.setIntPref(PREF_SETTINGS_CLOCK_SKEW_SECONDS, clockDifference);
const checkedServerTimeInSeconds = Math.round(serverTimeMillis / 1000);
gPrefs.setIntPref(PREF_SETTINGS_LAST_UPDATE, checkedServerTimeInSeconds);
lazy.gPrefs.setIntPref(
PREF_SETTINGS_LAST_UPDATE,
checkedServerTimeInSeconds
);
// Iterate through the collections version info and initiate a synchronization
// on the related remote settings clients.
@ -352,7 +359,7 @@ function remoteSettingsFunction() {
checkedServerTimeInSeconds
);
} catch (e) {
console.error(e);
lazy.console.error(e);
if (!firstError) {
firstError = e;
firstError.details = change;
@ -374,14 +381,14 @@ function remoteSettingsFunction() {
if (firstError) {
// Report the global synchronization failure. Individual uptake reports will also have been sent for each collection.
const status = UptakeTelemetry.STATUS.SYNC_ERROR;
await UptakeTelemetry.report(
const status = lazy.UptakeTelemetry.STATUS.SYNC_ERROR;
await lazy.UptakeTelemetry.report(
TELEMETRY_COMPONENT,
status,
syncTelemetryArgs
);
// Keep track of sync failure in history.
await gSyncHistory
await lazy.gSyncHistory
.store(currentEtag, status, {
expectedTimestamp,
errorName: firstError.name,
@ -397,9 +404,9 @@ function remoteSettingsFunction() {
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1729400
// and https://bugzilla.mozilla.org/show_bug.cgi?id=1658597
if (await isSynchronizationBroken()) {
await UptakeTelemetry.report(
await lazy.UptakeTelemetry.report(
TELEMETRY_COMPONENT,
UptakeTelemetry.STATUS.SYNC_BROKEN_ERROR,
lazy.UptakeTelemetry.STATUS.SYNC_BROKEN_ERROR,
syncTelemetryArgs
);
@ -414,21 +421,21 @@ function remoteSettingsFunction() {
}
// Save current Etag for next poll.
gPrefs.setCharPref(PREF_SETTINGS_LAST_ETAG, currentEtag);
lazy.gPrefs.setCharPref(PREF_SETTINGS_LAST_ETAG, currentEtag);
// Report the global synchronization success.
const status = UptakeTelemetry.STATUS.SUCCESS;
await UptakeTelemetry.report(
const status = lazy.UptakeTelemetry.STATUS.SUCCESS;
await lazy.UptakeTelemetry.report(
TELEMETRY_COMPONENT,
status,
syncTelemetryArgs
);
// Keep track of sync success in history.
await gSyncHistory
await lazy.gSyncHistory
.store(currentEtag, status)
.catch(error => Cu.reportError(error));
console.info("Polling for changes done");
lazy.console.info("Polling for changes done");
Services.obs.notifyObservers(null, "remote-settings:changes-poll-end");
};
@ -441,7 +448,7 @@ function remoteSettingsFunction() {
*/
remoteSettings.enablePreviewMode = enabled => {
// Set the flag for future clients.
Utils.enablePreviewMode(enabled);
lazy.Utils.enablePreviewMode(enabled);
// Enable it on existing clients.
for (const client of _clients.values()) {
client.refreshBucketName();
@ -456,7 +463,7 @@ function remoteSettingsFunction() {
const {
changes,
currentEtag: serverTimestamp,
} = await Utils.fetchLatestChanges(Utils.SERVER_URL);
} = await lazy.Utils.fetchLatestChanges(lazy.Utils.SERVER_URL);
const collections = await Promise.all(
changes.map(async change => {
@ -482,19 +489,19 @@ function remoteSettingsFunction() {
);
return {
serverURL: Utils.SERVER_URL,
pollingEndpoint: Utils.SERVER_URL + Utils.CHANGES_PATH,
serverURL: lazy.Utils.SERVER_URL,
pollingEndpoint: lazy.Utils.SERVER_URL + lazy.Utils.CHANGES_PATH,
serverTimestamp,
localTimestamp: gPrefs.getCharPref(PREF_SETTINGS_LAST_ETAG, null),
lastCheck: gPrefs.getIntPref(PREF_SETTINGS_LAST_UPDATE, 0),
mainBucket: Utils.actualBucketName(
AppConstants.REMOTE_SETTINGS_DEFAULT_BUCKET
localTimestamp: lazy.gPrefs.getCharPref(PREF_SETTINGS_LAST_ETAG, null),
lastCheck: lazy.gPrefs.getIntPref(PREF_SETTINGS_LAST_UPDATE, 0),
mainBucket: lazy.Utils.actualBucketName(
lazy.AppConstants.REMOTE_SETTINGS_DEFAULT_BUCKET
),
defaultSigner: DEFAULT_SIGNER,
previewMode: Utils.PREVIEW_MODE,
previewMode: lazy.Utils.PREVIEW_MODE,
collections: collections.filter(c => !!c),
history: {
[TELEMETRY_SOURCE_SYNC]: await gSyncHistory.list(),
[TELEMETRY_SOURCE_SYNC]: await lazy.gSyncHistory.list(),
},
};
};
@ -521,19 +528,26 @@ function remoteSettingsFunction() {
* Startup function called from nsBrowserGlue.
*/
remoteSettings.init = () => {
console.info("Initialize Remote Settings");
lazy.console.info("Initialize Remote Settings");
// Hook the Push broadcast and RemoteSettings polling.
// When we start on a new profile there will be no ETag stored.
// Use an arbitrary ETag that is guaranteed not to occur.
// This will trigger a broadcast message but that's fine because we
// will check the changes on each collection and retrieve only the
// changes (e.g. nothing if we have a dump with the same data).
const currentVersion = gPrefs.getStringPref(PREF_SETTINGS_LAST_ETAG, '"0"');
const currentVersion = lazy.gPrefs.getStringPref(
PREF_SETTINGS_LAST_ETAG,
'"0"'
);
const moduleInfo = {
moduleURI: __URI__,
symbolName: "remoteSettingsBroadcastHandler",
};
pushBroadcastService.addListener(BROADCAST_ID, currentVersion, moduleInfo);
lazy.pushBroadcastService.addListener(
BROADCAST_ID,
currentVersion,
moduleInfo
);
};
return remoteSettings;
@ -545,11 +559,11 @@ var remoteSettingsBroadcastHandler = {
async receivedBroadcastMessage(version, broadcastID, context) {
const { phase } = context;
const isStartup = [
pushBroadcastService.PHASES.HELLO,
pushBroadcastService.PHASES.REGISTER,
lazy.pushBroadcastService.PHASES.HELLO,
lazy.pushBroadcastService.PHASES.REGISTER,
].includes(phase);
console.info(
lazy.console.info(
`Push notification received (version=${version} phase=${phase})`
);