1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
|
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const BASE_GUID = "null".padEnd(11, "_");
const LAST_USED_ANNO = "bookmarkPropertiesDialog/folderLastUsed";
const LAST_USED_META_DATA = "places/bookmarks/edit/lastusedfolder";
let expectedGuids = [];
async function adjustIndices(db, itemGuid) {
await db.execute(
`
UPDATE moz_bookmarks SET
position = position - 1
WHERE parent = (SELECT parent FROM moz_bookmarks
WHERE guid = :itemGuid) AND
position >= (SELECT position FROM moz_bookmarks
WHERE guid = :itemGuid)`,
{ itemGuid }
);
}
async function fetchChildInfos(db, parentGuid) {
let rows = await db.execute(
`
SELECT b.guid, b.position, b.syncChangeCounter
FROM moz_bookmarks b
JOIN moz_bookmarks p ON p.id = b.parent
WHERE p.guid = :parentGuid
ORDER BY b.position`,
{ parentGuid }
);
return rows.map(row => ({
guid: row.getResultByName("guid"),
position: row.getResultByName("position"),
syncChangeCounter: row.getResultByName("syncChangeCounter"),
}));
}
add_task(async function setup() {
await setupPlacesDatabase("places_v43.sqlite");
// Setup database contents to be migrated.
let path = PathUtils.join(PathUtils.profileDir, DB_FILENAME);
let db = await Sqlite.openConnection({ path });
// We can reuse the same guid, it doesn't matter for this test.
await db.execute(
`INSERT INTO moz_anno_attributes (name)
VALUES (:last_used_anno)`,
{ last_used_anno: LAST_USED_ANNO }
);
for (let i = 0; i < 3; i++) {
let guid = `${BASE_GUID}${i}`;
await db.execute(
`INSERT INTO moz_bookmarks (guid, type)
VALUES (:guid, :type)
`,
{ guid, type: PlacesUtils.bookmarks.TYPE_FOLDER }
);
await db.execute(
`INSERT INTO moz_items_annos (item_id, anno_attribute_id, content)
VALUES ((SELECT id FROM moz_bookmarks WHERE guid = :guid),
(SELECT id FROM moz_anno_attributes WHERE name = :last_used_anno),
:content)`,
{
guid,
content: new Date(1517318477569) - (3 - i) * 60 * 60 * 1000,
last_used_anno: LAST_USED_ANNO,
}
);
expectedGuids.unshift(guid);
}
info("Move menu into unfiled");
await adjustIndices(db, "menu________");
await db.execute(
`
UPDATE moz_bookmarks SET
parent = (SELECT id FROM moz_bookmarks WHERE guid = :newParentGuid),
position = IFNULL((SELECT MAX(position) + 1 FROM moz_bookmarks
WHERE guid = :newParentGuid), 0)
WHERE guid = :itemGuid`,
{ newParentGuid: "unfiled_____", itemGuid: "menu________" }
);
info("Move toolbar into mobile");
let mobileChildren = [
"bookmarkAAAA",
"bookmarkBBBB",
"toolbar_____",
"bookmarkCCCC",
"bookmarkDDDD",
];
await adjustIndices(db, "toolbar_____");
for (let position = 0; position < mobileChildren.length; position++) {
await db.execute(
`
INSERT INTO moz_bookmarks(guid, parent, position)
VALUES(:guid, (SELECT id FROM moz_bookmarks WHERE guid = 'mobile______'),
:position)
ON CONFLICT(guid) DO UPDATE SET
parent = excluded.parent,
position = excluded.position`,
{ guid: mobileChildren[position], position }
);
}
info("Reset Sync change counters");
await db.execute(`UPDATE moz_bookmarks SET syncChangeCounter = 0`);
await db.close();
});
add_task(async function database_is_valid() {
// Accessing the database for the first time triggers migration.
Assert.equal(
PlacesUtils.history.databaseStatus,
PlacesUtils.history.DATABASE_STATUS_UPGRADED
);
let db = await PlacesUtils.promiseDBConnection();
Assert.equal(await db.getSchemaVersion(), CURRENT_SCHEMA_VERSION);
});
add_task(async function test_folders_migrated() {
let metaData = await PlacesUtils.metadata.get(LAST_USED_META_DATA);
Assert.deepEqual(JSON.parse(metaData), expectedGuids);
});
add_task(async function test_annotations_removed() {
let db = await PlacesUtils.promiseDBConnection();
await assertAnnotationsRemoved(db, [LAST_USED_ANNO]);
});
add_task(async function test_no_orphan_annotations() {
let db = await PlacesUtils.promiseDBConnection();
await assertNoOrphanAnnotations(db);
});
add_task(async function test_roots_fixed() {
let db = await PlacesUtils.promiseDBConnection();
let expectedRootInfos = [
{
guid: PlacesUtils.bookmarks.tagsGuid,
position: 0,
syncChangeCounter: 0,
},
{
guid: PlacesUtils.bookmarks.unfiledGuid,
position: 1,
syncChangeCounter: 1,
},
{
guid: PlacesUtils.bookmarks.mobileGuid,
position: 2,
syncChangeCounter: 1,
},
{
guid: PlacesUtils.bookmarks.menuGuid,
position: 3,
syncChangeCounter: 1,
},
{
guid: PlacesUtils.bookmarks.toolbarGuid,
position: 4,
syncChangeCounter: 1,
},
];
Assert.deepEqual(
expectedRootInfos,
await fetchChildInfos(db, PlacesUtils.bookmarks.rootGuid),
"All roots should be reparented to the Places root"
);
let expectedMobileInfos = [
{
guid: "bookmarkAAAA",
position: 0,
syncChangeCounter: 0,
},
{
guid: "bookmarkBBBB",
position: 1,
syncChangeCounter: 0,
},
{
guid: "bookmarkCCCC",
position: 2,
syncChangeCounter: 0,
},
{
guid: "bookmarkDDDD",
position: 3,
syncChangeCounter: 0,
},
];
Assert.deepEqual(
expectedMobileInfos,
await fetchChildInfos(db, PlacesUtils.bookmarks.mobileGuid),
"Should fix misparented root sibling positions"
);
});
|