| 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 |