OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // Shared cloud importer namespace | 5 // Shared cloud importer namespace |
6 var importer = importer || {}; | 6 var importer = importer || {}; |
7 | 7 |
8 /** @enum {string} */ | 8 /** @enum {string} */ |
9 importer.ScanEvent = { | 9 importer.ScanEvent = { |
10 FINALIZED: 'finalized', | 10 FINALIZED: 'finalized', |
(...skipping 25 matching lines...) Expand all Loading... |
36 }; | 36 }; |
37 | 37 |
38 /** | 38 /** |
39 * @typedef {function( | 39 * @typedef {function( |
40 * !importer.ScanEvent, importer.ScanResult)} | 40 * !importer.ScanEvent, importer.ScanResult)} |
41 */ | 41 */ |
42 importer.ScanObserver; | 42 importer.ScanObserver; |
43 | 43 |
44 /** | 44 /** |
45 * Volume types eligible for the affections of Cloud Import. | 45 * Volume types eligible for the affections of Cloud Import. |
46 * @private @const {!Array.<!VolumeManagerCommon.VolumeType>} | 46 * @private @const {!Array<!VolumeManagerCommon.VolumeType>} |
47 */ | 47 */ |
48 importer.ELIGIBLE_VOLUME_TYPES_ = [ | 48 importer.ELIGIBLE_VOLUME_TYPES_ = [ |
49 VolumeManagerCommon.VolumeType.MTP, | 49 VolumeManagerCommon.VolumeType.MTP, |
50 VolumeManagerCommon.VolumeType.REMOVABLE | 50 VolumeManagerCommon.VolumeType.REMOVABLE |
51 ]; | 51 ]; |
52 | 52 |
53 /** | 53 /** |
54 * @enum {string} | 54 * @enum {string} |
55 */ | 55 */ |
56 importer.Destination = { | 56 importer.Destination = { |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
137 path !== '/MP_ROOT' && path !== '/MP_ROOT/') { | 137 path !== '/MP_ROOT' && path !== '/MP_ROOT/') { |
138 return false; | 138 return false; |
139 } | 139 } |
140 | 140 |
141 console.assert(volumeInfoProvider !== null); | 141 console.assert(volumeInfoProvider !== null); |
142 var volumeInfo = volumeInfoProvider.getVolumeInfo(entry); | 142 var volumeInfo = volumeInfoProvider.getVolumeInfo(entry); |
143 return importer.isEligibleVolume(volumeInfo); | 143 return importer.isEligibleVolume(volumeInfo); |
144 }; | 144 }; |
145 | 145 |
146 /** | 146 /** |
147 * @return {!Promise.<boolean>} Resolves with true when Cloud Import feature | 147 * @return {!Promise<boolean>} Resolves with true when Cloud Import feature |
148 * is enabled. | 148 * is enabled. |
149 */ | 149 */ |
150 importer.importEnabled = function() { | 150 importer.importEnabled = function() { |
151 return new Promise( | 151 return new Promise( |
152 function(resolve, reject) { | 152 function(resolve, reject) { |
153 chrome.commandLinePrivate.hasSwitch( | 153 chrome.commandLinePrivate.hasSwitch( |
154 'disable-cloud-import', | 154 'disable-cloud-import', |
155 /** @param {boolean} disabled */ | 155 /** @param {boolean} disabled */ |
156 function(disabled) { | 156 function(disabled) { |
157 resolve(!disabled); | 157 resolve(!disabled); |
(...skipping 17 matching lines...) Expand all Loading... |
175 console.error( | 175 console.error( |
176 'Unrecognized message type received from photos app: ' + message); | 176 'Unrecognized message type received from photos app: ' + message); |
177 return Promise.reject(); | 177 return Promise.reject(); |
178 } | 178 } |
179 | 179 |
180 var storage = importer.ChromeLocalStorage.getInstance(); | 180 var storage = importer.ChromeLocalStorage.getInstance(); |
181 return storage.set(importer.Setting.PHOTOS_APP_ENABLED, message); | 181 return storage.set(importer.Setting.PHOTOS_APP_ENABLED, message); |
182 }; | 182 }; |
183 | 183 |
184 /** | 184 /** |
185 * @return {!Promise.<boolean>} Resolves with true when Cloud Import feature | 185 * @return {!Promise<boolean>} Resolves with true when Cloud Import feature |
186 * is enabled. | 186 * is enabled. |
187 */ | 187 */ |
188 importer.isPhotosAppImportEnabled = function() { | 188 importer.isPhotosAppImportEnabled = function() { |
189 var storage = importer.ChromeLocalStorage.getInstance(); | 189 var storage = importer.ChromeLocalStorage.getInstance(); |
190 return storage.get(importer.Setting.PHOTOS_APP_ENABLED, false); | 190 return storage.get(importer.Setting.PHOTOS_APP_ENABLED, false); |
191 }; | 191 }; |
192 | 192 |
193 /** | 193 /** |
194 * @param {!Date} date | 194 * @param {!Date} date |
195 * @return {string} The current date, in YYYY-MM-DD format. | 195 * @return {string} The current date, in YYYY-MM-DD format. |
196 */ | 196 */ |
197 importer.getDirectoryNameForDate = function(date) { | 197 importer.getDirectoryNameForDate = function(date) { |
198 var padAndConvert = function(i) { | 198 var padAndConvert = function(i) { |
199 return (i < 10 ? '0' : '') + i.toString(); | 199 return (i < 10 ? '0' : '') + i.toString(); |
200 }; | 200 }; |
201 | 201 |
202 var year = date.getFullYear().toString(); | 202 var year = date.getFullYear().toString(); |
203 // Months are 0-based, but days aren't. | 203 // Months are 0-based, but days aren't. |
204 var month = padAndConvert(date.getMonth() + 1); | 204 var month = padAndConvert(date.getMonth() + 1); |
205 var day = padAndConvert(date.getDate()); | 205 var day = padAndConvert(date.getDate()); |
206 | 206 |
207 // NOTE: We use YYYY-MM-DD since it sorts numerically. | 207 // NOTE: We use YYYY-MM-DD since it sorts numerically. |
208 // Ideally this would be localized and appropriate sorting would | 208 // Ideally this would be localized and appropriate sorting would |
209 // be done behind the scenes. | 209 // be done behind the scenes. |
210 return year + '-' + month + '-' + day; | 210 return year + '-' + month + '-' + day; |
211 }; | 211 }; |
212 | 212 |
213 /** | 213 /** |
214 * @return {!Promise.<number>} Resolves with an integer that is probably | 214 * @return {!Promise<number>} Resolves with an integer that is probably |
215 * relatively unique to this machine (among a users machines). | 215 * relatively unique to this machine (among a users machines). |
216 */ | 216 */ |
217 importer.getMachineId = function() { | 217 importer.getMachineId = function() { |
218 var storage = importer.ChromeLocalStorage.getInstance(); | 218 var storage = importer.ChromeLocalStorage.getInstance(); |
219 return storage.get(importer.Setting.MACHINE_ID) | 219 return storage.get(importer.Setting.MACHINE_ID) |
220 .then( | 220 .then( |
221 function(id) { | 221 function(id) { |
222 if (id) { | 222 if (id) { |
223 return id; | 223 return id; |
224 } | 224 } |
225 var id = importer.generateMachineId_(); | 225 var id = importer.generateMachineId_(); |
226 return storage.set(importer.Setting.MACHINE_ID, id) | 226 return storage.set(importer.Setting.MACHINE_ID, id) |
227 .then( | 227 .then( |
228 function() { | 228 function() { |
229 return id; | 229 return id; |
230 }); | 230 }); |
231 }); | 231 }); |
232 }; | 232 }; |
233 | 233 |
234 /** | 234 /** |
235 * @return {!Promise.<string>} Resolves with the filename of this | 235 * @return {!Promise<string>} Resolves with the filename of this |
236 * machines history file. | 236 * machines history file. |
237 */ | 237 */ |
238 importer.getHistoryFilename = function() { | 238 importer.getHistoryFilename = function() { |
239 return importer.getMachineId().then( | 239 return importer.getMachineId().then( |
240 function(machineId) { | 240 function(machineId) { |
241 return machineId + '-import-history.log'; | 241 return machineId + '-import-history.log'; |
242 }); | 242 }); |
243 }; | 243 }; |
244 | 244 |
245 /** | 245 /** |
246 * @param {number} logId | 246 * @param {number} logId |
247 * @return {!Promise.<string>} Resolves with the filename of this | 247 * @return {!Promise<string>} Resolves with the filename of this |
248 * machines debug log file. | 248 * machines debug log file. |
249 */ | 249 */ |
250 importer.getDebugLogFilename = function(logId) { | 250 importer.getDebugLogFilename = function(logId) { |
251 return importer.getMachineId().then( | 251 return importer.getMachineId().then( |
252 function(machineId) { | 252 function(machineId) { |
253 return machineId + '-import-debug-' + logId + '.log'; | 253 return machineId + '-import-debug-' + logId + '.log'; |
254 }); | 254 }); |
255 }; | 255 }; |
256 | 256 |
257 /** | 257 /** |
(...skipping 15 matching lines...) Expand all Loading... |
273 importer.Resolver = function() { | 273 importer.Resolver = function() { |
274 /** @private {boolean} */ | 274 /** @private {boolean} */ |
275 this.settled_ = false; | 275 this.settled_ = false; |
276 | 276 |
277 /** @private {function(T=)} */ | 277 /** @private {function(T=)} */ |
278 this.resolve_; | 278 this.resolve_; |
279 | 279 |
280 /** @private {function(*=)} */ | 280 /** @private {function(*=)} */ |
281 this.reject_; | 281 this.reject_; |
282 | 282 |
283 /** @private {!Promise.<T>} */ | 283 /** @private {!Promise<T>} */ |
284 this.promise_ = new Promise( | 284 this.promise_ = new Promise( |
285 function(resolve, reject) { | 285 function(resolve, reject) { |
286 this.resolve_ = resolve; | 286 this.resolve_ = resolve; |
287 this.reject_ = reject; | 287 this.reject_ = reject; |
288 }.bind(this)); | 288 }.bind(this)); |
289 | 289 |
290 var settler = function() { | 290 var settler = function() { |
291 this.settled_ = true; | 291 this.settled_ = true; |
292 }.bind(this); | 292 }.bind(this); |
293 | 293 |
294 this.promise_.then(settler, settler); | 294 this.promise_.then(settler, settler); |
295 }; | 295 }; |
296 | 296 |
297 importer.Resolver.prototype = /** @struct */ { | 297 importer.Resolver.prototype = /** @struct */ { |
298 /** | 298 /** |
299 * @return {function(T=)} | 299 * @return {function(T=)} |
300 * @template T | 300 * @template T |
301 */ | 301 */ |
302 get resolve() { | 302 get resolve() { |
303 return this.resolve_; | 303 return this.resolve_; |
304 }, | 304 }, |
305 /** | 305 /** |
306 * @return {function(*=)} | 306 * @return {function(*=)} |
307 * @template T | 307 * @template T |
308 */ | 308 */ |
309 get reject() { | 309 get reject() { |
310 return this.reject_; | 310 return this.reject_; |
311 }, | 311 }, |
312 /** | 312 /** |
313 * @return {!Promise.<T>} | 313 * @return {!Promise<T>} |
314 * @template T | 314 * @template T |
315 */ | 315 */ |
316 get promise() { | 316 get promise() { |
317 return this.promise_; | 317 return this.promise_; |
318 }, | 318 }, |
319 /** @return {boolean} */ | 319 /** @return {boolean} */ |
320 get settled() { | 320 get settled() { |
321 return this.settled_; | 321 return this.settled_; |
322 } | 322 } |
323 }; | 323 }; |
(...skipping 27 matching lines...) Expand all Loading... |
351 * @struct | 351 * @struct |
352 * | 352 * |
353 * @param {!FileEntry} fileEntry | 353 * @param {!FileEntry} fileEntry |
354 */ | 354 */ |
355 importer.PromisingFileEntry = function(fileEntry) { | 355 importer.PromisingFileEntry = function(fileEntry) { |
356 /** @private {!FileEntry} */ | 356 /** @private {!FileEntry} */ |
357 this.fileEntry_ = fileEntry; | 357 this.fileEntry_ = fileEntry; |
358 }; | 358 }; |
359 | 359 |
360 /** | 360 /** |
| 361 * Convenience method for creating new instances. Can, for example, |
| 362 * be passed to Array.map. |
| 363 * |
| 364 * @param {!FileEntry} entry |
| 365 * @return {!importer.PromisingFileEntry} |
| 366 */ |
| 367 importer.PromisingFileEntry.create = function(entry) { |
| 368 return new importer.PromisingFileEntry(entry); |
| 369 }; |
| 370 |
| 371 /** |
361 * A "Promisary" wrapper around entry.getWriter. | 372 * A "Promisary" wrapper around entry.getWriter. |
362 * @return {!Promise.<!FileWriter>} | 373 * @return {!Promise<!FileWriter>} |
363 */ | 374 */ |
364 importer.PromisingFileEntry.prototype.createWriter = function() { | 375 importer.PromisingFileEntry.prototype.createWriter = function() { |
365 return new Promise(this.fileEntry_.createWriter.bind(this.fileEntry_)); | 376 return new Promise(this.fileEntry_.createWriter.bind(this.fileEntry_)); |
366 }; | 377 }; |
367 | 378 |
368 /** | 379 /** |
369 * A "Promisary" wrapper around entry.file. | 380 * A "Promisary" wrapper around entry.file. |
370 * @return {!Promise.<!File>} | 381 * @return {!Promise<!File>} |
371 */ | 382 */ |
372 importer.PromisingFileEntry.prototype.file = function() { | 383 importer.PromisingFileEntry.prototype.file = function() { |
373 return new Promise(this.fileEntry_.file.bind(this.fileEntry_)); | 384 return new Promise(this.fileEntry_.file.bind(this.fileEntry_)); |
374 }; | 385 }; |
375 | 386 |
376 /** | 387 /** |
377 * @return {!Promise.<!Object>} | 388 * @return {!Promise<!Object>} |
378 */ | 389 */ |
379 importer.PromisingFileEntry.prototype.getMetadata = function() { | 390 importer.PromisingFileEntry.prototype.getMetadata = function() { |
380 return new Promise(this.fileEntry_.getMetadata.bind(this.fileEntry_)); | 391 return new Promise(this.fileEntry_.getMetadata.bind(this.fileEntry_)); |
381 }; | 392 }; |
382 | 393 |
383 /** | 394 /** |
384 * This prefix is stripped from URL used in import history. It is stripped | 395 * This prefix is stripped from URL used in import history. It is stripped |
385 * to same on disk space, parsing time, and runtime memory. | 396 * to same on disk space, parsing time, and runtime memory. |
386 * @private @const {string} | 397 * @private @const {string} |
387 */ | 398 */ |
(...skipping 25 matching lines...) Expand all Loading... |
413 */ | 424 */ |
414 importer.inflateAppUrl = function(deflated) { | 425 importer.inflateAppUrl = function(deflated) { |
415 if (deflated.substring(0, 1) === '$') { | 426 if (deflated.substring(0, 1) === '$') { |
416 return importer.APP_URL_PREFIX_ + deflated.substring(1); | 427 return importer.APP_URL_PREFIX_ + deflated.substring(1); |
417 } | 428 } |
418 return deflated; | 429 return deflated; |
419 }; | 430 }; |
420 | 431 |
421 /** | 432 /** |
422 * @param {!FileEntry} fileEntry | 433 * @param {!FileEntry} fileEntry |
423 * @return {!Promise.<string>} Resolves with a "hashcode" consisting of | 434 * @return {!Promise<string>} Resolves with a "hashcode" consisting of |
424 * just the last modified time and the file size. | 435 * just the last modified time and the file size. |
425 */ | 436 */ |
426 importer.createMetadataHashcode = function(fileEntry) { | 437 importer.createMetadataHashcode = function(fileEntry) { |
427 var entry = new importer.PromisingFileEntry(fileEntry); | 438 var entry = new importer.PromisingFileEntry(fileEntry); |
428 return new Promise( | 439 return new Promise( |
429 /** | 440 /** |
430 * @param {function()} resolve | 441 * @param {function()} resolve |
431 * @param {function()} reject | 442 * @param {function()} reject |
432 * @this {importer.PersistentImportHistory} | 443 * @this {importer.PersistentImportHistory} |
433 */ | 444 */ |
434 function(resolve, reject) { | 445 function(resolve, reject) { |
435 entry.getMetadata() | 446 entry.getMetadata() |
436 .then( | 447 .then( |
437 /** | 448 /** |
438 * @param {!Object} metadata | 449 * @param {!Object} metadata |
439 * @return {!Promise.<string>} | 450 * @return {!Promise<string>} |
440 * @this {importer.PersistentImportHistory} | 451 * @this {importer.PersistentImportHistory} |
441 */ | 452 */ |
442 function(metadata) { | 453 function(metadata) { |
443 if (!('modificationTime' in metadata)) { | 454 if (!('modificationTime' in metadata)) { |
444 reject('File entry missing "modificationTime" field.'); | 455 reject('File entry missing "modificationTime" field.'); |
445 } else if (!('size' in metadata)) { | 456 } else if (!('size' in metadata)) { |
446 reject('File entry missing "size" field.'); | 457 reject('File entry missing "size" field.'); |
447 } else { | 458 } else { |
448 var secondsSinceEpoch = | 459 var secondsSinceEpoch = |
449 importer.toSecondsFromEpoch(metadata.modificationTime); | 460 importer.toSecondsFromEpoch(metadata.modificationTime); |
(...skipping 12 matching lines...) Expand all Loading... |
462 importer.toSecondsFromEpoch = function(date) { | 473 importer.toSecondsFromEpoch = function(date) { |
463 // Since we're parsing a value that only has | 474 // Since we're parsing a value that only has |
464 // precision to the second, our last three digits | 475 // precision to the second, our last three digits |
465 // will always be 000. We strip them and end up | 476 // will always be 000. We strip them and end up |
466 // with seconds. | 477 // with seconds. |
467 var milliseconds = String(Date.parse(date)); | 478 var milliseconds = String(Date.parse(date)); |
468 return milliseconds.substring(0, milliseconds.length - 3); | 479 return milliseconds.substring(0, milliseconds.length - 3); |
469 }; | 480 }; |
470 | 481 |
471 /** | 482 /** |
472 * Factory interface for creating/accessing synced {@code FileEntry} | 483 * Namespace for ChromeSyncFilesystem related stuffs. |
473 * instances and listening to sync events on those files. | |
474 * | |
475 * @interface | |
476 */ | 484 */ |
477 importer.SyncFileEntryProvider = function() {}; | 485 importer.ChromeSyncFilesystem = {}; |
478 | |
479 /** | |
480 * Provides accsess to the sync FileEntry owned/managed by this class. | |
481 * | |
482 * @return {!Promise.<!FileEntry>} | |
483 */ | |
484 importer.SyncFileEntryProvider.prototype.getSyncFileEntry; | |
485 | |
486 /** | |
487 * Factory for synchronized files based on chrome.syncFileSystem. | |
488 * | |
489 * @constructor | |
490 * @implements {importer.SyncFileEntryProvider} | |
491 * @struct | |
492 * | |
493 * @param {string} fileName | |
494 */ | |
495 importer.ChromeSyncFileEntryProvider = function(fileName) { | |
496 | |
497 /** @private {string} */ | |
498 this.fileName_ = fileName; | |
499 | |
500 /** @private {!Array.<function()>} */ | |
501 this.syncListeners_ = []; | |
502 | |
503 /** @private {Promise.<!FileEntry>} */ | |
504 this.fileEntryPromise_ = null; | |
505 }; | |
506 | |
507 /** | |
508 * Returns a sync FileEntry. Convenience method for class that just want | |
509 * a file, but don't need to monitor changes. | |
510 * @param {!Promise.<string>} fileNamePromise | |
511 * @return {!Promise.<!FileEntry>} | |
512 */ | |
513 importer.ChromeSyncFileEntryProvider.getFileEntry = | |
514 function(fileNamePromise) { | |
515 return fileNamePromise.then( | |
516 function(fileName) { | |
517 return new importer.ChromeSyncFileEntryProvider(fileName) | |
518 .getSyncFileEntry(); | |
519 }); | |
520 }; | |
521 | |
522 /** @override */ | |
523 importer.ChromeSyncFileEntryProvider.prototype.getSyncFileEntry = function() { | |
524 if (this.fileEntryPromise_) { | |
525 return /** @type {!Promise.<!FileEntry>} */ (this.fileEntryPromise_); | |
526 }; | |
527 | |
528 this.fileEntryPromise_ = this.getFileSystem_() | |
529 .then( | |
530 /** | |
531 * @param {!FileSystem} fileSystem | |
532 * @return {!Promise.<!FileEntry>} | |
533 * @this {importer.ChromeSyncFileEntryProvider} | |
534 */ | |
535 function(fileSystem) { | |
536 return this.getFileEntry_(fileSystem); | |
537 }.bind(this)); | |
538 | |
539 return /** @type {!Promise.<!FileEntry>} */ (this.fileEntryPromise_); | |
540 }; | |
541 | 486 |
542 /** | 487 /** |
543 * Wraps chrome.syncFileSystem in a Promise. | 488 * Wraps chrome.syncFileSystem in a Promise. |
544 * | 489 * |
545 * @return {!Promise.<!FileSystem>} | 490 * @return {!Promise<!FileSystem>} |
546 * @private | 491 * @private |
547 */ | 492 */ |
548 importer.ChromeSyncFileEntryProvider.prototype.getFileSystem_ = function() { | 493 importer.ChromeSyncFilesystem.getFileSystem_ = function() { |
549 return new Promise( | 494 return new Promise( |
550 /** | |
551 * @param {function()} resolve | |
552 * @param {function()} reject | |
553 * @this {importer.ChromeSyncFileEntryProvider} | |
554 */ | |
555 function(resolve, reject) { | 495 function(resolve, reject) { |
556 chrome.syncFileSystem.requestFileSystem( | 496 chrome.syncFileSystem.requestFileSystem( |
557 /** | 497 /** @param {FileSystem} fileSystem */ |
558 * @param {FileSystem} fileSystem | |
559 * @this {importer.ChromeSyncFileEntryProvider} | |
560 */ | |
561 function(fileSystem) { | 498 function(fileSystem) { |
562 if (chrome.runtime.lastError) { | 499 if (chrome.runtime.lastError) { |
563 reject(chrome.runtime.lastError.message); | 500 reject(chrome.runtime.lastError.message); |
564 } else { | 501 } else { |
565 resolve(/** @type {!FileSystem} */ (fileSystem)); | 502 resolve(/** @type {!FileSystem} */ (fileSystem)); |
566 } | 503 } |
567 }); | 504 }); |
568 }.bind(this)); | 505 }); |
569 }; | 506 }; |
570 | 507 |
571 /** | 508 /** |
572 * @param {!FileSystem} fileSystem | 509 * Returns a sync file entry for the named file, creating it as needed. |
573 * @return {!Promise.<!FileEntry>} | 510 * |
574 * @private | 511 * @param {!Promise<string>} fileNamePromise |
| 512 * @return {!Promise<!FileEntry>} |
575 */ | 513 */ |
576 importer.ChromeSyncFileEntryProvider.prototype.getFileEntry_ = | 514 importer.ChromeSyncFilesystem.getOrCreateFileEntry = function(fileNamePromise) { |
577 function(fileSystem) { | 515 var promise = importer.ChromeSyncFilesystem.getFileSystem_() |
578 return new Promise( | 516 .then( |
579 /** | 517 /** |
580 * @param {function()} resolve | 518 * @param {!FileSystem} fileSystem |
581 * @param {function()} reject | 519 * @return {!Promise<!FileEntry>} |
582 * @this {importer.ChromeSyncFileEntryProvider} | 520 */ |
583 */ | 521 function(fileSystem) { |
584 function(resolve, reject) { | 522 fileNamePromise.then( |
585 fileSystem.root.getFile( | 523 /** @param {string} fileName */ |
586 this.fileName_, | 524 function(fileName) { |
587 { | 525 return new Promise( |
588 create: true, | 526 function(resolve, reject) { |
589 exclusive: false | 527 fileSystem.root.getFile( |
590 }, | 528 fileName, |
591 resolve, | 529 { |
592 reject); | 530 create: true, |
593 }.bind(this)); | 531 exclusive: false |
| 532 }, |
| 533 resolve, |
| 534 reject); |
| 535 }); |
| 536 |
| 537 }); |
| 538 }); |
| 539 |
| 540 return /** @type {!Promise<!FileEntry>} */ (promise); |
594 }; | 541 }; |
595 | 542 |
596 /** | 543 /** |
597 * Handles sync events. Checks to see if the event is for the file | |
598 * we track, and sync-direction, and if so, notifies syncListeners. | |
599 * | |
600 * @see https://developer.chrome.com/apps/syncFileSystem | |
601 * #event-onFileStatusChanged | |
602 * | |
603 * @param {!Object} event Having a structure not unlike: { | |
604 * fileEntry: Entry, | |
605 * status: string, | |
606 * action: (string|undefined), | |
607 * direction: (string|undefined)} | |
608 * | |
609 * @private | |
610 */ | |
611 importer.ChromeSyncFileEntryProvider.prototype.handleSyncEvent_ = | |
612 function(event) { | |
613 if (!this.fileEntryPromise_) { | |
614 return; | |
615 } | |
616 | |
617 this.fileEntryPromise_.then( | |
618 /** | |
619 * @param {!FileEntry} fileEntry | |
620 * @this {importer.ChromeSyncFileEntryProvider} | |
621 */ | |
622 function(fileEntry) { | |
623 if (event['fileEntry'].fullPath !== fileEntry.fullPath) { | |
624 return; | |
625 } | |
626 | |
627 if (event.direction && event.direction !== 'remote_to_local') { | |
628 return; | |
629 } | |
630 | |
631 if (event.action && event.action !== 'updated') { | |
632 console.warn( | |
633 'Unusual sync event action for sync file: ' + event.action); | |
634 return; | |
635 } | |
636 | |
637 this.syncListeners_.forEach( | |
638 /** | |
639 * @param {function()} listener | |
640 * @this {importer.ChromeSyncFileEntryProvider} | |
641 */ | |
642 function(listener) { | |
643 // Notify by way of a promise so that it is fully asynchronous | |
644 // (which can rationalize testing). | |
645 Promise.resolve().then(listener); | |
646 }.bind(this)); | |
647 }.bind(this)); | |
648 }; | |
649 | |
650 /** | |
651 * A basic logging mechanism. | 544 * A basic logging mechanism. |
652 * | 545 * |
653 * @interface | 546 * @interface |
654 */ | 547 */ |
655 importer.Logger = function() {}; | 548 importer.Logger = function() {}; |
656 | 549 |
657 /** | 550 /** |
658 * Writes an error message to the logger followed by a new line. | 551 * Writes an error message to the logger followed by a new line. |
659 * | 552 * |
660 * @param {string} message | 553 * @param {string} message |
(...skipping 16 matching lines...) Expand all Loading... |
677 importer.Logger.prototype.catcher; | 570 importer.Logger.prototype.catcher; |
678 | 571 |
679 /** | 572 /** |
680 * A {@code importer.Logger} that persists data in a {@code FileEntry}. | 573 * A {@code importer.Logger} that persists data in a {@code FileEntry}. |
681 * | 574 * |
682 * @constructor | 575 * @constructor |
683 * @implements {importer.Logger} | 576 * @implements {importer.Logger} |
684 * @struct | 577 * @struct |
685 * @final | 578 * @final |
686 * | 579 * |
687 * @param {!Promise.<!FileEntry>} fileEntryPromise | 580 * @param {!Promise<!FileEntry>} fileEntryPromise |
688 * @param {!Promise.<!analytics.Tracker>} trackerPromise | 581 * @param {!Promise<!analytics.Tracker>} trackerPromise |
689 */ | 582 */ |
690 importer.RuntimeLogger = function(fileEntryPromise, trackerPromise) { | 583 importer.RuntimeLogger = function(fileEntryPromise, trackerPromise) { |
691 | 584 |
692 /** @private {!Promise.<!importer.PromisingFileEntry>} */ | 585 /** @private {!Promise<!importer.PromisingFileEntry>} */ |
693 this.fileEntryPromise_ = fileEntryPromise.then( | 586 this.fileEntryPromise_ = fileEntryPromise.then( |
694 /** @param {!FileEntry} fileEntry */ | 587 /** @param {!FileEntry} fileEntry */ |
695 function(fileEntry) { | 588 function(fileEntry) { |
696 return new importer.PromisingFileEntry(fileEntry); | 589 return new importer.PromisingFileEntry(fileEntry); |
697 }); | 590 }); |
698 | 591 |
699 /** @private {!Promise.<!analytics.Tracker>} */ | 592 /** @private {!Promise<!analytics.Tracker>} */ |
700 this.trackerPromise_ = trackerPromise; | 593 this.trackerPromise_ = trackerPromise; |
701 }; | 594 }; |
702 | 595 |
703 /** | 596 /** |
704 * Reports an error to analytics. | 597 * Reports an error to analytics. |
705 * | 598 * |
706 * @param {string} context MUST NOT contain any dynamic error content, | 599 * @param {string} context MUST NOT contain any dynamic error content, |
707 * only statically defined string will dooooo. | 600 * only statically defined string will dooooo. |
708 */ | 601 */ |
709 importer.RuntimeLogger.prototype.reportErrorContext_ = function(context) { | 602 importer.RuntimeLogger.prototype.reportErrorContext_ = function(context) { |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
798 /** @private {importer.Logger} */ | 691 /** @private {importer.Logger} */ |
799 importer.logger_ = null; | 692 importer.logger_ = null; |
800 | 693 |
801 /** | 694 /** |
802 * Creates a new logger instance...all ready to go. | 695 * Creates a new logger instance...all ready to go. |
803 * | 696 * |
804 * @return {!importer.Logger} | 697 * @return {!importer.Logger} |
805 */ | 698 */ |
806 importer.getLogger = function() { | 699 importer.getLogger = function() { |
807 if (!importer.logger_) { | 700 if (!importer.logger_) { |
| 701 |
808 var nextLogId = importer.getNextDebugLogId_(); | 702 var nextLogId = importer.getNextDebugLogId_(); |
809 | 703 |
810 /** @return {!Promise} */ | 704 /** @return {!Promise} */ |
811 var rotator = function() { | 705 var rotator = function() { |
812 return importer.rotateLogs( | 706 return importer.rotateLogs( |
813 nextLogId, | 707 nextLogId, |
814 importer.ChromeSyncFileEntryProvider.getFileEntry); | 708 importer.ChromeSyncFilesystem.getOrCreateFileEntry); |
815 }; | 709 }; |
816 | 710 |
817 // This is a sligtly odd arrangement in service of two goals. | 711 // This is a sligtly odd arrangement in service of two goals. |
818 // | 712 // |
819 // 1) Make a logger available synchronously. | 713 // 1) Make a logger available synchronously. |
820 // 2) Nuke old log files before reusing their names. | 714 // 2) Nuke old log files before reusing their names. |
821 // | 715 // |
822 // In support of these goals we push the "rotator" between | 716 // In support of these goals we push the "rotator" between |
823 // the call to load the file entry and the method that | 717 // the call to load the file entry and the method that |
824 // produces the name of the file to load. That method | 718 // produces the name of the file to load. That method |
825 // (getDebugLogFilename) returns promise. We exploit this. | 719 // (getDebugLogFilename) returns promise. We exploit this. |
826 importer.logger_ = new importer.RuntimeLogger( | 720 importer.logger_ = new importer.RuntimeLogger( |
827 importer.ChromeSyncFileEntryProvider.getFileEntry( | 721 importer.ChromeSyncFilesystem.getOrCreateFileEntry( |
828 /** @type {!Promise.<string>} */ (rotator().then( | 722 /** @type {!Promise<string>} */ (rotator().then( |
829 importer.getDebugLogFilename.bind(null, nextLogId)))), | 723 importer.getDebugLogFilename.bind(null, nextLogId)))), |
830 importer.getTracker_()); | 724 importer.getTracker_()); |
831 } | 725 } |
832 | 726 |
833 return importer.logger_; | 727 return importer.logger_; |
834 }; | 728 }; |
835 | 729 |
836 /** | 730 /** |
837 * Fetch analytics.Tracker from background page. | 731 * Fetch analytics.Tracker from background page. |
838 * @return {!Promise.<!analytics.Tracker>} | 732 * @return {!Promise<!analytics.Tracker>} |
839 * @private | 733 * @private |
840 */ | 734 */ |
841 importer.getTracker_ = function() { | 735 importer.getTracker_ = function() { |
842 return new Promise( | 736 return new Promise( |
843 function(resolve, reject) { | 737 function(resolve, reject) { |
844 chrome.runtime.getBackgroundPage( | 738 chrome.runtime.getBackgroundPage( |
845 /** @param {Window=} opt_background */ | 739 /** @param {Window=} opt_background */ |
846 function(opt_background) { | 740 function(opt_background) { |
847 if (chrome.runtime.lastError) { | 741 if (chrome.runtime.lastError) { |
848 reject(chrome.runtime.lastError); | 742 reject(chrome.runtime.lastError); |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
939 } else { | 833 } else { |
940 resolve(undefined); | 834 resolve(undefined); |
941 } | 835 } |
942 }); | 836 }); |
943 }); | 837 }); |
944 }; | 838 }; |
945 | 839 |
946 /** | 840 /** |
947 * @param {string} key | 841 * @param {string} key |
948 * @param {T=} opt_default | 842 * @param {T=} opt_default |
949 * @return {!Promise.<T>} Resolves with the value, or {@code opt_default} when | 843 * @return {!Promise<T>} Resolves with the value, or {@code opt_default} when |
950 * no value entry existis, or {@code undefined}. | 844 * no value entry existis, or {@code undefined}. |
951 * @template T | 845 * @template T |
952 */ | 846 */ |
953 importer.ChromeLocalStorage.prototype.get = function(key, opt_default) { | 847 importer.ChromeLocalStorage.prototype.get = function(key, opt_default) { |
954 return new Promise( | 848 return new Promise( |
955 function(resolve, reject) { | 849 function(resolve, reject) { |
956 chrome.storage.local.get( | 850 chrome.storage.local.get( |
957 key, | 851 key, |
958 /** @param {Object.<string, ?>} values */ | 852 /** @param {Object.<string, ?>} values */ |
959 function(values) { | 853 function(values) { |
960 if (chrome.runtime.lastError) { | 854 if (chrome.runtime.lastError) { |
961 reject(chrome.runtime.lastError); | 855 reject(chrome.runtime.lastError); |
962 } else if (key in values) { | 856 } else if (key in values) { |
963 resolve(values[key]); | 857 resolve(values[key]); |
964 } else { | 858 } else { |
965 resolve(opt_default); | 859 resolve(opt_default); |
966 } | 860 } |
967 }); | 861 }); |
968 }); | 862 }); |
969 }; | 863 }; |
970 | 864 |
971 /** @private @const {!importer.ChromeLocalStorage} */ | 865 /** @private @const {!importer.ChromeLocalStorage} */ |
972 importer.ChromeLocalStorage.INSTANCE_ = new importer.ChromeLocalStorage(); | 866 importer.ChromeLocalStorage.INSTANCE_ = new importer.ChromeLocalStorage(); |
973 | 867 |
974 /** @return {!importer.ChromeLocalStorage} */ | 868 /** @return {!importer.ChromeLocalStorage} */ |
975 importer.ChromeLocalStorage.getInstance = function() { | 869 importer.ChromeLocalStorage.getInstance = function() { |
976 return importer.ChromeLocalStorage.INSTANCE_; | 870 return importer.ChromeLocalStorage.INSTANCE_; |
977 }; | 871 }; |
OLD | NEW |