зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1838974 - Bookmarks engine roundtrips data it doesn't know about r=markh,lina,sync-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D181593
This commit is contained in:
Родитель
2cd140ad77
Коммит
525249fbdf
|
@ -1389,3 +1389,124 @@ add_bookmark_test(async function test_livemarks(engine) {
|
|||
await cleanup(engine, server);
|
||||
}
|
||||
});
|
||||
|
||||
add_bookmark_test(async function test_unknown_fields(engine) {
|
||||
let store = engine._store;
|
||||
let server = await serverForFoo(engine);
|
||||
await SyncTestingInfrastructure(server);
|
||||
let collection = server.user("foo").collection("bookmarks");
|
||||
try {
|
||||
let folder1 = await PlacesUtils.bookmarks.insert({
|
||||
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
||||
title: "Folder 1",
|
||||
});
|
||||
let bmk1 = await PlacesUtils.bookmarks.insert({
|
||||
parentGuid: folder1.guid,
|
||||
url: "http://getfirefox.com/",
|
||||
title: "Get Firefox!",
|
||||
});
|
||||
let bmk2 = await PlacesUtils.bookmarks.insert({
|
||||
parentGuid: folder1.guid,
|
||||
url: "http://getthunderbird.com/",
|
||||
title: "Get Thunderbird!",
|
||||
});
|
||||
let toolbar_record = await store.createRecord("toolbar");
|
||||
collection.insert("toolbar", encryptPayload(toolbar_record.cleartext));
|
||||
|
||||
let folder1_record_without_unknown_fields = await store.createRecord(
|
||||
folder1.guid
|
||||
);
|
||||
collection.insert(
|
||||
folder1.guid,
|
||||
encryptPayload(folder1_record_without_unknown_fields.cleartext)
|
||||
);
|
||||
|
||||
// First bookmark record has an unknown string field
|
||||
let bmk1_record = await store.createRecord(bmk1.guid);
|
||||
console.log("bmk1_record: ", bmk1_record);
|
||||
bmk1_record.cleartext.unknownStrField =
|
||||
"an unknown field from another client";
|
||||
collection.insert(bmk1.guid, encryptPayload(bmk1_record.cleartext));
|
||||
|
||||
// Second bookmark record as an unknown object field
|
||||
let bmk2_record = await store.createRecord(bmk2.guid);
|
||||
bmk2_record.cleartext.unknownObjField = {
|
||||
name: "an unknown object from another client",
|
||||
};
|
||||
collection.insert(bmk2.guid, encryptPayload(bmk2_record.cleartext));
|
||||
|
||||
// Sync the two bookmarks
|
||||
await sync_engine_and_validate_telem(engine, true);
|
||||
|
||||
// Add a folder could also have an unknown field
|
||||
let folder1_record = await store.createRecord(folder1.guid);
|
||||
folder1_record.cleartext.unknownStrField =
|
||||
"a folder could also have an unknown field!";
|
||||
collection.insert(folder1.guid, encryptPayload(folder1_record.cleartext));
|
||||
|
||||
// sync the new updates
|
||||
await engine.setLastSync(1);
|
||||
await sync_engine_and_validate_telem(engine, true);
|
||||
|
||||
let payloads = collection.payloads();
|
||||
// Validate the server has the unknown fields at the top level (and now unknownFields)
|
||||
let server_bmk1 = payloads.find(payload => payload.id == bmk1.guid);
|
||||
deepEqual(
|
||||
server_bmk1.unknownStrField,
|
||||
"an unknown field from another client",
|
||||
"unknown fields correctly on the record"
|
||||
);
|
||||
Assert.equal(server_bmk1.unknownFields, null);
|
||||
|
||||
// Check that the mirror table has unknown fields
|
||||
let db = await PlacesUtils.promiseDBConnection();
|
||||
let rows = await db.executeCached(
|
||||
`
|
||||
SELECT guid, title, unknownFields from items WHERE guid IN
|
||||
(:bmk1, :bmk2, :folder1)`,
|
||||
{ bmk1: bmk1.guid, bmk2: bmk2.guid, folder1: folder1.guid }
|
||||
);
|
||||
// We should have 3 rows that came from the server
|
||||
Assert.equal(rows.length, 3);
|
||||
|
||||
// Bookmark 1 - unknown string field
|
||||
let remote_bmk1 = rows.find(
|
||||
row => row.getResultByName("guid") == bmk1.guid
|
||||
);
|
||||
Assert.equal(remote_bmk1.getResultByName("title"), "Get Firefox!");
|
||||
deepEqual(JSON.parse(remote_bmk1.getResultByName("unknownFields")), {
|
||||
unknownStrField: "an unknown field from another client",
|
||||
});
|
||||
|
||||
// Bookmark 2 - unknown object field
|
||||
let remote_bmk2 = rows.find(
|
||||
row => row.getResultByName("guid") == bmk2.guid
|
||||
);
|
||||
Assert.equal(remote_bmk2.getResultByName("title"), "Get Thunderbird!");
|
||||
deepEqual(JSON.parse(remote_bmk2.getResultByName("unknownFields")), {
|
||||
unknownObjField: {
|
||||
name: "an unknown object from another client",
|
||||
},
|
||||
});
|
||||
|
||||
// Folder with unknown field
|
||||
|
||||
// check the server still has the unknown field
|
||||
deepEqual(
|
||||
payloads.find(payload => payload.id == folder1.guid).unknownStrField,
|
||||
"a folder could also have an unknown field!",
|
||||
"Server still has the unknown field"
|
||||
);
|
||||
|
||||
let remote_folder = rows.find(
|
||||
row => row.getResultByName("guid") == folder1.guid
|
||||
);
|
||||
Assert.equal(remote_folder.getResultByName("title"), "Folder 1");
|
||||
deepEqual(JSON.parse(remote_folder.getResultByName("unknownFields")), {
|
||||
unknownStrField: "a folder could also have an unknown field!",
|
||||
});
|
||||
} finally {
|
||||
await cleanup(engine, server);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -74,7 +74,7 @@ const DB_TITLE_LENGTH_MAX = 4096;
|
|||
|
||||
// The current mirror database schema version. Bump for migrations, then add
|
||||
// migration code to `migrateMirrorSchema`.
|
||||
const MIRROR_SCHEMA_VERSION = 8;
|
||||
const MIRROR_SCHEMA_VERSION = 9;
|
||||
|
||||
// Use a shared jankYielder in these functions
|
||||
XPCOMUtils.defineLazyGetter(lazy, "yieldState", () => lazy.Async.yieldState());
|
||||
|
@ -806,13 +806,21 @@ export class SyncedBookmarksMirror {
|
|||
? Ci.mozISyncedBookmarksMerger.VALIDITY_VALID
|
||||
: Ci.mozISyncedBookmarksMerger.VALIDITY_REPLACE;
|
||||
|
||||
let unknownFields = extractUnknownFields(record.cleartext, [
|
||||
"bmkUri",
|
||||
"description",
|
||||
"keyword",
|
||||
"tags",
|
||||
"title",
|
||||
...COMMON_UNKNOWN_FIELDS,
|
||||
]);
|
||||
await this.db.executeCached(
|
||||
`
|
||||
REPLACE INTO items(guid, parentGuid, serverModified, needsMerge, kind,
|
||||
dateAdded, title, keyword, validity,
|
||||
dateAdded, title, keyword, validity, unknownFields,
|
||||
urlId)
|
||||
VALUES(:guid, :parentGuid, :serverModified, :needsMerge, :kind,
|
||||
:dateAdded, NULLIF(:title, ''), :keyword, :validity,
|
||||
:dateAdded, NULLIF(:title, ''), :keyword, :validity, :unknownFields,
|
||||
(SELECT id FROM urls
|
||||
WHERE hash = hash(:url) AND
|
||||
url = :url))`,
|
||||
|
@ -827,6 +835,7 @@ export class SyncedBookmarksMirror {
|
|||
keyword,
|
||||
url: url ? url.href : null,
|
||||
validity,
|
||||
unknownFields,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -923,18 +932,29 @@ export class SyncedBookmarksMirror {
|
|||
let dateAdded = determineDateAdded(record);
|
||||
let title = validateTitle(record.title);
|
||||
|
||||
let unknownFields = extractUnknownFields(record.cleartext, [
|
||||
"bmkUri",
|
||||
"description",
|
||||
"folderName",
|
||||
"keyword",
|
||||
"queryId",
|
||||
"tags",
|
||||
"title",
|
||||
...COMMON_UNKNOWN_FIELDS,
|
||||
]);
|
||||
|
||||
await this.db.executeCached(
|
||||
`
|
||||
REPLACE INTO items(guid, parentGuid, serverModified, needsMerge, kind,
|
||||
dateAdded, title,
|
||||
urlId,
|
||||
validity)
|
||||
validity, unknownFields)
|
||||
VALUES(:guid, :parentGuid, :serverModified, :needsMerge, :kind,
|
||||
:dateAdded, NULLIF(:title, ''),
|
||||
(SELECT id FROM urls
|
||||
WHERE hash = hash(:url) AND
|
||||
url = :url),
|
||||
:validity)`,
|
||||
:validity, :unknownFields)`,
|
||||
{
|
||||
guid,
|
||||
parentGuid,
|
||||
|
@ -945,6 +965,7 @@ export class SyncedBookmarksMirror {
|
|||
title,
|
||||
url: url ? url.href : null,
|
||||
validity,
|
||||
unknownFields,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -957,13 +978,18 @@ export class SyncedBookmarksMirror {
|
|||
let serverModified = determineServerModified(record);
|
||||
let dateAdded = determineDateAdded(record);
|
||||
let title = validateTitle(record.title);
|
||||
|
||||
let unknownFields = extractUnknownFields(record.cleartext, [
|
||||
"children",
|
||||
"description",
|
||||
"title",
|
||||
...COMMON_UNKNOWN_FIELDS,
|
||||
]);
|
||||
await this.db.executeCached(
|
||||
`
|
||||
REPLACE INTO items(guid, parentGuid, serverModified, needsMerge, kind,
|
||||
dateAdded, title)
|
||||
dateAdded, title, unknownFields)
|
||||
VALUES(:guid, :parentGuid, :serverModified, :needsMerge, :kind,
|
||||
:dateAdded, NULLIF(:title, ''))`,
|
||||
:dateAdded, NULLIF(:title, ''), :unknownFields)`,
|
||||
{
|
||||
guid,
|
||||
parentGuid,
|
||||
|
@ -972,6 +998,7 @@ export class SyncedBookmarksMirror {
|
|||
kind: Ci.mozISyncedBookmarksMerger.KIND_FOLDER,
|
||||
dateAdded,
|
||||
title,
|
||||
unknownFields,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -1022,12 +1049,21 @@ export class SyncedBookmarksMirror {
|
|||
? Ci.mozISyncedBookmarksMerger.VALIDITY_VALID
|
||||
: Ci.mozISyncedBookmarksMerger.VALIDITY_REPLACE;
|
||||
|
||||
let unknownFields = extractUnknownFields(record.cleartext, [
|
||||
"children",
|
||||
"description",
|
||||
"feedUri",
|
||||
"siteUri",
|
||||
"title",
|
||||
...COMMON_UNKNOWN_FIELDS,
|
||||
]);
|
||||
|
||||
await this.db.executeCached(
|
||||
`
|
||||
REPLACE INTO items(guid, parentGuid, serverModified, needsMerge, kind,
|
||||
dateAdded, title, feedURL, siteURL, validity)
|
||||
dateAdded, title, feedURL, siteURL, validity, unknownFields)
|
||||
VALUES(:guid, :parentGuid, :serverModified, :needsMerge, :kind,
|
||||
:dateAdded, NULLIF(:title, ''), :feedURL, :siteURL, :validity)`,
|
||||
:dateAdded, NULLIF(:title, ''), :feedURL, :siteURL, :validity, :unknownFields)`,
|
||||
{
|
||||
guid,
|
||||
parentGuid,
|
||||
|
@ -1039,6 +1075,7 @@ export class SyncedBookmarksMirror {
|
|||
feedURL: feedURL ? feedURL.href : null,
|
||||
siteURL: siteURL ? siteURL.href : null,
|
||||
validity,
|
||||
unknownFields,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -1050,13 +1087,17 @@ export class SyncedBookmarksMirror {
|
|||
);
|
||||
let serverModified = determineServerModified(record);
|
||||
let dateAdded = determineDateAdded(record);
|
||||
let unknownFields = extractUnknownFields(record.cleartext, [
|
||||
"pos",
|
||||
...COMMON_UNKNOWN_FIELDS,
|
||||
]);
|
||||
|
||||
await this.db.executeCached(
|
||||
`
|
||||
REPLACE INTO items(guid, parentGuid, serverModified, needsMerge, kind,
|
||||
dateAdded)
|
||||
dateAdded, unknownFields)
|
||||
VALUES(:guid, :parentGuid, :serverModified, :needsMerge, :kind,
|
||||
:dateAdded)`,
|
||||
:dateAdded, :unknownFields)`,
|
||||
{
|
||||
guid,
|
||||
parentGuid,
|
||||
|
@ -1064,6 +1105,7 @@ export class SyncedBookmarksMirror {
|
|||
needsMerge,
|
||||
kind: Ci.mozISyncedBookmarksMerger.KIND_SEPARATOR,
|
||||
dateAdded,
|
||||
unknownFields,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -1184,7 +1226,7 @@ export class SyncedBookmarksMirror {
|
|||
await this.db.execute(
|
||||
`SELECT id, syncChangeCounter, guid, isDeleted, type, isQuery,
|
||||
tagFolderName, keyword, url, IFNULL(title, '') AS title,
|
||||
position, parentGuid,
|
||||
position, parentGuid, unknownFields,
|
||||
IFNULL(parentTitle, '') AS parentTitle, dateAdded
|
||||
FROM itemsToUpload`,
|
||||
null,
|
||||
|
@ -1227,6 +1269,10 @@ export class SyncedBookmarksMirror {
|
|||
let parentRecordId =
|
||||
lazy.PlacesSyncUtils.bookmarks.guidToRecordId(parentGuid);
|
||||
|
||||
let unknownFieldsRow = row.getResultByName("unknownFields");
|
||||
let unknownFields = unknownFieldsRow
|
||||
? JSON.parse(unknownFieldsRow)
|
||||
: null;
|
||||
let type = row.getResultByName("type");
|
||||
switch (type) {
|
||||
case lazy.PlacesUtils.bookmarks.TYPE_BOOKMARK: {
|
||||
|
@ -1253,6 +1299,7 @@ export class SyncedBookmarksMirror {
|
|||
title: row.getResultByName("title"),
|
||||
// folderName should never be an empty string or null
|
||||
folderName: row.getResultByName("tagFolderName") || undefined,
|
||||
...unknownFields,
|
||||
};
|
||||
changeRecords[recordId] = new BookmarkChangeRecord(
|
||||
syncChangeCounter,
|
||||
|
@ -1270,6 +1317,7 @@ export class SyncedBookmarksMirror {
|
|||
dateAdded: row.getResultByName("dateAdded") || undefined,
|
||||
bmkUri: row.getResultByName("url"),
|
||||
title: row.getResultByName("title"),
|
||||
...unknownFields,
|
||||
};
|
||||
let keyword = row.getResultByName("keyword");
|
||||
if (keyword) {
|
||||
|
@ -1296,6 +1344,7 @@ export class SyncedBookmarksMirror {
|
|||
parentName: row.getResultByName("parentTitle"),
|
||||
dateAdded: row.getResultByName("dateAdded") || undefined,
|
||||
title: row.getResultByName("title"),
|
||||
...unknownFields,
|
||||
};
|
||||
let localId = row.getResultByName("id");
|
||||
let childRecordIds = childRecordIdsByLocalParentId.get(localId);
|
||||
|
@ -1317,6 +1366,7 @@ export class SyncedBookmarksMirror {
|
|||
dateAdded: row.getResultByName("dateAdded") || undefined,
|
||||
// Older Desktops use `pos` for deduping.
|
||||
pos: row.getResultByName("position"),
|
||||
...unknownFields,
|
||||
};
|
||||
changeRecords[recordId] = new BookmarkChangeRecord(
|
||||
syncChangeCounter,
|
||||
|
@ -1503,6 +1553,19 @@ async function migrateMirrorSchema(db, currentSchemaVersion) {
|
|||
WHERE EXISTS (SELECT 1 FROM mirror.items
|
||||
WHERE guid = b.guid)`);
|
||||
}
|
||||
if (currentSchemaVersion < 9) {
|
||||
// Adding unknownFields to the mirror table, which allows us to
|
||||
// keep fields we may not yet understand from other clients and roundtrip
|
||||
// them during the sync process
|
||||
let columns = await db.execute(`PRAGMA table_info(items)`);
|
||||
// migration needs to be idempotent, so we check if the column exists first
|
||||
let exists = columns.find(
|
||||
row => row.getResultByName("name") === "unknownFields"
|
||||
);
|
||||
if (!exists) {
|
||||
await db.execute(`ALTER TABLE items ADD COLUMN unknownFields TEXT`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1543,7 +1606,8 @@ async function initializeMirrorDatabase(db) {
|
|||
loadInSidebar BOOLEAN,
|
||||
smartBookmarkName TEXT,
|
||||
feedURL TEXT,
|
||||
siteURL TEXT
|
||||
siteURL TEXT,
|
||||
unknownFields TEXT
|
||||
)`);
|
||||
|
||||
await db.execute(`CREATE TABLE mirror.structure(
|
||||
|
@ -1932,7 +1996,8 @@ async function initializeTempMirrorEntities(db) {
|
|||
url TEXT,
|
||||
tagFolderName TEXT,
|
||||
keyword TEXT,
|
||||
position INTEGER
|
||||
position INTEGER,
|
||||
unknownFields TEXT
|
||||
)`);
|
||||
|
||||
await db.execute(`CREATE TEMP TABLE structureToUpload(
|
||||
|
@ -2527,4 +2592,40 @@ function anyAborted(finalizeSignal, interruptSignal = null) {
|
|||
return controller.signal;
|
||||
}
|
||||
|
||||
// Common unknown fields for places items
|
||||
const COMMON_UNKNOWN_FIELDS = [
|
||||
"dateAdded",
|
||||
"hasDupe",
|
||||
"id",
|
||||
"modified",
|
||||
"parentid",
|
||||
"parentName",
|
||||
"type",
|
||||
];
|
||||
|
||||
// Other clients might have new fields we don't quite understand yet,
|
||||
// so we add it to a "unknownFields" field to roundtrip back to the server
|
||||
// so other clients don't experience data loss
|
||||
function extractUnknownFields(record, validFields) {
|
||||
let { unknownFields, hasUnknownFields } = Object.keys(record).reduce(
|
||||
({ unknownFields, hasUnknownFields }, key) => {
|
||||
if (validFields.includes(key)) {
|
||||
return { unknownFields, hasUnknownFields };
|
||||
}
|
||||
unknownFields[key] = record[key];
|
||||
return { unknownFields, hasUnknownFields: true };
|
||||
},
|
||||
{ unknownFields: {}, hasUnknownFields: false }
|
||||
);
|
||||
// If we found some unknown fields, we stringify it to be able
|
||||
// to properly encrypt it for roundtripping since we can't know if
|
||||
// it contained sensitive fields or not
|
||||
if (hasUnknownFields) {
|
||||
// For simplicity, we store the unknown fields as a string
|
||||
// since we never operate on it and just need it for roundtripping
|
||||
return JSON.stringify(unknownFields);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// In conclusion, this is why bookmark syncing is hard.
|
||||
|
|
|
@ -1104,7 +1104,8 @@ fn stage_items_to_upload(
|
|||
"INSERT OR IGNORE INTO itemsToUpload(id, guid, syncChangeCounter,
|
||||
parentGuid, parentTitle, dateAdded,
|
||||
type, title, placeId, isQuery, url,
|
||||
keyword, position, tagFolderName)
|
||||
keyword, position, tagFolderName,
|
||||
unknownFields)
|
||||
{}
|
||||
JOIN itemsToApply n ON n.mergedGuid = b.guid
|
||||
WHERE n.localDateAddedMicroseconds < n.remoteDateAddedMicroseconds",
|
||||
|
@ -1118,7 +1119,8 @@ fn stage_items_to_upload(
|
|||
parentGuid, parentTitle,
|
||||
dateAdded, type, title,
|
||||
placeId, isQuery, url, keyword,
|
||||
position, tagFolderName)
|
||||
position, tagFolderName,
|
||||
unknownFields)
|
||||
{}
|
||||
WHERE b.guid IN ({})",
|
||||
UploadItemsFragment("b"),
|
||||
|
@ -1304,10 +1306,12 @@ impl fmt::Display for UploadItemsFragment {
|
|||
(SELECT keyword FROM moz_keywords WHERE place_id = h.id),
|
||||
{0}.position,
|
||||
(SELECT get_query_param(substr(url, 7), 'tag')
|
||||
WHERE substr(h.url, 1, 6) = 'place:') AS tagFolderName
|
||||
WHERE substr(h.url, 1, 6) = 'place:') AS tagFolderName,
|
||||
v.unknownFields
|
||||
FROM moz_bookmarks {0}
|
||||
JOIN moz_bookmarks p ON p.id = {0}.parent
|
||||
LEFT JOIN moz_places h ON h.id = {0}.fk",
|
||||
LEFT JOIN moz_places h ON h.id = {0}.fk
|
||||
LEFT JOIN items v ON v.guid = {0}.guid",
|
||||
self.0
|
||||
)
|
||||
}
|
||||
|
|
Двоичный файл не отображается.
|
@ -2,7 +2,7 @@
|
|||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Keep in sync with `SyncedBookmarksMirror.jsm`.
|
||||
const CURRENT_MIRROR_SCHEMA_VERSION = 8;
|
||||
const CURRENT_MIRROR_SCHEMA_VERSION = 9;
|
||||
|
||||
// The oldest schema version that we support. Any databases with schemas older
|
||||
// than this will be dropped and recreated.
|
||||
|
@ -159,8 +159,8 @@ add_task(async function test_database_corrupt() {
|
|||
await buf.finalize();
|
||||
});
|
||||
|
||||
add_task(async function test_migrate_v8() {
|
||||
let buf = await openMirror("test_migrate_v8");
|
||||
add_task(async function test_migrate_v7_v9() {
|
||||
let buf = await openMirror("test_migrate_v7_v9");
|
||||
|
||||
await PlacesUtils.bookmarks.insertTree({
|
||||
guid: PlacesUtils.bookmarks.menuGuid,
|
||||
|
@ -205,8 +205,8 @@ add_task(async function test_migrate_v8() {
|
|||
await buf.finalize();
|
||||
|
||||
// reopen it.
|
||||
buf = await openMirror("test_migrate_v8");
|
||||
Assert.equal(await buf.db.getSchemaVersion("mirror"), 8, "did upgrade");
|
||||
buf = await openMirror("test_migrate_v7_v9");
|
||||
Assert.equal(await buf.db.getSchemaVersion("mirror"), 9, "did upgrade");
|
||||
|
||||
let fields = await PlacesTestUtils.fetchBookmarkSyncFields(
|
||||
"bookmarkAAAA",
|
||||
|
@ -226,3 +226,21 @@ add_task(async function test_migrate_v8() {
|
|||
Assert.equal(fieldsMenu.syncStatus, PlacesUtils.bookmarks.SYNC_STATUS.NORMAL);
|
||||
await buf.finalize();
|
||||
});
|
||||
|
||||
add_task(async function test_migrate_v8_v9() {
|
||||
let dbFile = await setupFixtureFile("mirror_v8.sqlite");
|
||||
let buf = await SyncedBookmarksMirror.open({
|
||||
path: dbFile.path,
|
||||
recordStepTelemetry() {},
|
||||
recordValidationTelemetry() {},
|
||||
});
|
||||
|
||||
Assert.equal(await buf.db.getSchemaVersion("mirror"), 9, "did upgrade");
|
||||
|
||||
// Verify the new column is there
|
||||
Assert.ok(await buf.db.execute("SELECT unknownFields FROM items"));
|
||||
|
||||
await buf.finalize();
|
||||
await PlacesUtils.bookmarks.eraseEverything();
|
||||
await PlacesSyncUtils.bookmarks.reset();
|
||||
});
|
||||
|
|
|
@ -0,0 +1,206 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
add_task(async function test_bookmark_unknown_fields() {
|
||||
let buf = await openMirror("unknown_fields");
|
||||
|
||||
await PlacesUtils.bookmarks.insertTree({
|
||||
guid: PlacesUtils.bookmarks.menuGuid,
|
||||
children: [
|
||||
{
|
||||
guid: "mozBmk______",
|
||||
url: "https://mozilla.org",
|
||||
title: "Mozilla",
|
||||
tags: ["moz", "dot", "org"],
|
||||
},
|
||||
],
|
||||
});
|
||||
await storeRecords(
|
||||
buf,
|
||||
shuffle([
|
||||
{
|
||||
id: "menu",
|
||||
parentid: "places",
|
||||
type: "folder",
|
||||
children: ["mozBmk______"],
|
||||
},
|
||||
{
|
||||
id: "mozBmk______",
|
||||
parentid: "menu",
|
||||
type: "bookmark",
|
||||
title: "Mozilla",
|
||||
bmkUri: "https://mozilla.org",
|
||||
tags: ["moz", "dot", "org"],
|
||||
unknownStr: "an unknown field",
|
||||
},
|
||||
]),
|
||||
{ needsMerge: false }
|
||||
);
|
||||
await PlacesTestUtils.markBookmarksAsSynced();
|
||||
|
||||
await storeRecords(
|
||||
buf,
|
||||
[
|
||||
{
|
||||
id: "mozBmk______",
|
||||
parentid: "menu",
|
||||
type: "bookmark",
|
||||
title: "New Mozilla",
|
||||
bmkUri: "https://mozilla.org",
|
||||
tags: ["moz", "dot", "org"],
|
||||
unknownStr: "a new unknown field",
|
||||
},
|
||||
],
|
||||
{ needsMerge: true }
|
||||
);
|
||||
|
||||
let controller = new AbortController();
|
||||
const wasMerged = await buf.merge(controller.signal);
|
||||
Assert.ok(wasMerged);
|
||||
|
||||
let itemRows = await buf.db.execute(`SELECT guid, unknownFields FROM items`);
|
||||
|
||||
let updatedBookmark = itemRows.find(
|
||||
row => row.getResultByName("guid") == "mozBmk______"
|
||||
);
|
||||
deepEqual(JSON.parse(updatedBookmark.getResultByName("unknownFields")), {
|
||||
unknownStr: "a new unknown field",
|
||||
});
|
||||
|
||||
await buf.finalize();
|
||||
await PlacesUtils.bookmarks.eraseEverything();
|
||||
await PlacesSyncUtils.bookmarks.reset();
|
||||
});
|
||||
|
||||
add_task(async function test_changes_unknown_fields_all_types() {
|
||||
let buf = await openMirror("unknown_fields_all");
|
||||
|
||||
await storeRecords(
|
||||
buf,
|
||||
[
|
||||
{
|
||||
id: "menu",
|
||||
parentid: "places",
|
||||
type: "folder",
|
||||
title: "menu",
|
||||
children: ["bookmarkAAAA", "separatorAAA", "queryAAAAAAA"],
|
||||
unknownFolderField: "an unknown folder field",
|
||||
},
|
||||
{
|
||||
id: "bookmarkAAAA",
|
||||
parentid: "menu",
|
||||
type: "bookmark",
|
||||
title: "Mozilla2",
|
||||
bmkUri: "https://mozilla.org",
|
||||
tags: ["moz", "dot", "org"],
|
||||
unknownStrField: "an unknown bookmark field",
|
||||
unknownStrObj: { newField: "unknown pt deux" },
|
||||
},
|
||||
{
|
||||
id: "separatorAAA",
|
||||
parentid: "menu",
|
||||
type: "separator",
|
||||
unknownSepField: "an unknown separator field",
|
||||
},
|
||||
{
|
||||
id: "queryAAAAAAA",
|
||||
parentid: "menu",
|
||||
type: "bookmark",
|
||||
title: "a query",
|
||||
bmkUri: "place:foo",
|
||||
unknownQueryField: "an unknown query field",
|
||||
},
|
||||
],
|
||||
{ needsMerge: true }
|
||||
);
|
||||
|
||||
await PlacesTestUtils.markBookmarksAsSynced();
|
||||
|
||||
let changesToUpload = await buf.apply();
|
||||
// Should be no local changes needing to be uploaded
|
||||
deepEqual(changesToUpload, {});
|
||||
|
||||
// Make updates to all the type of bookmarks
|
||||
await PlacesUtils.bookmarks.update({
|
||||
guid: "menu________",
|
||||
title: "updated menu",
|
||||
});
|
||||
await PlacesUtils.bookmarks.update({
|
||||
guid: "bookmarkAAAA",
|
||||
title: "Mozilla3",
|
||||
});
|
||||
await PlacesUtils.bookmarks.update({ guid: "separatorAAA", index: 2 });
|
||||
await PlacesUtils.bookmarks.update({
|
||||
guid: "queryAAAAAAA",
|
||||
title: "an updated query",
|
||||
});
|
||||
|
||||
// We should now have a bunch of changes to upload
|
||||
changesToUpload = await buf.apply();
|
||||
const { menu, bookmarkAAAA, separatorAAA, queryAAAAAAA } = changesToUpload;
|
||||
|
||||
// Validate we have the updated title as well as the unknown fields
|
||||
Assert.equal(menu.cleartext.title, "updated menu");
|
||||
Assert.equal(menu.cleartext.unknownFolderField, "an unknown folder field");
|
||||
|
||||
// Test bookmark unknown fields
|
||||
Assert.equal(bookmarkAAAA.cleartext.title, "Mozilla3");
|
||||
Assert.equal(
|
||||
bookmarkAAAA.cleartext.unknownStrField,
|
||||
"an unknown bookmark field"
|
||||
);
|
||||
deepEqual(bookmarkAAAA.cleartext.unknownStrObj, {
|
||||
newField: "unknown pt deux",
|
||||
});
|
||||
|
||||
// Test separator unknown fields
|
||||
Assert.equal(
|
||||
separatorAAA.cleartext.unknownSepField,
|
||||
"an unknown separator field"
|
||||
);
|
||||
|
||||
// Test query unknown fields
|
||||
Assert.equal(queryAAAAAAA.cleartext.title, "an updated query");
|
||||
Assert.equal(
|
||||
queryAAAAAAA.cleartext.unknownQueryField,
|
||||
"an unknown query field"
|
||||
);
|
||||
|
||||
let itemRows = await buf.db.execute(`SELECT guid, unknownFields FROM items`);
|
||||
|
||||
// Test bookmark correctly JSON'd in the mirror
|
||||
let remoteBookmark = itemRows.find(
|
||||
row => row.getResultByName("guid") == "bookmarkAAAA"
|
||||
);
|
||||
deepEqual(JSON.parse(remoteBookmark.getResultByName("unknownFields")), {
|
||||
unknownStrField: "an unknown bookmark field",
|
||||
unknownStrObj: { newField: "unknown pt deux" },
|
||||
});
|
||||
|
||||
// Test folder correctly JSON'd in the mirror
|
||||
let remoteFolder = itemRows.find(
|
||||
row => row.getResultByName("guid") == "menu________"
|
||||
);
|
||||
deepEqual(JSON.parse(remoteFolder.getResultByName("unknownFields")), {
|
||||
unknownFolderField: "an unknown folder field",
|
||||
});
|
||||
// Test query correctly JSON'd in the mirror
|
||||
let remoteQuery = itemRows.find(
|
||||
row => row.getResultByName("guid") == "queryAAAAAAA"
|
||||
);
|
||||
deepEqual(JSON.parse(remoteQuery.getResultByName("unknownFields")), {
|
||||
unknownQueryField: "an unknown query field",
|
||||
});
|
||||
// Test separator correctly JSON'd in the mirror
|
||||
let remoteSeparator = itemRows.find(
|
||||
row => row.getResultByName("guid") == "separatorAAA"
|
||||
);
|
||||
deepEqual(JSON.parse(remoteSeparator.getResultByName("unknownFields")), {
|
||||
unknownSepField: "an unknown separator field",
|
||||
});
|
||||
|
||||
await buf.finalize();
|
||||
await PlacesUtils.bookmarks.eraseEverything();
|
||||
await PlacesSyncUtils.bookmarks.reset();
|
||||
});
|
|
@ -6,6 +6,7 @@ support-files =
|
|||
mirror_corrupt.sqlite
|
||||
mirror_v1.sqlite
|
||||
mirror_v5.sqlite
|
||||
mirror_v8.sqlite
|
||||
|
||||
[test_bookmark_abort_merging.js]
|
||||
[test_bookmark_chunking.js]
|
||||
|
@ -20,4 +21,5 @@ support-files =
|
|||
[test_bookmark_reconcile.js]
|
||||
[test_bookmark_structure_changes.js]
|
||||
[test_bookmark_value_changes.js]
|
||||
[test_bookmark_unknown_fields.js]
|
||||
[test_sync_utils.js]
|
||||
|
|
Загрузка…
Ссылка в новой задаче