Bug 1377533 - Remove scattered references to Kinto and Sqlite in blocklist clients r=glasserc,mgoodwin

MozReview-Commit-ID: FExozSDHgNN

--HG--
extra : rebase_source : 892c1c5825ac20243ce74118d4df437ec99cfe2c
This commit is contained in:
Mathieu Leplatre 2017-06-30 12:07:28 -07:00
Родитель c08d0d1a58
Коммит b4905330e7
6 изменённых файлов: 207 добавлений и 278 удалений

Просмотреть файл

@ -49,10 +49,10 @@ const PREF_BLOCKLIST_ENFORCE_SIGNING = "services.blocklist.signing.enfor
const INVALID_SIGNATURE = "Invalid content/signature";
// FIXME: this was the default path in earlier versions of
// This was the default path in earlier versions of
// FirefoxAdapter, so for backwards compatibility we maintain this
// filename, even though it isn't descriptive of who is using it.
this.KINTO_STORAGE_PATH = "kinto.sqlite";
const KINTO_STORAGE_PATH = "kinto.sqlite";
@ -114,6 +114,37 @@ class BlocklistClient {
return `${identifier}.json`;
}
/**
* Open the underlying Kinto collection, using the appropriate adapter and
* options. This acts as a context manager where the connection is closed
* once the specified `callback` has finished.
*
* @param {callback} function the async function to execute with the open SQlite connection.
* @param {Object} options additional advanced options.
* @param {string} options.bucket override bucket name of client (default: this.bucketName)
* @param {string} options.collection override collection name of client (default: this.collectionName)
* @param {string} options.path override default Sqlite path (default: kinto.sqlite)
* @param {string} options.hooks hooks to execute on synchronization (see Kinto.js docs)
*/
async openCollection(callback, options = {}) {
const { bucket = this.bucketName, path = KINTO_STORAGE_PATH } = options;
if (!this._kinto) {
this._kinto = new Kinto({bucket, adapter: FirefoxAdapter});
}
let sqliteHandle;
try {
sqliteHandle = await FirefoxAdapter.openConnection({path});
const colOptions = Object.assign({adapterOptions: {sqliteHandle}}, options);
const {collection: collectionName = this.collectionName} = options;
const collection = this._kinto.collection(collectionName, colOptions);
return await callback(collection);
} finally {
if (sqliteHandle) {
await sqliteHandle.close();
}
}
}
/**
* Load the the JSON file distributed with the release for this blocklist.
*
@ -186,118 +217,106 @@ class BlocklistClient {
const enforceCollectionSigning =
Services.prefs.getBoolPref(PREF_BLOCKLIST_ENFORCE_SIGNING);
if (!this._kinto) {
this._kinto = new Kinto({
bucket: this.bucketName,
adapter: FirefoxAdapter,
});
}
// if there is a signerName and collection signing is enforced, add a
// hook for incoming changes that validates the signature
let hooks;
const colOptions = {};
if (this.signerName && enforceCollectionSigning) {
hooks = {
colOptions.hooks = {
"incoming-changes": [(payload, collection) => {
return this.validateCollectionSignature(remote, payload, collection);
}]
}
}
let sqliteHandle;
let reportStatus = null;
try {
// Synchronize remote data into a local Sqlite DB.
sqliteHandle = await FirefoxAdapter.openConnection({path: KINTO_STORAGE_PATH});
const options = {
hooks,
adapterOptions: {sqliteHandle},
};
const collection = this._kinto.collection(this.collectionName, options);
return await this.openCollection(async (collection) => {
// Synchronize remote data into a local Sqlite DB.
let collectionLastModified = await collection.db.getLastModified();
let collectionLastModified = await collection.db.getLastModified();
// If there is no data currently in the collection, attempt to import
// initial data from the application defaults.
// This allows to avoid synchronizing the whole collection content on
// cold start.
if (!collectionLastModified && loadDump) {
try {
const initialData = await this.loadDumpFile();
await collection.loadDump(initialData.data);
collectionLastModified = await collection.db.getLastModified();
} catch (e) {
// Report but go-on.
Cu.reportError(e);
}
}
// If the data is up to date, there's no need to sync. We still need
// to record the fact that a check happened.
if (lastModified <= collectionLastModified) {
this.updateLastCheck(serverTime);
reportStatus = UptakeTelemetry.STATUS.UP_TO_DATE;
return;
}
// Fetch changes from server.
try {
// Server changes have priority during synchronization.
const strategy = Kinto.syncStrategy.SERVER_WINS;
const {ok} = await collection.sync({remote, strategy});
if (!ok) {
// Some synchronization conflicts occured.
reportStatus = UptakeTelemetry.STATUS.CONFLICT_ERROR;
throw new Error("Sync failed");
}
} catch (e) {
if (e.message == INVALID_SIGNATURE) {
// Signature verification failed during synchronzation.
reportStatus = 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.
const payload = await fetchRemoteCollection(remote, collection);
// If there is no data currently in the collection, attempt to import
// initial data from the application defaults.
// This allows to avoid synchronizing the whole collection content on
// cold start.
if (!collectionLastModified && loadDump) {
try {
await this.validateCollectionSignature(remote, payload, collection, {ignoreLocal: true});
const initialData = await this.loadDumpFile();
await collection.loadDump(initialData.data);
collectionLastModified = await collection.db.getLastModified();
} catch (e) {
reportStatus = UptakeTelemetry.STATUS.SIGNATURE_RETRY_ERROR;
// Report but go-on.
Cu.reportError(e);
}
}
// If the data is up to date, there's no need to sync. We still need
// to record the fact that a check happened.
if (lastModified <= collectionLastModified) {
this.updateLastCheck(serverTime);
reportStatus = UptakeTelemetry.STATUS.UP_TO_DATE;
return;
}
// Fetch changes from server.
try {
// Server changes have priority during synchronization.
const strategy = Kinto.syncStrategy.SERVER_WINS;
const {ok} = await collection.sync({remote, strategy});
if (!ok) {
// Some synchronization conflicts occured.
reportStatus = UptakeTelemetry.STATUS.CONFLICT_ERROR;
throw new Error("Sync failed");
}
} catch (e) {
if (e.message == INVALID_SIGNATURE) {
// Signature verification failed during synchronzation.
reportStatus = 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.
const payload = await fetchRemoteCollection(remote, collection);
try {
await this.validateCollectionSignature(remote, payload, collection, {ignoreLocal: true});
} catch (e) {
reportStatus = UptakeTelemetry.STATUS.SIGNATURE_RETRY_ERROR;
throw e;
}
// if the signature is good (we haven't thrown), and the remote
// last_modified is newer than the local last_modified, replace the
// local data
const localLastModified = await collection.db.getLastModified();
if (payload.last_modified >= localLastModified) {
await collection.clear();
await collection.loadDump(payload.data);
}
} else {
// The sync has thrown, it can be a network or a general error.
if (/NetworkError/.test(e.message)) {
reportStatus = UptakeTelemetry.STATUS.NETWORK_ERROR;
} else if (/Backoff/.test(e.message)) {
reportStatus = UptakeTelemetry.STATUS.BACKOFF;
} else {
reportStatus = UptakeTelemetry.STATUS.SYNC_ERROR;
}
throw e;
}
// if the signature is good (we haven't thrown), and the remote
// last_modified is newer than the local last_modified, replace the
// local data
const localLastModified = await collection.db.getLastModified();
if (payload.last_modified >= localLastModified) {
await collection.clear();
await collection.loadDump(payload.data);
}
} else {
// The sync has thrown, it can be a network or a general error.
if (/NetworkError/.test(e.message)) {
reportStatus = UptakeTelemetry.STATUS.NETWORK_ERROR;
} else if (/Backoff/.test(e.message)) {
reportStatus = UptakeTelemetry.STATUS.BACKOFF;
} else {
reportStatus = UptakeTelemetry.STATUS.SYNC_ERROR;
}
}
// Read local collection of records.
const {data} = await collection.list();
// Handle the obtained records (ie. apply locally).
try {
await this.processCallback(data);
} catch (e) {
reportStatus = UptakeTelemetry.STATUS.APPLY_ERROR;
throw e;
}
}
// Read local collection of records.
const {data} = await collection.list();
// Handle the obtained records (ie. apply locally).
try {
await this.processCallback(data);
} catch (e) {
reportStatus = UptakeTelemetry.STATUS.APPLY_ERROR;
throw e;
}
// Track last update.
this.updateLastCheck(serverTime);
// Track last update.
this.updateLastCheck(serverTime);
}, colOptions);
} catch (e) {
// No specific error was tracked, mark it as unknown.
if (reportStatus === null) {
@ -305,9 +324,6 @@ class BlocklistClient {
}
throw e;
} finally {
if (sqliteHandle) {
await sqliteHandle.close();
}
// No error was reported, this is a success!
if (reportStatus === null) {
reportStatus = UptakeTelemetry.STATUS.SUCCESS;

Просмотреть файл

@ -3,31 +3,12 @@ const { Constructor: CC } = Components;
Cu.import("resource://testing-common/httpd.js");
const { OneCRLBlocklistClient } = Cu.import("resource://services-common/blocklist-clients.js", {});
const { Kinto } = Cu.import("resource://services-common/kinto-offline-client.js", {});
const { FirefoxAdapter } = Cu.import("resource://services-common/kinto-storage-adapter.js", {});
const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
"nsIBinaryInputStream", "setInputStream");
let server;
// set up what we need to make storage adapters
let sqliteHandle;
const KINTO_FILENAME = "kinto.sqlite";
function do_get_kinto_collection(collectionName) {
let config = {
// Set the remote to be some server that will cause test failure when
// hit since we should never hit the server directly, only via maybeSync()
remote: "https://firefox.settings.services.mozilla.com/v1/",
// Set up the adapter and bucket as normal
adapter: FirefoxAdapter,
adapterOptions: {sqliteHandle},
bucket: "blocklists"
};
return new Kinto(config).collection(collectionName);
}
// Some simple tests to demonstrate that the logic inside maybeSync works
// correctly and that simple kinto operations are working as expected. There
// are more tests for core Kinto.js (and its storage adapter) in the
@ -67,14 +48,13 @@ add_task(async function test_something() {
// Test an empty db populates
await OneCRLBlocklistClient.maybeSync(2000, Date.now());
sqliteHandle = await FirefoxAdapter.openConnection({path: KINTO_FILENAME});
const collection = do_get_kinto_collection("certificates");
// Open the collection, verify it's been populated:
let list = await collection.list();
// We know there will be initial values from the JSON dump.
// (at least as many as in the dump shipped when this test was written).
do_check_true(list.data.length >= 363);
await OneCRLBlocklistClient.openCollection(async (collection) => {
// Open the collection, verify it's been populated:
const list = await collection.list();
// We know there will be initial values from the JSON dump.
// (at least as many as in the dump shipped when this test was written).
do_check_true(list.data.length >= 363);
});
// No sync will be intented if maybeSync() is up-to-date.
Services.prefs.clearUserPref("services.settings.server");
@ -86,26 +66,34 @@ add_task(async function test_something() {
// Restore server pref.
Services.prefs.setCharPref("services.settings.server", dummyServerURL);
// clear the collection, save a non-zero lastModified so we don't do
// import of initial data when we sync again.
await collection.clear();
// a lastModified value of 1000 means we get a remote collection with a
// single record
await collection.db.saveLastModified(1000);
await OneCRLBlocklistClient.openCollection(async (collection) => {
// clear the collection, save a non-zero lastModified so we don't do
// import of initial data when we sync again.
await collection.clear();
// a lastModified value of 1000 means we get a remote collection with a
// single record
await collection.db.saveLastModified(1000);
});
await OneCRLBlocklistClient.maybeSync(2000, Date.now());
// Open the collection, verify it's been updated:
// Our test data now has two records; both should be in the local collection
list = await collection.list();
do_check_eq(list.data.length, 1);
await OneCRLBlocklistClient.openCollection(async (collection) => {
// Open the collection, verify it's been updated:
// Our test data now has two records; both should be in the local collection
const list = await collection.list();
do_check_eq(list.data.length, 1);
});
// Test the db is updated when we call again with a later lastModified value
await OneCRLBlocklistClient.maybeSync(4000, Date.now());
// Open the collection, verify it's been updated:
// Our test data now has two records; both should be in the local collection
list = await collection.list();
do_check_eq(list.data.length, 3);
await OneCRLBlocklistClient.openCollection(async (collection) => {
// Open the collection, verify it's been updated:
// Our test data now has two records; both should be in the local collection
const list = await collection.list();
do_check_eq(list.data.length, 3);
});
// Try to maybeSync with the current lastModified value - no connection
// should be attempted.
@ -143,7 +131,6 @@ function run_test() {
do_register_cleanup(function() {
server.stop(() => { });
return sqliteHandle.close();
});
}

Просмотреть файл

@ -6,14 +6,11 @@ Cu.import("resource://gre/modules/Timer.jsm");
const { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm", {});
const { OS } = Cu.import("resource://gre/modules/osfile.jsm", {});
const { Kinto } = Cu.import("resource://services-common/kinto-offline-client.js", {});
const { FirefoxAdapter } = Cu.import("resource://services-common/kinto-storage-adapter.js", {});
const BlocklistClients = Cu.import("resource://services-common/blocklist-clients.js", {});
const { UptakeTelemetry } = Cu.import("resource://services-common/uptake-telemetry.js", {});
const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
"nsIBinaryInputStream", "setInputStream");
const kintoFilename = "kinto.sqlite";
const gBlocklistClients = [
{client: BlocklistClients.AddonBlocklistClient, testData: ["i808", "i720", "i539"]},
@ -24,18 +21,6 @@ const gBlocklistClients = [
let server;
function kintoCollection(collectionName, sqliteHandle) {
const config = {
// Set the remote to be some server that will cause test failure when
// hit since we should never hit the server directly, only via maybeSync()
remote: "https://firefox.settings.services.mozilla.com/v1/",
adapter: FirefoxAdapter,
adapterOptions: {sqliteHandle},
bucket: "blocklists"
};
return new Kinto(config).collection(collectionName);
}
async function readJSON(filepath) {
const binaryData = await OS.File.read(filepath);
const textData = (new TextDecoder()).decode(binaryData);
@ -48,14 +33,9 @@ async function clear_state() {
Services.prefs.clearUserPref(client.lastCheckTimePref);
// Clear local DB.
let sqliteHandle;
try {
sqliteHandle = await FirefoxAdapter.openConnection({path: kintoFilename});
const collection = kintoCollection(client.collectionName, sqliteHandle);
await client.openCollection(async (collection) => {
await collection.clear();
} finally {
await sqliteHandle.close();
}
});
// Remove JSON dumps folders in profile dir.
const dumpFile = OS.Path.join(OS.Constants.Path.profileDir, client.filename);
@ -120,11 +100,10 @@ add_task(async function test_initial_dump_is_loaded_as_synced_when_collection_is
await client.maybeSync(1, Date.now());
// Open the collection, verify the loaded data has status to synced:
const sqliteHandle = await FirefoxAdapter.openConnection({path: kintoFilename});
const collection = kintoCollection(client.collectionName, sqliteHandle);
const list = await collection.list();
equal(list.data[0]._status, "synced");
await sqliteHandle.close();
await client.openCollection(async (collection) => {
const list = await collection.list();
equal(list.data[0]._status, "synced");
});
}
});
add_task(clear_state);
@ -136,11 +115,10 @@ add_task(async function test_records_obtained_from_server_are_stored_in_db() {
// Open the collection, verify it's been populated:
// Our test data has a single record; it should be in the local collection
const sqliteHandle = await FirefoxAdapter.openConnection({path: kintoFilename});
let collection = kintoCollection(client.collectionName, sqliteHandle);
let list = await collection.list();
equal(list.data.length, 1);
await sqliteHandle.close();
await client.openCollection(async (collection) => {
const list = await collection.list();
equal(list.data.length, 1);
});
}
});
add_task(clear_state);
@ -149,14 +127,12 @@ add_task(async function test_records_changes_are_overwritten_by_server_changes()
const {client} = gBlocklistClients[0];
// Create some local conflicting data, and make sure it syncs without error.
const sqliteHandle = await FirefoxAdapter.openConnection({path: kintoFilename});
const collection = kintoCollection(client.collectionName, sqliteHandle);
await collection.create({
"versionRange": [],
"id": "9d500963-d80e-3a91-6e74-66f3811b99cc"
}, { useRecordId: true });
await sqliteHandle.close();
await client.openCollection(async (collection) => {
await collection.create({
"versionRange": [],
"id": "9d500963-d80e-3a91-6e74-66f3811b99cc"
}, { useRecordId: true });
});
await client.maybeSync(2000, Date.now(), {loadDump: false});
});
add_task(clear_state);
@ -284,10 +260,9 @@ add_task(async function test_telemetry_reports_if_sync_fails() {
const {client} = gBlocklistClients[0];
const serverTime = Date.now();
const sqliteHandle = await FirefoxAdapter.openConnection({path: kintoFilename});
const collection = kintoCollection(client.collectionName, sqliteHandle);
await collection.db.saveLastModified(9999);
await sqliteHandle.close();
await client.openCollection(async (collection) => {
await collection.db.saveLastModified(9999);
});
const startHistogram = getUptakeTelemetrySnapshot(client.identifier);
@ -304,15 +279,15 @@ add_task(clear_state);
add_task(async function test_telemetry_reports_unknown_errors() {
const {client} = gBlocklistClients[0];
const serverTime = Date.now();
const backup = FirefoxAdapter.openConnection;
FirefoxAdapter.openConnection = () => { throw new Error("Internal"); };
const backup = client.openCollection;
client.openCollection = () => { throw new Error("Internal"); };
const startHistogram = getUptakeTelemetrySnapshot(client.identifier);
try {
await client.maybeSync(2000, serverTime);
} catch (e) {}
FirefoxAdapter.openConnection = backup;
client.openCollection = backup;
const endHistogram = getUptakeTelemetrySnapshot(client.identifier);
const expectedIncrements = {[UptakeTelemetry.STATUS.UNKNOWN_ERROR]: 1};
checkUptakeTelemetry(startHistogram, endHistogram, expectedIncrements);

Просмотреть файл

@ -4,16 +4,9 @@ const { Constructor: CC } = Components;
Cu.import("resource://testing-common/httpd.js");
const { Kinto } = Cu.import("resource://services-common/kinto-offline-client.js", {});
const { FirefoxAdapter } = Cu.import("resource://services-common/kinto-storage-adapter.js", {});
const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
"nsIBinaryInputStream", "setInputStream");
const PREF_BLOCKLIST_PINNING_COLLECTION = "services.blocklist.pinning.collection";
const COLLECTION_NAME = "pins";
const KINTO_STORAGE_PATH = "kinto.sqlite";
// First, we need to setup appInfo or we can't do version checks
var id = "xpcshell@tests.mozilla.org";
var appName = "XPCShell";
@ -31,30 +24,9 @@ updateAppInfo({
let server;
function do_get_kinto_collection(connection, collectionName) {
let config = {
// Set the remote to be some server that will cause test failure when
// hit since we should never hit the server directly (any non-local
// request causes failure in tests), only via maybeSync()
remote: "https://firefox.settings.services.mozilla.com/v1/",
// Set up the adapter and bucket as normal
adapter: FirefoxAdapter,
adapterOptions: {sqliteHandle: connection},
bucket: "pinning"
};
let kintoClient = new Kinto(config);
return kintoClient.collection(collectionName);
}
// Some simple tests to demonstrate that the core preload sync operations work
// correctly and that simple kinto operations are working as expected.
add_task(async function test_something() {
// set the collection name explicitly - since there will be version
// specific collection names in prefs
Services.prefs.setCharPref(PREF_BLOCKLIST_PINNING_COLLECTION,
COLLECTION_NAME);
const { PinningPreloadClient } = Cu.import("resource://services-common/blocklist-clients.js", {});
const configPath = "/v1/";
@ -106,13 +78,12 @@ add_task(async function test_something() {
// Test an empty db populates
await PinningPreloadClient.maybeSync(2000, Date.now());
let connection = await FirefoxAdapter.openConnection({path: KINTO_STORAGE_PATH});
// Open the collection, verify it's been populated:
// Our test data has a single record; it should be in the local collection
let collection = do_get_kinto_collection(connection, COLLECTION_NAME);
let list = await collection.list();
do_check_eq(list.data.length, 1);
await PinningPreloadClient.openCollection(async (collection) => {
const list = await collection.list();
do_check_eq(list.data.length, 1);
});
// check that a pin exists for one.example.com
ok(sss.isSecureURI(sss.HEADER_HPKP,
@ -123,10 +94,10 @@ add_task(async function test_something() {
// Open the collection, verify it's been updated:
// Our data now has four new records; all should be in the local collection
collection = do_get_kinto_collection(connection, COLLECTION_NAME);
list = await collection.list();
do_check_eq(list.data.length, 5);
await connection.close();
await PinningPreloadClient.openCollection(async (collection) => {
const list = await collection.list();
do_check_eq(list.data.length, 5);
});
// check that a pin exists for two.example.com and three.example.com
ok(sss.isSecureURI(sss.HEADER_HPKP,

Просмотреть файл

@ -3,25 +3,19 @@
Cu.import("resource://services-common/blocklist-updater.js");
Cu.import("resource://testing-common/httpd.js");
const { Kinto } = Cu.import("resource://services-common/kinto-offline-client.js", {});
const { FirefoxAdapter } = Cu.import("resource://services-common/kinto-storage-adapter.js", {});
const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
const { OneCRLBlocklistClient } = Cu.import("resource://services-common/blocklist-clients.js", {});
const { UptakeTelemetry } = Cu.import("resource://services-common/uptake-telemetry.js", {});
let server;
const PREF_BLOCKLIST_BUCKET = "services.blocklist.bucket";
const PREF_BLOCKLIST_ENFORCE_SIGNING = "services.blocklist.signing.enforced";
const PREF_BLOCKLIST_ONECRL_COLLECTION = "services.blocklist.onecrl.collection";
const PREF_SETTINGS_SERVER = "services.settings.server";
const PREF_SIGNATURE_ROOT = "security.content.signature.root_hash";
// Telemetry reports.
const TELEMETRY_HISTOGRAM_KEY = OneCRLBlocklistClient.identifier;
const kintoFilename = "kinto.sqlite";
const CERT_DIR = "test_blocklist_signatures/";
const CHAIN_FILES =
["collection_signing_ee.pem",
@ -60,29 +54,11 @@ function getCertChain() {
}
async function checkRecordCount(count) {
// open the collection manually
const base = Services.prefs.getCharPref(PREF_SETTINGS_SERVER);
const bucket = Services.prefs.getCharPref(PREF_BLOCKLIST_BUCKET);
const collectionName =
Services.prefs.getCharPref(PREF_BLOCKLIST_ONECRL_COLLECTION);
const sqliteHandle = await FirefoxAdapter.openConnection({path: kintoFilename});
const config = {
remote: base,
bucket,
adapter: FirefoxAdapter,
adapterOptions: {sqliteHandle},
};
const db = new Kinto(config);
const collection = db.collection(collectionName);
// Check we have the expected number of records
let records = await collection.list();
do_check_eq(count, records.data.length);
// Close the collection so the test can exit cleanly
await sqliteHandle.close();
await OneCRLBlocklistClient.openCollection(async (collection) => {
// Check we have the expected number of records
const records = await collection.list();
do_check_eq(count, records.data.length);
});
}
// Check to ensure maybeSync is called with correct values when a changes

Просмотреть файл

@ -57,7 +57,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
Kinto: "resource://services-common/kinto-offline-client.js",
FirefoxAdapter: "resource://services-common/kinto-storage-adapter.js",
Observers: "resource://services-common/observers.js",
Sqlite: "resource://gre/modules/Sqlite.jsm",
Utils: "resource://services-sync/util.js",
});
@ -334,30 +333,25 @@ global.KeyRingEncryptionRemoteTransformer = KeyRingEncryptionRemoteTransformer;
* - connection: a Sqlite connection. Meant for internal use only.
* - kinto: a KintoBase object, suitable for using in Firefox. All
* collections in this database will use the same Sqlite connection.
* @returns {Promise<Object>}
*/
const storageSyncInit = (async function() {
const path = "storage-sync.sqlite";
const opts = {path, sharedMemoryCache: false};
const connection = await Sqlite.openConnection(opts);
await FirefoxAdapter._init(connection);
return {
connection,
kinto: new Kinto({
adapter: FirefoxAdapter,
adapterOptions: {sqliteHandle: connection},
timeout: KINTO_REQUEST_TIMEOUT,
}),
};
})();
AsyncShutdown.profileBeforeChange.addBlocker(
"ExtensionStorageSync: close Sqlite handle",
async function() {
const ret = await storageSyncInit;
const {connection} = ret;
await connection.close();
async function storageSyncInit() {
// Memoize the result to share the connection.
if (storageSyncInit.result === undefined) {
const path = "storage-sync.sqlite";
const connection = await FirefoxAdapter.openConnection({path});
storageSyncInit.result = {
connection,
kinto: new Kinto({
adapter: FirefoxAdapter,
adapterOptions: {sqliteHandle: connection},
timeout: KINTO_REQUEST_TIMEOUT,
}),
};
}
);
return storageSyncInit.result;
}
// Kinto record IDs have two condtions:
//
// - They must contain only ASCII alphanumerics plus - and _. To fix
@ -432,7 +426,7 @@ class CryptoCollection {
async getCollection() {
throwIfNoFxA(this._fxaService, "tried to access cryptoCollection");
const {kinto} = await storageSyncInit;
const {kinto} = await storageSyncInit();
return kinto.collection(STORAGE_SYNC_CRYPTO_COLLECTION_NAME, {
idSchema: cryptoCollectionIdSchema,
remoteTransformers: [new KeyRingEncryptionRemoteTransformer(this._fxaService)],
@ -695,7 +689,7 @@ function cleanUpForContext(extension, context) {
*/
const openCollection = async function(cryptoCollection, extension, context) {
let collectionId = extension.id;
const {kinto} = await storageSyncInit;
const {kinto} = await storageSyncInit();
const remoteTransformers = [new CollectionKeyEncryptionRemoteTransformer(cryptoCollection, extension.id)];
const coll = kinto.collection(collectionId, {
idSchema: storageSyncIdSchema,
@ -1230,7 +1224,17 @@ class ExtensionStorageSync {
this.listeners.set(extension, listeners);
// Force opening the collection so that we will sync for this extension.
return this.getCollection(extension, context);
// This happens asynchronously, even though the surface API is synchronous.
return this.getCollection(extension, context)
.catch((e) => {
// We can ignore failures that happen during shutdown here. First, we
// can't report in any way. And second, a failure to open the collection
// does not matter, because there won't be any message to listen to.
// See Bug 1395215.
if (!(/Kinto storage adapter connection closing/.test(e.message))) {
throw e;
}
});
}
removeOnChangedListener(extension, listener) {