|
1 /* global |
|
2 sendAsyncMessage, |
|
3 addMessageListener, |
|
4 indexedDB |
|
5 */ |
|
6 "use strict"; |
|
7 |
|
8 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; |
|
9 |
|
10 let imports = {}; |
|
11 |
|
12 Cu.import("resource://gre/modules/ContactDB.jsm", imports); |
|
13 Cu.import("resource://gre/modules/ContactService.jsm", imports); |
|
14 Cu.import("resource://gre/modules/Promise.jsm", imports); |
|
15 Cu.importGlobalProperties(["indexedDB"]); |
|
16 |
|
17 const { |
|
18 STORE_NAME, |
|
19 SAVED_GETALL_STORE_NAME, |
|
20 REVISION_STORE, |
|
21 DB_NAME, |
|
22 ContactService, |
|
23 } = imports; |
|
24 // |const| will not work because |
|
25 // it will make the Promise object immutable before assigning. |
|
26 // Using Object.defineProperty() instead. |
|
27 Object.defineProperty(this, "Promise", { |
|
28 value: imports.Promise, writable: false, configurable: false |
|
29 }); |
|
30 |
|
31 let DEBUG = false; |
|
32 function debug(str) { |
|
33 if (DEBUG){ |
|
34 dump("-*- TestMigrationChromeScript: " + str + "\n"); |
|
35 } |
|
36 } |
|
37 |
|
38 const DATA = { |
|
39 12: { |
|
40 SCHEMA: function createSchema12(db, transaction) { |
|
41 let objectStore = db.createObjectStore(STORE_NAME, {keyPath: "id"}); |
|
42 objectStore.createIndex("familyName", "properties.familyName", { multiEntry: true }); |
|
43 objectStore.createIndex("givenName", "properties.givenName", { multiEntry: true }); |
|
44 objectStore.createIndex("familyNameLowerCase", "search.familyName", { multiEntry: true }); |
|
45 objectStore.createIndex("givenNameLowerCase", "search.givenName", { multiEntry: true }); |
|
46 objectStore.createIndex("telLowerCase", "search.tel", { multiEntry: true }); |
|
47 objectStore.createIndex("emailLowerCase", "search.email", { multiEntry: true }); |
|
48 objectStore.createIndex("tel", "search.exactTel", { multiEntry: true }); |
|
49 objectStore.createIndex("category", "properties.category", { multiEntry: true }); |
|
50 objectStore.createIndex("email", "search.email", { multiEntry: true }); |
|
51 objectStore.createIndex("telMatch", "search.parsedTel", {multiEntry: true}); |
|
52 db.createObjectStore(SAVED_GETALL_STORE_NAME); |
|
53 db.createObjectStore(REVISION_STORE).put(0, "revision"); |
|
54 }, |
|
55 BAD: [ |
|
56 { |
|
57 // this contact is bad because its "name" property is not an array and |
|
58 // is the empty string (upgrade 16 to 17) |
|
59 "properties": { |
|
60 "name": "", |
|
61 "email": [], |
|
62 "url": [{ |
|
63 "type": ["source"], |
|
64 "value": "urn:service:gmail:uid:http://www.google.com/m8/feeds/contacts/XXX/base/4567894654" |
|
65 }], |
|
66 "category": ["gmail"], |
|
67 "adr": [], |
|
68 "tel": [{ |
|
69 "type": ["mobile"], |
|
70 "value": "+7 123 456-78-90" |
|
71 }], |
|
72 "sex": "undefined", |
|
73 "genderIdentity": "undefined" |
|
74 }, |
|
75 "search": { |
|
76 "givenName": [], |
|
77 "familyName": [], |
|
78 "email": [], |
|
79 "category": ["gmail"], |
|
80 "tel": ["+71234567890","71234567890","1234567890","234567890","34567890", |
|
81 "4567890","567890","67890","7890","890","90","0","81234567890"], |
|
82 "exactTel": ["+71234567890"], |
|
83 "parsedTel": ["+71234567890","1234567890","81234567890","34567890"] |
|
84 }, |
|
85 "updated": new Date("2013-07-27T16:47:40.974Z"), |
|
86 "published": new Date("2013-07-27T16:47:40.974Z"), |
|
87 "id": "bad-1" |
|
88 }, |
|
89 { |
|
90 // This contact is bad because its "name" property is not an array |
|
91 // (upgrade 14 to 15) |
|
92 "properties": { |
|
93 "name": "name-bad-2", |
|
94 "email": [], |
|
95 "url": [{ |
|
96 "type": ["source"], |
|
97 "value": "urn:service:gmail:uid:http://www.google.com/m8/feeds/contacts/XXX/base/4567894654" |
|
98 }], |
|
99 "category": ["gmail"], |
|
100 "adr": [], |
|
101 "tel": [{ |
|
102 "type": ["mobile"], |
|
103 "value": "+7 123 456-78-90" |
|
104 }], |
|
105 "sex": "undefined", |
|
106 "genderIdentity": "undefined" |
|
107 }, |
|
108 "search": { |
|
109 "givenName": [], |
|
110 "familyName": [], |
|
111 "email": [], |
|
112 "category": ["gmail"], |
|
113 "tel": ["+71234567890","71234567890","1234567890","234567890","34567890", |
|
114 "4567890","567890","67890","7890","890","90","0","81234567890"], |
|
115 "exactTel": ["+71234567890"], |
|
116 "parsedTel": ["+71234567890","1234567890","81234567890","34567890"] |
|
117 }, |
|
118 "updated": new Date("2013-07-27T16:47:40.974Z"), |
|
119 "published": new Date("2013-07-27T16:47:40.974Z"), |
|
120 "id": "bad-2" |
|
121 }, |
|
122 { |
|
123 // This contact is bad because its bday property is a String (upgrade 15 |
|
124 // to 16), and its anniversary property is an empty string (upgrade 16 |
|
125 // to 17) |
|
126 "properties": { |
|
127 "name": ["name-bad-3"], |
|
128 "email": [], |
|
129 "url": [{ |
|
130 "type": ["source"], |
|
131 "value": "urn:service:gmail:uid:http://www.google.com/m8/feeds/contacts/XXX/base/4567894654" |
|
132 }], |
|
133 "category": ["gmail"], |
|
134 "adr": [], |
|
135 "tel": [{ |
|
136 "type": ["mobile"], |
|
137 "value": "+7 123 456-78-90" |
|
138 }], |
|
139 "sex": "undefined", |
|
140 "genderIdentity": "undefined", |
|
141 "bday": "2013-07-27T16:47:40.974Z", |
|
142 "anniversary": "" |
|
143 }, |
|
144 "search": { |
|
145 "givenName": [], |
|
146 "familyName": [], |
|
147 "email": [], |
|
148 "category": ["gmail"], |
|
149 "tel": ["+71234567890","71234567890","1234567890","234567890","34567890", |
|
150 "4567890","567890","67890","7890","890","90","0","81234567890"], |
|
151 "exactTel": ["+71234567890"], |
|
152 "parsedTel": ["+71234567890","1234567890","81234567890","34567890"] |
|
153 }, |
|
154 "updated": new Date("2013-07-27T16:47:40.974Z"), |
|
155 "published": new Date("2013-07-27T16:47:40.974Z"), |
|
156 "id": "bad-3" |
|
157 }, |
|
158 { |
|
159 // This contact is bad because its tel property has a tel.type null |
|
160 // value (upgrade 16 to 17), and email.type not array value (upgrade 14 |
|
161 // to 15) |
|
162 "properties": { |
|
163 "name": ["name-bad-4"], |
|
164 "email": [{ |
|
165 "value": "toto@toto.com", |
|
166 "type": "home" |
|
167 }], |
|
168 "url": [{ |
|
169 "type": ["source"], |
|
170 "value": "urn:service:gmail:uid:http://www.google.com/m8/feeds/contacts/XXX/base/4567894654" |
|
171 }], |
|
172 "category": ["gmail"], |
|
173 "adr": [], |
|
174 "tel": [{ |
|
175 "type": null, |
|
176 "value": "+7 123 456-78-90" |
|
177 }], |
|
178 "sex": "undefined", |
|
179 "genderIdentity": "undefined" |
|
180 }, |
|
181 "search": { |
|
182 "givenName": [], |
|
183 "familyName": [], |
|
184 "email": [], |
|
185 "category": ["gmail"], |
|
186 "tel": ["+71234567890","71234567890","1234567890","234567890","34567890", |
|
187 "4567890","567890","67890","7890","890","90","0","81234567890"], |
|
188 "exactTel": ["+71234567890"], |
|
189 "parsedTel": ["+71234567890","1234567890","81234567890","34567890"] |
|
190 }, |
|
191 "updated": new Date("2013-07-27T16:47:40.974Z"), |
|
192 "published": new Date("2013-07-27T16:47:40.974Z"), |
|
193 "id": "bad-4" |
|
194 } |
|
195 ], |
|
196 GOOD: [ |
|
197 { |
|
198 "properties": { |
|
199 "name": ["name-good-1"], |
|
200 "email": [], |
|
201 "url": [{ |
|
202 "type": ["source"], |
|
203 "value": "urn:service:gmail:uid:http://www.google.com/m8/feeds/contacts/XXX/base/4567894654" |
|
204 }], |
|
205 "category": ["gmail"], |
|
206 "adr": [], |
|
207 "tel": [{ |
|
208 "type": ["mobile"], |
|
209 "value": "+7 123 456-78-90" |
|
210 }], |
|
211 "sex": "undefined", |
|
212 "genderIdentity": "undefined" |
|
213 }, |
|
214 "search": { |
|
215 "givenName": [], |
|
216 "familyName": [], |
|
217 "email": [], |
|
218 "category": ["gmail"], |
|
219 "tel": ["+71234567890","71234567890","1234567890","234567890","34567890", |
|
220 "4567890","567890","67890","7890","890","90","0","81234567890"], |
|
221 "exactTel": ["+71234567890"], |
|
222 "parsedTel": ["+71234567890","1234567890","81234567890","34567890"] |
|
223 }, |
|
224 "updated": new Date("2013-07-27T16:47:40.974Z"), |
|
225 "published": new Date("2013-07-27T16:47:40.974Z"), |
|
226 "id": "good-1" |
|
227 } |
|
228 ] |
|
229 } |
|
230 }; |
|
231 |
|
232 function DataManager(version) { |
|
233 if (!(version in DATA)) { |
|
234 throw new Error("Version " + version + " can't be found in our test datas."); |
|
235 } |
|
236 |
|
237 this.version = version; |
|
238 this.data = DATA[version]; |
|
239 } |
|
240 |
|
241 DataManager.prototype = { |
|
242 open: function() { |
|
243 debug("opening for version " + this.version); |
|
244 var deferred = Promise.defer(); |
|
245 |
|
246 let req = indexedDB.open(DB_NAME, this.version); |
|
247 req.onupgradeneeded = function() { |
|
248 let db = req.result; |
|
249 let transaction = req.transaction; |
|
250 this.createSchema(db, transaction); |
|
251 this.addContacts(db, transaction); |
|
252 }.bind(this); |
|
253 |
|
254 req.onsuccess = function() { |
|
255 debug("succeeded opening the db, let's close it now"); |
|
256 req.result.close(); |
|
257 deferred.resolve(this.contactsCount()); |
|
258 }.bind(this); |
|
259 |
|
260 req.onerror = function() { |
|
261 deferred.reject(this.error); |
|
262 }; |
|
263 |
|
264 return deferred.promise; |
|
265 }, |
|
266 |
|
267 createSchema: function(db, transaction) { |
|
268 debug("createSchema for version " + this.version); |
|
269 this.data.SCHEMA(db, transaction); |
|
270 }, |
|
271 |
|
272 addContacts: function(db, transaction) { |
|
273 debug("adding contacts for version " + this.version); |
|
274 var os = transaction.objectStore(STORE_NAME); |
|
275 |
|
276 this.data.GOOD.forEach(function(contact) { |
|
277 os.put(contact); |
|
278 }); |
|
279 this.data.BAD.forEach(function(contact) { |
|
280 os.put(contact); |
|
281 }); |
|
282 }, |
|
283 |
|
284 contactsCount: function() { |
|
285 return this.data.BAD.length + this.data.GOOD.length; |
|
286 } |
|
287 }; |
|
288 |
|
289 DataManager.delete = function() { |
|
290 debug("Deleting the database"); |
|
291 var deferred = Promise.defer(); |
|
292 |
|
293 /* forcibly close the db before deleting it */ |
|
294 ContactService._db.close(); |
|
295 |
|
296 var req = indexedDB.deleteDatabase(DB_NAME); |
|
297 req.onsuccess = function() { |
|
298 debug("Successfully deleted!"); |
|
299 deferred.resolve(); |
|
300 }; |
|
301 |
|
302 req.onerror = function() { |
|
303 debug("Not deleted, error is " + this.error.name); |
|
304 deferred.reject(this.error); |
|
305 }; |
|
306 |
|
307 req.onblocked = function() { |
|
308 debug("Waiting for the current users"); |
|
309 }; |
|
310 |
|
311 return deferred.promise; |
|
312 }; |
|
313 |
|
314 addMessageListener("createDB", function(version) { |
|
315 // Promises help handling gracefully exceptions |
|
316 Promise.resolve().then(function() { |
|
317 return new DataManager(version); |
|
318 }).then(function(manager) { |
|
319 return manager.open(); |
|
320 }).then(function onSuccess(count) { |
|
321 sendAsyncMessage("createDB.success", count); |
|
322 }, function onError(err) { |
|
323 sendAsyncMessage("createDB.error", "Failed to create the DB: " + |
|
324 "(" + err.name + ") " + err.message); |
|
325 }); |
|
326 }); |
|
327 |
|
328 addMessageListener("deleteDB", function() { |
|
329 Promise.resolve().then( |
|
330 DataManager.delete |
|
331 ).then(function onSuccess() { |
|
332 sendAsyncMessage("deleteDB.success"); |
|
333 }, function onError(err) { |
|
334 sendAsyncMessage("deleteDB.error", "Failed to delete the DB:" + err.name); |
|
335 }); |
|
336 }); |