Chromium Code Reviews| 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 /** | 5 /** |
| 6 * Class representing the results of a scan operation. | 6 * Class representing the results of a scan operation. |
| 7 * | 7 * |
| 8 * @interface | 8 * @interface |
| 9 */ | 9 */ |
| 10 importer.MediaScanner = function() {}; | 10 importer.MediaScanner = function() {}; |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 87 * @constructor | 87 * @constructor |
| 88 * @struct | 88 * @struct |
| 89 * @implements {importer.MediaScanner} | 89 * @implements {importer.MediaScanner} |
| 90 * | 90 * |
| 91 * @param {function(!FileEntry): !Promise.<string>} hashGenerator | 91 * @param {function(!FileEntry): !Promise.<string>} hashGenerator |
| 92 * @param {!importer.HistoryLoader} historyLoader | 92 * @param {!importer.HistoryLoader} historyLoader |
| 93 * @param {!importer.DirectoryWatcherFactory} watcherFactory | 93 * @param {!importer.DirectoryWatcherFactory} watcherFactory |
| 94 */ | 94 */ |
| 95 importer.DefaultMediaScanner = function( | 95 importer.DefaultMediaScanner = function( |
| 96 hashGenerator, historyLoader, watcherFactory) { | 96 hashGenerator, historyLoader, watcherFactory) { |
| 97 | |
| 98 /** @private {!importer.HistoryLoader} */ | |
| 99 this.historyLoader_ = historyLoader; | |
| 100 | |
| 101 /** @private {function(!FileEntry): !Promise.<string>} */ | |
| 102 this.createHashcode_ = hashGenerator; | |
| 103 | |
| 97 /** | 104 /** |
| 98 * A little factory for DefaultScanResults which allows us to forgo | 105 * A little factory for DefaultScanResults which allows us to forgo |
| 99 * the saving it's dependencies in our fields. | 106 * the saving it's dependencies in our fields. |
| 100 * @return {!importer.DefaultScanResult} | 107 * @return {!importer.DefaultScanResult} |
| 101 */ | 108 */ |
| 102 this.createScanResult_ = function() { | 109 this.createScanResult_ = function() { |
| 103 return new importer.DefaultScanResult(hashGenerator, historyLoader); | 110 return new importer.DefaultScanResult(); |
| 104 }; | 111 }; |
| 105 | 112 |
| 106 /** @private {!Array.<!importer.ScanObserver>} */ | 113 /** @private {!Array.<!importer.ScanObserver>} */ |
| 107 this.observers_ = []; | 114 this.observers_ = []; |
| 108 | 115 |
| 109 /** | 116 /** |
| 110 * @private {!importer.DirectoryWatcherFactory} | 117 * @private {!importer.DirectoryWatcherFactory} |
| 111 * @const | 118 * @const |
| 112 */ | 119 */ |
| 113 this.watcherFactory_ = watcherFactory; | 120 this.watcherFactory_ = watcherFactory; |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 132 importer.DefaultMediaScanner.prototype.scan = function(entries) { | 139 importer.DefaultMediaScanner.prototype.scan = function(entries) { |
| 133 if (entries.length == 0) { | 140 if (entries.length == 0) { |
| 134 throw new Error('Cannot scan empty list of entries.'); | 141 throw new Error('Cannot scan empty list of entries.'); |
| 135 } | 142 } |
| 136 | 143 |
| 137 var scanResult = this.createScanResult_(); | 144 var scanResult = this.createScanResult_(); |
| 138 var watcher = this.watcherFactory_( | 145 var watcher = this.watcherFactory_( |
| 139 /** @this {importer.DefaultMediaScanner} */ | 146 /** @this {importer.DefaultMediaScanner} */ |
| 140 function() { | 147 function() { |
| 141 scanResult.invalidateScan(); | 148 scanResult.invalidateScan(); |
| 142 this.observers_.forEach( | 149 this.notify_(importer.ScanEvent.INVALIDATED, scanResult); |
| 143 /** @param {!importer.ScanObserver} observer */ | |
| 144 function(observer) { | |
| 145 observer(importer.ScanEvent.INVALIDATED, scanResult); | |
| 146 }); | |
| 147 }.bind(this)); | 150 }.bind(this)); |
| 148 var scanPromises = entries.map( | 151 var scanPromises = entries.map( |
| 149 this.scanEntry_.bind(this, scanResult, watcher)); | 152 this.scanEntry_.bind(this, scanResult, watcher)); |
| 150 | 153 |
| 151 Promise.all(scanPromises) | 154 Promise.all(scanPromises) |
| 152 .then(scanResult.resolve) | 155 .then(scanResult.resolve) |
| 153 .catch(scanResult.reject); | 156 .catch(scanResult.reject); |
| 154 | 157 |
| 155 scanResult.whenFinal() | 158 scanResult.whenFinal() |
| 156 .then( | 159 .then( |
| 160 /** @this {importer.DefaultMediaScanner} */ | |
| 157 function() { | 161 function() { |
| 158 this.onScanFinished_(scanResult); | 162 this.notify_(importer.ScanEvent.FINALIZED, scanResult); |
| 159 }.bind(this)); | 163 }.bind(this)); |
| 160 | 164 |
| 161 return scanResult; | 165 return scanResult; |
| 162 }; | 166 }; |
| 163 | 167 |
| 164 /** | 168 /** |
| 165 * Called when a scan is finished. | 169 * Notifies all listeners at some point in the near future. |
| 166 * | 170 * |
| 171 * @param {!importer.ScanEvent} event | |
| 167 * @param {!importer.DefaultScanResult} result | 172 * @param {!importer.DefaultScanResult} result |
| 168 * @private | 173 * @private |
| 169 */ | 174 */ |
| 170 importer.DefaultMediaScanner.prototype.onScanFinished_ = function(result) { | 175 importer.DefaultMediaScanner.prototype.notify_ = function(event, result) { |
| 171 this.observers_.forEach( | 176 this.observers_.forEach( |
| 172 /** @param {!importer.ScanObserver} observer */ | 177 /** @param {!importer.ScanObserver} observer */ |
| 173 function(observer) { | 178 function(observer) { |
| 174 observer(importer.ScanEvent.FINALIZED, result); | 179 observer(event, result); |
| 175 }); | 180 }); |
| 176 }; | 181 }; |
| 177 | 182 |
| 178 /** | 183 /** |
| 179 * Resolves the entry to a list of {@code FileEntry}. | 184 * Resolves the entry by either: |
| 185 * a) recursing on it (when a directory) | |
| 186 * b) adding it to the results (when a media type file) | |
| 187 * c) ignoring it, if neither a or b | |
| 180 * | 188 * |
| 181 * @param {!importer.DefaultScanResult} result | 189 * @param {!importer.DefaultScanResult} scan |
| 182 * @param {!importer.DirectoryWatcher} watcher | 190 * @param {!importer.DirectoryWatcher} watcher |
| 183 * @param {!Entry} entry | 191 * @param {!Entry} entry |
| 192 * | |
| 184 * @return {!Promise} | 193 * @return {!Promise} |
| 185 * @private | 194 * @private |
| 186 */ | 195 */ |
| 187 importer.DefaultMediaScanner.prototype.scanEntry_ = | 196 importer.DefaultMediaScanner.prototype.scanEntry_ = |
| 188 function(result, watcher, entry) { | 197 function(scan, watcher, entry) { |
| 189 return entry.isFile ? | 198 if (entry.isDirectory) { |
| 190 result.onFileEntryFound(/** @type {!FileEntry} */ (entry)) : | 199 return this.scanDirectory_( |
| 191 this.scanDirectory_( | 200 scan, |
| 192 result, watcher, /** @type {!DirectoryEntry} */ (entry)); | 201 watcher, |
| 202 /** @type {!DirectoryEntry} */ (entry)); | |
| 203 } | |
| 204 | |
| 205 console.assert(entry.isFile); | |
|
Ben Kwa
2015/02/05 14:54:26
Suggestion: include an explanatory string in the a
Steve McKay
2015/02/05 15:46:46
Removed.
| |
| 206 return this.onFileEntryFound_(scan, /** @type {!FileEntry} */ (entry)); | |
| 193 }; | 207 }; |
| 194 | 208 |
| 195 /** | 209 /** |
| 196 * Finds all files beneath directory. | 210 * Finds all files beneath directory. |
| 197 * | 211 * |
| 198 * @param {!importer.DefaultScanResult} result | 212 * @param {!importer.DefaultScanResult} scan |
| 199 * @param {!importer.DirectoryWatcher} watcher | 213 * @param {!importer.DirectoryWatcher} watcher |
| 200 * @param {!DirectoryEntry} entry | 214 * @param {!DirectoryEntry} entry |
| 201 * @return {!Promise} | 215 * @return {!Promise} |
| 202 * @private | 216 * @private |
| 203 */ | 217 */ |
| 204 importer.DefaultMediaScanner.prototype.scanDirectory_ = | 218 importer.DefaultMediaScanner.prototype.scanDirectory_ = |
| 205 function(result, watcher, entry) { | 219 function(scan, watcher, entry) { |
| 206 // Collect promises for all files being added to results. | 220 // Collect promises for all files being added to results. |
| 207 // The directory scan promise can't resolve until all | 221 // The directory scan promise can't resolve until all |
| 208 // file entries are completely promised. | 222 // file entries are completely promised. |
| 209 var promises = []; | 223 var promises = []; |
| 210 | 224 |
| 211 return fileOperationUtil.findEntriesRecursively( | 225 return fileOperationUtil.findEntriesRecursively( |
| 212 entry, | 226 entry, |
| 213 /** @param {!Entry} entry */ | 227 /** |
| 228 * @param {!Entry} entry | |
| 229 * @this {importer.DefaultMediaScanner} | |
| 230 */ | |
| 214 function(entry) { | 231 function(entry) { |
| 215 if (watcher.triggered) { | 232 if (watcher.triggered) { |
| 233 console.log('Skipping file entry...watched directory was modified.'); | |
|
hirono
2015/02/05 10:51:43
Is it intentionally left here?
Steve McKay
2015/02/05 15:46:46
It was, but on second thought...removed it.
| |
| 216 return; | 234 return; |
| 217 } | 235 } |
| 236 | |
| 218 if (entry.isDirectory) { | 237 if (entry.isDirectory) { |
| 238 // Note, there is no need for us to recurse, the utility | |
| 239 // funciton findEntriesRecursively does that. So we | |
|
Ben Kwa
2015/02/05 14:54:26
function
Steve McKay
2015/02/05 15:46:46
Done.
| |
| 240 // just watch the directory for modifications, and that's it. | |
| 219 watcher.addDirectory(/** @type {!DirectoryEntry} */(entry)); | 241 watcher.addDirectory(/** @type {!DirectoryEntry} */(entry)); |
| 242 return; | |
| 243 } | |
| 244 | |
| 245 promises.push( | |
| 246 this.onFileEntryFound_(scan, /** @type {!FileEntry} */(entry))); | |
| 247 | |
| 248 }.bind(this)) | |
| 249 .then(Promise.all.bind(Promise, promises)); | |
| 250 }; | |
| 251 | |
| 252 /** | |
| 253 * Finds all files beneath directory. | |
| 254 * | |
| 255 * @param {!importer.DefaultScanResult} scan | |
| 256 * @param {!FileEntry} entry | |
| 257 * @return {!Promise} | |
| 258 * @private | |
| 259 */ | |
| 260 importer.DefaultMediaScanner.prototype.onFileEntryFound_ = | |
| 261 function(scan, entry) { | |
| 262 | |
| 263 if (!FileType.isImageOrVideo(entry)) { | |
| 264 return Promise.resolve(false); | |
|
Ben Kwa
2015/02/05 14:54:26
I think this should just be Promise.resolve(). Th
Steve McKay
2015/02/05 15:46:46
Done.
| |
| 265 } | |
| 266 return this.hasHistoryDuplicate_(entry).then( | |
|
Ben Kwa
2015/02/05 14:54:26
nit: line break before .then
Steve McKay
2015/02/05 15:46:46
Done.
| |
| 267 /** | |
| 268 * @param {boolean} duplicate | |
| 269 * @return {!Promise} | |
| 270 * @this {importer.DefaultMediaScanner} | |
| 271 */ | |
| 272 function(duplicate) { | |
| 273 if (duplicate) { | |
| 274 return false; | |
| 220 } else { | 275 } else { |
| 221 promises.push( | 276 return this.createHashcode_(entry) |
| 222 result.onFileEntryFound(/** @type {!FileEntry} */(entry))); | 277 .then( |
| 278 function(hashcode) { | |
| 279 return scan.addFileEntry(entry, hashcode); | |
| 280 }); | |
| 223 } | 281 } |
| 224 }) | 282 }.bind(this)).then( |
|
Ben Kwa
2015/02/05 14:54:26
nit: line break before .then
Steve McKay
2015/02/05 15:46:46
Done.
| |
| 225 .then(Promise.all.bind(Promise, promises)); | 283 /** |
| 284 * @param {boolean} added | |
| 285 * @this {importer.DefaultMediaScanner} | |
| 286 */ | |
| 287 function(added) { | |
| 288 if (added) { | |
| 289 this.notify_(importer.ScanEvent.UPDATED, scan); | |
| 290 } | |
| 291 }.bind(this)); | |
| 292 }; | |
| 293 | |
| 294 /** | |
| 295 * @param {!FileEntry} entry | |
| 296 * @return {!Promise.<boolean>} True if there is a history-entry-duplicate | |
| 297 * for the file. | |
| 298 * @private | |
| 299 */ | |
| 300 importer.DefaultMediaScanner.prototype.hasHistoryDuplicate_ = function(entry) { | |
| 301 return this.historyLoader_.getHistory() | |
| 302 .then( | |
| 303 /** | |
| 304 * @param {!importer.ImportHistory} history | |
| 305 * @return {!Promise} | |
| 306 * @this {importer.DefaultMediaScanner} | |
| 307 */ | |
| 308 function(history) { | |
| 309 return Promise.all([ | |
| 310 history.wasCopied(entry, importer.Destination.GOOGLE_DRIVE), | |
| 311 history.wasImported(entry, importer.Destination.GOOGLE_DRIVE) | |
| 312 ]).then( | |
| 313 /** | |
| 314 * @param {!Array.<boolean>} results | |
| 315 * @return {!Promise} | |
| 316 * @this {importer.DefaultMediaScanner} | |
| 317 */ | |
| 318 function(results) { | |
| 319 return results[0] || results[1]; | |
| 320 }.bind(this)); | |
| 321 }.bind(this)); | |
| 226 }; | 322 }; |
| 227 | 323 |
| 228 /** | 324 /** |
| 229 * Results of a scan operation. The object is "live" in that data can and | 325 * Results of a scan operation. The object is "live" in that data can and |
| 230 * will change as the scan operation discovers files. | 326 * will change as the scan operation discovers files. |
| 231 * | 327 * |
| 232 * <p>The scan is complete, and the object will become static once the | 328 * <p>The scan is complete, and the object will become static once the |
| 233 * {@code whenFinal} promise resolves. | 329 * {@code whenFinal} promise resolves. |
| 234 * | 330 * |
| 235 * @constructor | 331 * @constructor |
| 236 * @struct | 332 * @struct |
| 237 * @implements {importer.ScanResult} | 333 * @implements {importer.ScanResult} |
| 238 * | |
| 239 * @param {function(!FileEntry): !Promise.<string>} hashGenerator | |
| 240 * @param {!importer.HistoryLoader} historyLoader | |
| 241 */ | 334 */ |
| 242 importer.DefaultScanResult = function(hashGenerator, historyLoader) { | 335 importer.DefaultScanResult = function() { |
| 243 | |
| 244 /** @private {function(!FileEntry): !Promise.<string>} */ | |
| 245 this.createHashcode_ = hashGenerator; | |
| 246 | |
| 247 /** @private {!importer.HistoryLoader} */ | |
| 248 this.historyLoader_ = historyLoader; | |
| 249 | 336 |
| 250 /** | 337 /** |
| 251 * List of file entries found while scanning. | 338 * List of file entries found while scanning. |
| 252 * @private {!Array.<!FileEntry>} | 339 * @private {!Array.<!FileEntry>} |
| 253 */ | 340 */ |
| 254 this.fileEntries_ = []; | 341 this.fileEntries_ = []; |
| 255 | 342 |
| 256 /** | 343 /** |
| 257 * @private {boolean} | |
| 258 */ | |
| 259 this.invalidated_ = false; | |
| 260 | |
| 261 /** | |
| 262 * Hashcodes of all files included captured by this result object so-far. | 344 * Hashcodes of all files included captured by this result object so-far. |
| 263 * Used to dedupe newly discovered files against other files withing | 345 * Used to dedupe newly discovered files against other files withing |
| 264 * the ScanResult. | 346 * the ScanResult. |
| 265 * @private {!Object.<string, !FileEntry>} | 347 * @private {!Object.<string, !FileEntry>} |
| 266 */ | 348 */ |
| 267 this.fileHashcodes_ = {}; | 349 this.fileHashcodes_ = {}; |
| 268 | 350 |
| 269 /** @private {number} */ | 351 /** @private {number} */ |
| 270 this.totalBytes_ = 0; | 352 this.totalBytes_ = 0; |
| 271 | 353 |
| 272 /** | 354 /** |
| 273 * The point in time when the scan was started. | 355 * The point in time when the scan was started. |
| 274 * @type {Date} | 356 * @type {Date} |
| 275 */ | 357 */ |
| 276 this.scanStarted_ = new Date(); | 358 this.scanStarted_ = new Date(); |
| 277 | 359 |
| 278 /** | 360 /** |
| 279 * The point in time when the last scan activity occured. | 361 * The point in time when the last scan activity occured. |
| 280 * @type {Date} | 362 * @type {Date} |
| 281 */ | 363 */ |
| 282 this.lastScanActivity_ = this.scanStarted_; | 364 this.lastScanActivity_ = this.scanStarted_; |
| 283 | 365 |
| 366 /** | |
| 367 * @private {boolean} | |
| 368 */ | |
| 369 this.invalidated_ = false; | |
| 370 | |
| 284 /** @private {!importer.Resolver.<!importer.ScanResult>} */ | 371 /** @private {!importer.Resolver.<!importer.ScanResult>} */ |
| 285 this.resolver_ = new importer.Resolver(); | 372 this.resolver_ = new importer.Resolver(); |
| 286 }; | 373 }; |
| 287 | 374 |
| 288 /** @struct */ | 375 /** @struct */ |
| 289 importer.DefaultScanResult.prototype = { | 376 importer.DefaultScanResult.prototype = { |
| 290 | 377 |
| 291 /** @return {function()} */ | 378 /** @return {function()} */ |
| 292 get resolve() { return this.resolver_.resolve.bind(null, this); }, | 379 get resolve() { return this.resolver_.resolve.bind(null, this); }, |
| 293 | 380 |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 325 }; | 412 }; |
| 326 | 413 |
| 327 /** | 414 /** |
| 328 * Invalidates this scan. | 415 * Invalidates this scan. |
| 329 */ | 416 */ |
| 330 importer.DefaultScanResult.prototype.invalidateScan = function() { | 417 importer.DefaultScanResult.prototype.invalidateScan = function() { |
| 331 this.invalidated_ = true; | 418 this.invalidated_ = true; |
| 332 }; | 419 }; |
| 333 | 420 |
| 334 /** | 421 /** |
| 335 * Handles files discovered during scanning. | |
| 336 * | |
| 337 * @param {!FileEntry} entry | |
| 338 * @return {!Promise} Resolves once file entry has been processed | |
| 339 * and is represented in results. | |
| 340 */ | |
| 341 importer.DefaultScanResult.prototype.onFileEntryFound = function(entry) { | |
| 342 this.lastScanActivity_ = new Date(); | |
| 343 | |
| 344 if (!FileType.isImageOrVideo(entry)) { | |
| 345 return Promise.resolve(); | |
| 346 } | |
| 347 | |
| 348 return this.historyLoader_.getHistory() | |
| 349 .then( | |
| 350 /** | |
| 351 * @param {!importer.ImportHistory} history | |
| 352 * @return {!Promise} | |
| 353 * @this {importer.DefaultScanResult} | |
| 354 */ | |
| 355 function(history) { | |
| 356 return Promise.all([ | |
| 357 history.wasCopied(entry, importer.Destination.GOOGLE_DRIVE), | |
| 358 history.wasImported(entry, importer.Destination.GOOGLE_DRIVE) | |
| 359 ]).then( | |
| 360 /** | |
| 361 * @param {!Array.<boolean>} results | |
| 362 * @return {!Promise} | |
| 363 * @this {importer.DefaultScanResult} | |
| 364 */ | |
| 365 function(results) { | |
| 366 return results[0] || results[1] ? | |
| 367 Promise.resolve() : | |
| 368 this.addFileEntry_(entry); | |
| 369 }.bind(this)); | |
| 370 }.bind(this)); | |
| 371 }; | |
| 372 | |
| 373 /** | |
| 374 * Adds a file to results. | 422 * Adds a file to results. |
| 375 * | 423 * |
| 376 * @param {!FileEntry} entry | 424 * @param {!FileEntry} entry |
| 377 * @return {!Promise} Resolves once file entry has been processed | 425 * @param {string} hashcode |
| 378 * and is represented in results. | 426 * @return {!Promise.<boolean>} True if the file as added, false if it was |
| 379 * @private | 427 * rejected as a dupe. |
| 380 */ | 428 */ |
| 381 importer.DefaultScanResult.prototype.addFileEntry_ = function(entry) { | 429 importer.DefaultScanResult.prototype.addFileEntry = function(entry, hashcode) { |
| 382 return new Promise( | 430 return new Promise(entry.getMetadata.bind(entry)).then( |
| 383 function(resolve, reject) { | 431 /** |
| 384 this.createHashcode_(entry).then( | 432 * @param {!Metadata} metadata |
| 385 /** | 433 * @this {importer.DefaultScanResult} |
| 386 * @param {string} hashcode | 434 */ |
| 387 * @this {importer.DefaultScanResult} | 435 function(metadata) { |
| 388 */ | 436 console.assert( |
| 389 function(hashcode) { | 437 'size' in metadata, |
| 390 // Ignore the entry if it is a duplicate. | 438 'size attribute missing from metadata.'); |
| 391 if (hashcode in this.fileHashcodes_) { | 439 this.lastScanActivity_ = new Date(); |
| 392 resolve(); | |
| 393 return; | |
| 394 } | |
| 395 | 440 |
| 396 entry.getMetadata( | 441 // We wait to check the hashcode until after all |
|
Ben Kwa
2015/02/05 14:54:26
I find this comment confusing. Which async data a
Steve McKay
2015/02/05 15:46:46
Rewrote to make the thought clearer.
| |
| 397 /** | 442 // async data is loaded. This avoids a possible race. |
| 398 * @param {!Metadata} metadata | 443 if (hashcode in this.fileHashcodes_) { |
| 399 * @this {importer.DefaultScanResult} | 444 return false; |
| 400 */ | 445 } |
| 401 function(metadata) { | |
| 402 console.assert( | |
| 403 'size' in metadata, | |
| 404 'size attribute missing from metadata.'); | |
| 405 this.lastScanActivity_ = new Date(); | |
| 406 | 446 |
| 407 // Double check that a dupe entry wasn't added while we were | 447 entry.size = metadata.size; |
| 408 // busy looking up metadata. | 448 this.totalBytes_ += metadata['size']; |
| 409 if (hashcode in this.fileHashcodes_) { | 449 this.fileHashcodes_[hashcode] = entry; |
| 410 resolve(); | 450 this.fileEntries_.push(entry); |
| 411 return; | 451 return true; |
| 412 } | 452 |
| 413 entry.size = metadata.size; | 453 }.bind(this)); |
| 414 this.totalBytes_ += metadata['size']; | |
| 415 this.fileHashcodes_[hashcode] = entry; | |
| 416 this.fileEntries_.push(entry); | |
| 417 resolve(); | |
| 418 }.bind(this)); | |
| 419 }.bind(this)); | |
| 420 }.bind(this)); | |
| 421 }; | 454 }; |
| 422 | 455 |
| 423 /** | 456 /** |
| 424 * Watcher for directories. | 457 * Watcher for directories. |
| 425 * @interface | 458 * @interface |
| 426 */ | 459 */ |
| 427 importer.DirectoryWatcher = function() {}; | 460 importer.DirectoryWatcher = function() {}; |
| 428 | 461 |
| 429 /** | 462 /** |
| 430 * Registers new directory to be watched. | 463 * Registers new directory to be watched. |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 490 if (!this.watchedDirectories_[event.entry.toURL()]) | 523 if (!this.watchedDirectories_[event.entry.toURL()]) |
| 491 return; | 524 return; |
| 492 this.triggered = true; | 525 this.triggered = true; |
| 493 for (var url in this.watchedDirectories_) { | 526 for (var url in this.watchedDirectories_) { |
| 494 chrome.fileManagerPrivate.removeFileWatch(url, function() {}); | 527 chrome.fileManagerPrivate.removeFileWatch(url, function() {}); |
| 495 } | 528 } |
| 496 chrome.fileManagerPrivate.onDirectoryChanged.removeListener( | 529 chrome.fileManagerPrivate.onDirectoryChanged.removeListener( |
| 497 assert(this.listener_)); | 530 assert(this.listener_)); |
| 498 this.callback_(); | 531 this.callback_(); |
| 499 }; | 532 }; |
| OLD | NEW |