Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(68)

Side by Side Diff: chrome/browser/resources/file_manager/js/file_manager.js

Issue 10310163: Refactoring file manager: moving volume mounting related code to a separate class. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fixed gallery opening on GDATA at startup. Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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 * FileManager constructor. 6 * FileManager constructor.
7 * 7 *
8 * FileManager objects encapsulate the functionality of the file selector 8 * FileManager objects encapsulate the functionality of the file selector
9 * dialogs, as well as the full screen file manager application (though the 9 * dialogs, as well as the full screen file manager application (though the
10 * latter is not yet implemented). 10 * latter is not yet implemented).
11 * 11 *
12 * @constructor 12 * @constructor
13 * @param {HTMLElement} dialogDom The DOM node containing the prototypical 13 * @param {HTMLElement} dialogDom The DOM node containing the prototypical
14 * dialog UI. 14 * dialog UI.
15 */ 15 */
16 function FileManager(dialogDom) { 16 function FileManager(dialogDom) {
17 this.dialogDom_ = dialogDom; 17 this.dialogDom_ = dialogDom;
18 this.filesystem_ = null; 18 this.filesystem_ = null;
19 this.params_ = location.search ? 19 this.params_ = location.search ?
20 JSON.parse(decodeURIComponent(location.search.substr(1))) : 20 JSON.parse(decodeURIComponent(location.search.substr(1))) :
21 {}; 21 {};
22 22
23 this.listType_ = null; 23 this.listType_ = null;
24 this.showDelayTimeout_ = null;
24 25
25 this.selection = null; 26 this.selection = null;
26 27
27 this.butterTimer_ = null; 28 this.butterTimer_ = null;
28 this.currentButter_ = null; 29 this.currentButter_ = null;
29 this.butterLastShowTime_ = 0; 30 this.butterLastShowTime_ = 0;
30 31
31 this.watchedDirectoryUrl_ = null;
32 this.filesystemObserverId_ = null; 32 this.filesystemObserverId_ = null;
33 this.gdataObserverId_ = null; 33 this.gdataObserverId_ = null;
34 34
35 this.commands_ = {}; 35 this.commands_ = {};
36 36
37 this.thumbnailUrlCache_ = {}; 37 this.thumbnailUrlCache_ = {};
38 38
39 this.document_ = dialogDom.ownerDocument; 39 this.document_ = dialogDom.ownerDocument;
40 this.dialogType_ = this.params_.type || FileManager.DialogType.FULL_PAGE; 40 this.dialogType_ = this.params_.type || FileManager.DialogType.FULL_PAGE;
41 41
42 metrics.recordEnum('Create', this.dialogType_, 42 metrics.recordEnum('Create', this.dialogType_,
43 [FileManager.DialogType.SELECT_FOLDER, 43 [FileManager.DialogType.SELECT_FOLDER,
44 FileManager.DialogType.SELECT_SAVEAS_FILE, 44 FileManager.DialogType.SELECT_SAVEAS_FILE,
45 FileManager.DialogType.SELECT_OPEN_FILE, 45 FileManager.DialogType.SELECT_OPEN_FILE,
46 FileManager.DialogType.SELECT_OPEN_MULTI_FILE, 46 FileManager.DialogType.SELECT_OPEN_MULTI_FILE,
47 FileManager.DialogType.FULL_PAGE]); 47 FileManager.DialogType.FULL_PAGE]);
48 48
49 // TODO(dgozman): This will be changed to LocaleInfo. 49 // TODO(dgozman): This will be changed to LocaleInfo.
50 this.locale_ = new v8Locale(navigator.language); 50 this.locale_ = new v8Locale(navigator.language);
51 51
52 this.initFileSystem_(); 52 this.initFileSystem_();
53 this.volumeManager_ = VolumeManager.getInstance();
53 this.initDom_(); 54 this.initDom_();
54 this.initDialogType_(); 55 this.initDialogType_();
55 this.dialogDom_.style.opacity = '1';
56 } 56 }
57 57
58 FileManager.prototype = { 58 FileManager.prototype = {
59 __proto__: cr.EventTarget.prototype 59 __proto__: cr.EventTarget.prototype
60 }; 60 };
61 61
62 // Anonymous "namespace". 62 // Anonymous "namespace".
63 (function() { 63 (function() {
64 64
65 // Private variables and helper functions. 65 // Private variables and helper functions.
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after
201 201
202 if (parent_path[parent_path.length - 1] != '/') 202 if (parent_path[parent_path.length - 1] != '/')
203 parent_path += '/'; 203 parent_path += '/';
204 204
205 if (child_path[child_path.length - 1] != '/') 205 if (child_path[child_path.length - 1] != '/')
206 child_path += '/'; 206 child_path += '/';
207 207
208 return child_path.indexOf(parent_path) == 0; 208 return child_path.indexOf(parent_path) == 0;
209 } 209 }
210 210
211 /**
212 * Normalizes path not to start with /
213 *
214 * @param {string} path The file path.
215 */
216 function normalizeAbsolutePath(x) {
217 if (x[0] == '/')
218 return x.slice(1);
219 else
220 return x;
221 }
222
223 function removeChildren(element) { 211 function removeChildren(element) {
224 element.textContent = ''; 212 element.textContent = '';
225 } 213 }
226 214
227 // Public statics. 215 // Public statics.
228 216
229 /** 217 /**
230 * List of dialog types. 218 * List of dialog types.
231 * 219 *
232 * Keep this in sync with FileManagerDialog::GetDialogTypeAsString, except 220 * Keep this in sync with FileManagerDialog::GetDialogTypeAsString, except
(...skipping 15 matching lines...) Expand all
248 type == FileManager.DialogType.SELECT_OPEN_FILE || 236 type == FileManager.DialogType.SELECT_OPEN_FILE ||
249 type == FileManager.DialogType.SELECT_OPEN_MULTI_FILE; 237 type == FileManager.DialogType.SELECT_OPEN_MULTI_FILE;
250 }; 238 };
251 239
252 FileManager.ListType = { 240 FileManager.ListType = {
253 DETAIL: 'detail', 241 DETAIL: 'detail',
254 THUMBNAIL: 'thumb' 242 THUMBNAIL: 'thumb'
255 }; 243 };
256 244
257 /** 245 /**
246 * FileWatcher that also watches for metadata changes.
247 * @extends {FileWatcher}
248 */
249 FileManager.MetadataFileWatcher = function(fileManager) {
250 FileWatcher.call(this,
251 fileManager.filesystem_.root,
252 fileManager.directoryModel_,
253 fileManager.volumeManager_);
254 this.metadataCache_ = fileManager.metadataCache_;
255
256 this.filesystemChanngeHandler_ =
257 fileManager.updateMetadataInUI_.bind(fileManager, 'filesystem');
258 this.gdataChanngeHandler_ =
259 fileManager.updateMetadataInUI_.bind(fileManager, 'gdata');
260
261 this.filesystemObserverId_ = null;
262 this.gdataObserverId_ = null;
263 };
264
265 FileManager.MetadataFileWatcher.prototype.__proto__ = FileWatcher.prototype;
266
267 /**
268 * Changed metadata observers for the new directory.
269 * @override
270 * @param {DirectoryEntryi?} entry New watched directory entry.
271 */
272 FileManager.MetadataFileWatcher.prototype.changeWatchedEntry =
273 function(entry) {
274 FileWatcher.prototype.changeWatchedEntry.call(this, entry);
275
276 if (this.filesystemObserverId_)
277 this.metadataCache_.removeObserver(this.filesystemObserverId_);
278 if (this.gdataObserverId_)
279 this.metadataCache_.removeObserver(this.gdataObserverId_);
280 this.filesystemObserverId_ = null;
281 this.gdataObserverId_ = null;
282 if (!entry)
283 return;
284
285 this.filesystemObserverId_ = this.metadataCache_.addObserver(
286 entry,
287 MetadataCache.CHILDREN,
288 'filesystem',
289 this.filesystemChanngeHandler_);
290
291 if (DirectoryModel.getRootType(entry.fullPath) ==
292 DirectoryModel.RootType.GDATA) {
293 this.gdataObserverId_ = this.metadataCache_.addObserver(
294 entry,
295 MetadataCache.CHILDREN,
296 'gdata',
297 this.gdataChanngeHandler_);
298 }
299 };
300
301 /**
258 * Load translated strings. 302 * Load translated strings.
259 */ 303 */
260 FileManager.initStrings = function(callback) { 304 FileManager.initStrings = function(callback) {
261 chrome.fileBrowserPrivate.getStrings(function(strings) { 305 chrome.fileBrowserPrivate.getStrings(function(strings) {
262 localStrings = new LocalStrings(strings); 306 localStrings = new LocalStrings(strings);
263 if (callback) 307 if (callback)
264 callback(); 308 callback();
265 }); 309 });
266 }; 310 };
267 311
312 /**
313 * FileManager initially created hidden to prevent flickering.
314 * When DOM is almost constructed it need to be shown. Cancels
315 * delayed show.
316 */
317 FileManager.prototype.show_ = function() {
318 if (this.showDelayTimeout_) {
319 clearTimeout(this.showDelayTimeout_);
320 showDelayTimeout_ = null;
321 }
322 this.dialogDom_.classList.add('loaded');
323 };
324
325 /**
326 * If initialization code think that right after initialization
327 * something going to be shown instead of just a file list (like Gallery)
328 * it may delay show to prevent flickering. However initialization may take
329 * significant time and we don't want to keep it hidden for too long.
330 * So it will be shown not more than in 0.5 sec. If initialization completed
331 * the page must show immediatelly.
332 *
333 * @param {number} delay In milliseconds.
334 */
335 FileManager.prototype.delayShow_ = function(delay) {
336 if (!this.showDelayTimeout_) {
337 this.showDelayTimeout_ = setTimeout(function() {
338 this.showDelayTimeout_ = null;
339 this.show_();
340 }.bind(this), delay);
341 }
342 };
343
268 // Instance methods. 344 // Instance methods.
269 345
270 /** 346 /**
271 * Request local file system, resolve roots and init_ after that. 347 * Request local file system, resolve roots and init_ after that.
272 * @private 348 * @private
273 */ 349 */
274 FileManager.prototype.initFileSystem_ = function() { 350 FileManager.prototype.initFileSystem_ = function() {
275 util.installFileErrorToString(); 351 util.installFileErrorToString();
276 // Replace the default unit in util to translated unit. 352 // Replace the default unit in util to translated unit.
277 util.UNITS = [str('SIZE_KB'), 353 util.UNITS = [str('SIZE_KB'),
278 str('SIZE_MB'), 354 str('SIZE_MB'),
279 str('SIZE_GB'), 355 str('SIZE_GB'),
280 str('SIZE_TB'), 356 str('SIZE_TB'),
281 str('SIZE_PB')]; 357 str('SIZE_PB')];
282 358
283 metrics.startInterval('Load.FileSystem'); 359 metrics.startInterval('Load.FileSystem');
284 360
285 var self = this; 361 var self = this;
286 362 var downcount = 2;
287 // The list of active mount points to distinct them from other directories. 363 function done() {
288 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { 364 if (--downcount == 0)
289 self.setMountPoints_(mountPoints);
290 onDone();
291 });
292
293 function onDone() {
294 if (self.mountPoints_ && self.filesystem_)
295 self.init_(); 365 self.init_();
296 } 366 }
297 367
298 chrome.fileBrowserPrivate.requestLocalFileSystem(function(filesystem) { 368 chrome.fileBrowserPrivate.requestLocalFileSystem(function(filesystem) {
299 metrics.recordInterval('Load.FileSystem'); 369 metrics.recordInterval('Load.FileSystem');
300 self.filesystem_ = filesystem; 370 self.filesystem_ = filesystem;
301 onDone(); 371 done();
372 });
373
374 // GDATA preferences should be initialized before creating DirectoryModel
375 // to tot rebuild the roots list.
376 this.updateNetworkStateAndGDataPreferences_(function() {
377 done();
302 }); 378 });
303 }; 379 };
304 380
305 FileManager.prototype.setMountPoints_ = function(mountPoints) {
306 this.mountPoints_ = mountPoints;
307 // Add gdata mount info if present.
308 if (this.gdataMounted_)
309 this.mountPoints_.push(this.gdataMountInfo_);
310 };
311
312 /** 381 /**
313 * Continue initializing the file manager after resolving roots. 382 * Continue initializing the file manager after resolving roots.
314 */ 383 */
315 FileManager.prototype.init_ = function() { 384 FileManager.prototype.init_ = function() {
316 metrics.startInterval('Load.DOM'); 385 metrics.startInterval('Load.DOM');
317 this.initCommands_(); 386 this.initCommands_();
318 387
319 this.metadataCache_ = MetadataCache.createFull(); 388 this.metadataCache_ = MetadataCache.createFull();
320 389
321 this.shortDateFormatter_ = 390 this.shortDateFormatter_ =
(...skipping 11 matching lines...) Expand all
333 402
334 this.table_.startBatchUpdates(); 403 this.table_.startBatchUpdates();
335 this.grid_.startBatchUpdates(); 404 this.grid_.startBatchUpdates();
336 405
337 this.initFileList_(); 406 this.initFileList_();
338 this.initDialogs_(); 407 this.initDialogs_();
339 408
340 window.addEventListener('popstate', this.onPopState_.bind(this)); 409 window.addEventListener('popstate', this.onPopState_.bind(this));
341 window.addEventListener('unload', this.onUnload_.bind(this)); 410 window.addEventListener('unload', this.onUnload_.bind(this));
342 411
343 this.directoryModel_.addEventListener('directory-changed', 412 var dm = this.directoryModel_;
344 this.onDirectoryChanged_.bind(this)); 413 dm.addEventListener('directory-changed',
414 this.onDirectoryChanged_.bind(this));
345 var self = this; 415 var self = this;
346 this.directoryModel_.addEventListener('begin-update-files', function() { 416 dm.addEventListener('begin-update-files', function() {
347 self.currentList_.startBatchUpdates(); 417 self.currentList_.startBatchUpdates();
348 }); 418 });
349 this.directoryModel_.addEventListener('end-update-files', function() { 419 dm.addEventListener('end-update-files', function() {
350 self.restoreItemBeingRenamed_(); 420 self.restoreItemBeingRenamed_();
351 self.currentList_.endBatchUpdates(); 421 self.currentList_.endBatchUpdates();
352 }); 422 });
353 this.directoryModel_.addEventListener('scan-started', 423 dm.addEventListener('scan-started', this.showSpinnerLater_.bind(this));
354 this.showSpinnerLater_.bind(this)); 424 dm.addEventListener('scan-completed', this.showSpinner_.bind(this, false));
355 this.directoryModel_.addEventListener('scan-completed', 425 dm.addEventListener('scan-completed',
356 this.showSpinner_.bind(this, false)); 426 this.refreshCurrentDirectoryMetadata_.bind(this));
357 this.directoryModel_.addEventListener('scan-completed', 427 dm.addEventListener('rescan-completed',
358 this.refreshCurrentDirectoryMetadata_.bind(this)); 428 this.refreshCurrentDirectoryMetadata_.bind(this));
359 this.directoryModel_.addEventListener('rescan-completed',
360 this.refreshCurrentDirectoryMetadata_.bind(this));
361 this.addEventListener('selection-summarized', 429 this.addEventListener('selection-summarized',
362 this.onSelectionSummarized_.bind(this)); 430 this.onSelectionSummarized_.bind(this));
363 431
364 // The list of archives requested to mount. We will show contents once 432 this.setupCurrentDirectory_(true /* page loading */);
365 // archive is mounted, but only for mounts from within this filebrowser tab.
366 this.mountRequests_ = [];
367 this.unmountRequests_ = [];
368 chrome.fileBrowserPrivate.onMountCompleted.addListener(
369 this.onMountCompleted_.bind(this));
370 433
371 chrome.fileBrowserPrivate.onFileChanged.addListener( 434 var stateChangeHandler =
372 this.onFileChanged_.bind(this)); 435 this.onNetworkStateOrGDataPreferencesChanged_.bind(this);
373 436 chrome.fileBrowserPrivate.onGDataPreferencesChanged.addListener(
374 var queryGDataPreferences = function() { 437 stateChangeHandler);
375 chrome.fileBrowserPrivate.getGDataPreferences( 438 chrome.fileBrowserPrivate.onNetworkConnectionChanged.addListener(
376 this.onGDataPreferencesChanged_.bind(this)); 439 stateChangeHandler);
377 }.bind(this); 440 stateChangeHandler();
378 queryGDataPreferences();
379 chrome.fileBrowserPrivate.onGDataPreferencesChanged.
380 addListener(queryGDataPreferences);
381
382 var queryNetworkConnectionState = function() {
383 chrome.fileBrowserPrivate.getNetworkConnectionState(
384 this.onNetworkConnectionChanged_.bind(this));
385 }.bind(this);
386 queryNetworkConnectionState();
387 chrome.fileBrowserPrivate.onNetworkConnectionChanged.
388 addListener(queryNetworkConnectionState);
389
390 var invokeHandler = !this.params_.selectOnly;
391 if (this.isStartingOnGData_()) {
392 // We are opening on a GData path. Mount GData and show
393 // "Loading Google Docs" message until the directory content loads.
394 this.dialogContainer_.setAttribute('unmounted', true);
395 this.initGData_(true /* dirChanged */);
396 // This is a one-time handler (will be nulled out on the first call).
397 this.setupCurrentDirectoryPostponed_ = function(event) {
398 this.directoryModel_.removeEventListener('directory-changed',
399 this.setupCurrentDirectoryPostponed_);
400 this.setupCurrentDirectoryPostponed_ = null;
401 if (event) // If called as an event handler just exit silently.
402 return;
403 this.setupCurrentDirectory_(
404 invokeHandler, false /* blankWhileOpeningAFile */);
405 }.bind(this);
406 this.directoryModel_.addEventListener('directory-changed',
407 this.setupCurrentDirectoryPostponed_);
408 } else {
409 this.setupCurrentDirectory_(
410 invokeHandler, true /* blankWhileOpeningAFile */);
411 }
412
413 if (this.isGDataEnabled())
414 this.setupGDataWelcome_();
415 441
416 this.summarizeSelection_(); 442 this.summarizeSelection_();
417 443
418 var sortField = 444 var sortField =
419 window.localStorage['sort-field-' + this.dialogType_] || 445 window.localStorage['sort-field-' + this.dialogType_] ||
420 'modificationTime'; 446 'modificationTime';
421 var sortDirection = 447 var sortDirection =
422 window.localStorage['sort-direction-' + this.dialogType_] || 'desc'; 448 window.localStorage['sort-direction-' + this.dialogType_] || 'desc';
423 this.directoryModel_.sortFileList(sortField, sortDirection); 449 this.directoryModel_.sortFileList(sortField, sortDirection);
424 450
425 this.refocus(); 451 this.refocus();
426 452
427 this.metadataProvider_ = 453 this.metadataProvider_ =
428 new MetadataProvider(this.filesystem_.root.toURL()); 454 new MetadataProvider(this.filesystem_.root.toURL());
429 455
430 // PyAuto tests monitor this state by polling this variable 456 // PyAuto tests monitor this state by polling this variable
431 this.__defineGetter__('workerInitialized_', function() { 457 this.__defineGetter__('workerInitialized_', function() {
432 return self.getMetadataProvider().isInitialized(); 458 return self.getMetadataProvider().isInitialized();
433 }); 459 });
434 460
435 if (this.dialogType_ == FileManager.DialogType.FULL_PAGE) 461 if (this.dialogType_ == FileManager.DialogType.FULL_PAGE)
436 this.initDataTransferOperations_(); 462 this.initDataTransferOperations_();
437 463
438 this.table_.endBatchUpdates(); 464 this.table_.endBatchUpdates();
439 this.grid_.endBatchUpdates(); 465 this.grid_.endBatchUpdates();
440 466
467 // Show the page now unless it's already delayed.
468 this.delayShow_(0);
469
441 metrics.recordInterval('Load.DOM'); 470 metrics.recordInterval('Load.DOM');
442 metrics.recordInterval('Load.Total'); 471 metrics.recordInterval('Load.Total');
443 }; 472 };
444 473
445 FileManager.prototype.initDataTransferOperations_ = function() { 474 FileManager.prototype.initDataTransferOperations_ = function() {
446 this.copyManager_ = new FileCopyManager(this.filesystem_.root); 475 this.copyManager_ = new FileCopyManager(this.filesystem_.root);
447 this.copyManager_.addEventListener('copy-progress', 476 this.copyManager_.addEventListener('copy-progress',
448 this.onCopyProgress_.bind(this)); 477 this.onCopyProgress_.bind(this));
449 this.copyManager_.addEventListener('copy-operation-complete', 478 this.copyManager_.addEventListener('copy-operation-complete',
450 this.onCopyManagerOperationComplete_.bind(this)); 479 this.onCopyManagerOperationComplete_.bind(this));
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
519 this.filenameInput_ = this.dialogDom_.querySelector('.filename-input'); 548 this.filenameInput_ = this.dialogDom_.querySelector('.filename-input');
520 this.taskItems_ = this.dialogDom_.querySelector('#tasks'); 549 this.taskItems_ = this.dialogDom_.querySelector('#tasks');
521 this.okButton_ = this.dialogDom_.querySelector('.ok'); 550 this.okButton_ = this.dialogDom_.querySelector('.ok');
522 this.cancelButton_ = this.dialogDom_.querySelector('.cancel'); 551 this.cancelButton_ = this.dialogDom_.querySelector('.cancel');
523 this.deleteButton_ = this.dialogDom_.querySelector('#delete-button'); 552 this.deleteButton_ = this.dialogDom_.querySelector('#delete-button');
524 this.table_ = this.dialogDom_.querySelector('.detail-table'); 553 this.table_ = this.dialogDom_.querySelector('.detail-table');
525 this.grid_ = this.dialogDom_.querySelector('.thumbnail-grid'); 554 this.grid_ = this.dialogDom_.querySelector('.thumbnail-grid');
526 this.spinner_ = this.dialogDom_.querySelector('.spinner'); 555 this.spinner_ = this.dialogDom_.querySelector('.spinner');
527 this.showSpinner_(false); 556 this.showSpinner_(false);
528 this.butter_ = this.dialogDom_.querySelector('.butter-bar'); 557 this.butter_ = this.dialogDom_.querySelector('.butter-bar');
529 this.unmountedPanel_ = this.dialogDom_.querySelector('.unmounted-panel'); 558 this.unmountedPanel_ = this.dialogDom_.querySelector('#unmounted-panel');
530 559
531 cr.ui.decorate('#gdata-settings', cr.ui.MenuButton); 560 cr.ui.decorate('#gdata-settings', cr.ui.MenuButton);
532 cr.ui.Table.decorate(this.table_); 561 cr.ui.Table.decorate(this.table_);
533 cr.ui.Grid.decorate(this.grid_); 562 cr.ui.Grid.decorate(this.grid_);
534 563
535 this.document_.addEventListener('keydown', this.onKeyDown_.bind(this)); 564 this.document_.addEventListener('keydown', this.onKeyDown_.bind(this));
536 // Disable the default browser context menu. 565 // Disable the default browser context menu.
537 this.document_.addEventListener('contextmenu', 566 this.document_.addEventListener('contextmenu',
538 function(e) { e.preventDefault() }); 567 function(e) { e.preventDefault() });
539 568
(...skipping 29 matching lines...) Expand all
569 598
570 this.decorateSplitter( 599 this.decorateSplitter(
571 this.dialogDom_.querySelector('div.sidebar-splitter')); 600 this.dialogDom_.querySelector('div.sidebar-splitter'));
572 601
573 this.dialogContainer_ = this.dialogDom_.querySelector('.dialog-container'); 602 this.dialogContainer_ = this.dialogDom_.querySelector('.dialog-container');
574 this.dialogDom_.querySelector('#detail-view').addEventListener( 603 this.dialogDom_.querySelector('#detail-view').addEventListener(
575 'click', this.onDetailViewButtonClick_.bind(this)); 604 'click', this.onDetailViewButtonClick_.bind(this));
576 this.dialogDom_.querySelector('#thumbnail-view').addEventListener( 605 this.dialogDom_.querySelector('#thumbnail-view').addEventListener(
577 'click', this.onThumbnailViewButtonClick_.bind(this)); 606 'click', this.onThumbnailViewButtonClick_.bind(this));
578 607
579 // When we show the page for the first time we want to avoid
580 // the GDrive settings button animation, so we set the attribute ASAP.
581 if (this.isStartingOnGData_())
582 this.dialogContainer_.setAttribute('gdata', true);
583
584 this.syncButton = this.dialogDom_.querySelector('#gdata-sync-settings'); 608 this.syncButton = this.dialogDom_.querySelector('#gdata-sync-settings');
585 this.syncButton.addEventListener('click', this.onGDataPrefClick_.bind( 609 this.syncButton.addEventListener('click', this.onGDataPrefClick_.bind(
586 this, 'cellularDisabled', false /* not inverted */)); 610 this, 'cellularDisabled', false /* not inverted */));
587 611
588 this.hostedButton = this.dialogDom_.querySelector('#gdata-hosted-settings'); 612 this.hostedButton = this.dialogDom_.querySelector('#gdata-hosted-settings');
589 this.hostedButton.addEventListener('click', this.onGDataPrefClick_.bind( 613 this.hostedButton.addEventListener('click', this.onGDataPrefClick_.bind(
590 this, 'hostedFilesDisabled', true /* inverted */)); 614 this, 'hostedFilesDisabled', true /* inverted */));
591 615
592 cr.ui.ComboButton.decorate(this.taskItems_); 616 cr.ui.ComboButton.decorate(this.taskItems_);
593 this.taskItems_.addEventListener('select', 617 this.taskItems_.addEventListener('select',
(...skipping 25 matching lines...) Expand all
619 this.emptySelectionModel_ = new cr.ui.ListSelectionModel(); 643 this.emptySelectionModel_ = new cr.ui.ListSelectionModel();
620 644
621 var singleSelection = 645 var singleSelection =
622 this.dialogType_ == FileManager.DialogType.SELECT_OPEN_FILE || 646 this.dialogType_ == FileManager.DialogType.SELECT_OPEN_FILE ||
623 this.dialogType_ == FileManager.DialogType.SELECT_FOLDER || 647 this.dialogType_ == FileManager.DialogType.SELECT_FOLDER ||
624 this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE; 648 this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE;
625 649
626 this.directoryModel_ = new DirectoryModel( 650 this.directoryModel_ = new DirectoryModel(
627 this.filesystem_.root, 651 this.filesystem_.root,
628 singleSelection, 652 singleSelection,
629 this.metadataCache_); 653 this.metadataCache_,
654 this.volumeManager_,
655 this.isGDataEnabled());
656
657 this.directoryModel_.start();
658
659 this.fileWatcher_ = new FileManager.MetadataFileWatcher(this);
660 this.fileWatcher_.start();
630 661
631 var dataModel = this.directoryModel_.getFileList(); 662 var dataModel = this.directoryModel_.getFileList();
632 var collator = this.collator_; 663 var collator = this.collator_;
633 // TODO(dgozman): refactor comparison functions together with 664 // TODO(dgozman): refactor comparison functions together with
634 // render/update/display. 665 // render/update/display.
635 dataModel.setCompareFunction('name', function(a, b) { 666 dataModel.setCompareFunction('name', function(a, b) {
636 return collator.compare(a.name, b.name); 667 return collator.compare(a.name, b.name);
637 }); 668 });
638 dataModel.setCompareFunction('modificationTime', 669 dataModel.setCompareFunction('modificationTime',
639 this.compareMtime_.bind(this)); 670 this.compareMtime_.bind(this));
(...skipping 14 matching lines...) Expand all
654 this.initGrid_(); 685 this.initGrid_();
655 this.initRootsList_(); 686 this.initRootsList_();
656 687
657 var listType = FileManager.ListType.DETAIL; 688 var listType = FileManager.ListType.DETAIL;
658 if (FileManager.DialogType.isModal(this.dialogType_)) 689 if (FileManager.DialogType.isModal(this.dialogType_))
659 listType = window.localStorage['listType-' + this.dialogType_] || 690 listType = window.localStorage['listType-' + this.dialogType_] ||
660 FileManager.ListType.DETAIL; 691 FileManager.ListType.DETAIL;
661 this.setListType(listType); 692 this.setListType(listType);
662 693
663 this.textSearchState_ = {text: '', date: new Date()}; 694 this.textSearchState_ = {text: '', date: new Date()};
695
696 this.volumeManager_.addEventListener('gdata-status-changed',
697 this.updateGDataUnmountedPanel_.bind(this));
698 if (this.params_.mountTriggered) {
699 this.volumeManager_.addEventListener('unexpectedly-unmounted',
700 this.onUnexpectedlyUnmounted_.bind(this));
701 }
664 }; 702 };
665 703
666 FileManager.prototype.initRootsList_ = function() { 704 FileManager.prototype.initRootsList_ = function() {
667 this.rootsList_ = this.dialogDom_.querySelector('#roots-list'); 705 this.rootsList_ = this.dialogDom_.querySelector('#roots-list');
668 cr.ui.List.decorate(this.rootsList_); 706 cr.ui.List.decorate(this.rootsList_);
669 this.rootsList_.startBatchUpdates();
670 707
671 var self = this; 708 var self = this;
672 this.rootsList_.itemConstructor = function(entry) { 709 this.rootsList_.itemConstructor = function(entry) {
673 return self.renderRoot_(entry); 710 return self.renderRoot_(entry);
674 }; 711 };
675 712
676 this.rootsList_.selectionModel = 713 this.rootsList_.selectionModel =
677 this.directoryModel_.getRootsListSelectionModel(); 714 this.directoryModel_.getRootsListSelectionModel();
678 715
679 // TODO(dgozman): add "Add a drive" item. 716 // TODO(dgozman): add "Add a drive" item.
680 this.rootsList_.dataModel = this.directoryModel_.getRootsList(); 717 this.rootsList_.dataModel = this.directoryModel_.getRootsList();
681 this.directoryModel_.updateRoots(function() {
682 self.rootsList_.endBatchUpdates();
683 }, this.getGDataAccessMode_());
684 }; 718 };
685 719
686 /** 720 /**
687 * @param {boolean} dirChanged True if we just changed to GData directory, 721 * Shows the panel when current directory is GDATA and it's unmounted.
688 * False if "Retry" button clicked. 722 * Hides it otherwise. The pannel shows spinner if GDATA is mounting or
723 * an error message if it failed.
689 */ 724 */
690 FileManager.prototype.initGData_ = function(dirChanged) { 725 FileManager.prototype.updateGDataUnmountedPanel_ = function() {
691 this.initGDataUnmountedPanel_(); 726 var node = this.dialogContainer_;
692 727 if (this.isOnGData()) {
693 this.unmountedPanel_.removeAttribute('error'); 728 var status = this.volumeManager_.getGDataStatus();
694 if (dirChanged) { 729 if (status == VolumeManager.GDataStatus.MOUNTING ||
695 // When changing to GData directory we want to see a clear panel. 730 status == VolumeManager.GDataStatus.ERROR) {
696 this.unmountedPanel_.removeAttribute('retry'); 731 this.ensureGDataUnmountedPanelInitialized_();
697 if (this.gdataLoadingTimer_) { // Show immediately if already loading.
698 this.unmountedPanel_.setAttribute('loading', true);
699 } else {
700 this.unmountedPanel_.removeAttribute('loading');
701 setTimeout(function() {
702 if (this.gdataLoadingTimer_) { // Still loading.
703 this.unmountedPanel_.setAttribute('loading', true);
704 }
705 }.bind(this), 500);
706 } 732 }
733 if (status == VolumeManager.GDataStatus.ERROR)
734 this.unmountedPanel_.classList.add('retry-enabled');
735 node.setAttribute('gdata', status);
707 } else { 736 } else {
708 // When retrying we do not hide "Retry" and "Learn more". 737 node.removeAttribute('gdata');
709 this.unmountedPanel_.setAttribute('loading', true);
710 }
711
712 // If the user changed to another directory and then back to GData we
713 // re-enter this method while the timer is still active. In this case
714 // we only update the UI but do not request the mount again.
715 if (this.gdataLoadingTimer_)
716 return;
717
718 metrics.startInterval('Load.GData');
719 chrome.fileBrowserPrivate.addMount('', 'gdata', {},
720 function(sourcePath) {});
721
722 // This timer could fire before the mount succeeds. We will silently
723 // replace the error message with the correct directory contents.
724 this.gdataLoadingTimer_ = setTimeout(function() {
725 this.gdataLoadingTimer_ = null;
726 this.onGDataUnreachable_('GData load timeout');
727 }.bind(this),
728 15 * 60 * 1000);
729 };
730
731 FileManager.prototype.clearGDataLoadingTimer_ = function(message) {
732 if (this.gdataLoadingTimer_) {
733 clearTimeout(this.gdataLoadingTimer_);
734 this.gdataLoadingTimer_ = null;
735 } 738 }
736 }; 739 };
737 740
738 FileManager.prototype.onGDataUnreachable_ = function(message) { 741 /**
739 console.warn(message); 742 * Creates contents for the GDATA unmounted panel.
740 this.gdataMounted_ = false; 743 */
741 this.gdataMountInfo_ = null; 744 FileManager.prototype.ensureGDataUnmountedPanelInitialized_ = function() {
742 this.clearGDataLoadingTimer_(); 745 var panel = this.unmountedPanel_;
743 if (this.isOnGData()) { 746 if (panel.firstElementChild)
744 this.unmountedPanel_.removeAttribute('loading');
745 this.unmountedPanel_.setAttribute('error', true);
746 this.unmountedPanel_.setAttribute('retry', true);
747 }
748 };
749
750 FileManager.prototype.initGDataUnmountedPanel_ = function() {
751 if (this.unmountedPanel_.firstElementChild)
752 return; 747 return;
753 748
754 var loading = this.document_.createElement('div'); 749 function create(parent, tag, className, opt_textContent) {
755 loading.className = 'gdata loading'; 750 var div = panel.ownerDocument.createElement(tag);
756 loading.textContent = str('GDATA_LOADING'); 751 div.className = className;
757 this.unmountedPanel_.appendChild(loading); 752 div.textContent = opt_textContent || '';
753 parent.appendChild(div);
754 return div;
755 }
758 756
759 var spinnerBox = this.document_.createElement('div'); 757 var loading = create(panel, 'div', 'loading', str('GDATA_LOADING'));
760 spinnerBox.className = 'spinner-box'; 758 var spinnerBox = create(loading, 'div', 'spinner-box');
761 loading.appendChild(spinnerBox); 759 create(spinnerBox, 'div', 'spinner');
762 760 var progress = create(panel, 'div', 'progress');
763 var spinner = this.document_.createElement('div');
764 spinner.className = 'spinner';
765 spinnerBox.appendChild(spinner);
766
767 var progress = this.document_.createElement('div');
768 progress.className = 'gdata progress';
769 this.unmountedPanel_.appendChild(progress);
770
771 chrome.fileBrowserPrivate.onDocumentFeedFetched.addListener( 761 chrome.fileBrowserPrivate.onDocumentFeedFetched.addListener(
772 function(fileCount) { 762 function(fileCount) {
773 progress.textContent = strf('GDATA_LOADING_PROGRESS', fileCount); 763 progress.textContent = strf('GDATA_LOADING_PROGRESS', fileCount);
774 }); 764 });
775 765
776 var error = this.document_.createElement('div'); 766 create(panel, 'div', 'error',
777 error.className = 'gdata error'; 767 strf('GDATA_CANNOT_REACH', str('GDATA_PRODUCT_NAME')));
778 error.textContent = strf('GDATA_CANNOT_REACH', str('GDATA_PRODUCT_NAME'));
779 this.unmountedPanel_.appendChild(error);
780 768
781 var retry = this.document_.createElement('button'); 769 var retryButton = create(panel, 'button', 'retry', str('GDATA_RETRY'));
782 retry.className = 'gdata retry'; 770 retryButton.hidden = true;
783 retry.textContent = str('GDATA_RETRY'); 771 var vm = this.volumeManager_;
784 retry.onclick = this.initGData_.bind(this, false /* retry */); 772 retryButton.onclick = function() {
785 this.unmountedPanel_.appendChild(retry); 773 vm.mountGData(function() {}, function() {});
774 };
786 775
787 var learnMore = this.document_.createElement('div'); 776 var learnMore = create(panel, 'div', 'learn-more plain-link',
788 learnMore.className = 'gdata learn-more plain-link'; 777 str('GDATA_LEARN_MORE'));
789 learnMore.textContent = str('GDATA_LEARN_MORE'); 778 learnMore.onclick = this.onExternalLinkClick_.bind(this,
790 learnMore.addEventListener('click', 779 GOOGLE_DRIVE_ERROR_HELP_URL);
791 this.onExternalLinkClick_.bind(this, GOOGLE_DRIVE_ERROR_HELP_URL));
792 this.unmountedPanel_.appendChild(learnMore);
793 }; 780 };
794 781
795 FileManager.prototype.onDataModelSplice_ = function(event) { 782 FileManager.prototype.onDataModelSplice_ = function(event) {
796 var checkbox = this.document_.querySelector('#select-all-checkbox'); 783 var checkbox = this.document_.querySelector('#select-all-checkbox');
797 if (checkbox) 784 if (checkbox)
798 this.updateSelectAllCheckboxState_(checkbox); 785 this.updateSelectAllCheckboxState_(checkbox);
799 }; 786 };
800 787
801 FileManager.prototype.onDataModelPermuted_ = function(event) { 788 FileManager.prototype.onDataModelPermuted_ = function(event) {
802 var sortStatus = this.directoryModel_.getFileList().sortStatus; 789 var sortStatus = this.directoryModel_.getFileList().sortStatus;
(...skipping 478 matching lines...) Expand 10 before | Expand all | Expand 10 after
1281 } 1268 }
1282 } 1269 }
1283 }; 1270 };
1284 1271
1285 /** 1272 /**
1286 * Handler of file manager operations. Update directory model 1273 * Handler of file manager operations. Update directory model
1287 * to reflect operation result iimediatelly (not waiting directory 1274 * to reflect operation result iimediatelly (not waiting directory
1288 * update event). 1275 * update event).
1289 */ 1276 */
1290 FileManager.prototype.onCopyManagerOperationComplete_ = function(event) { 1277 FileManager.prototype.onCopyManagerOperationComplete_ = function(event) {
1291 var currentPath = 1278 var currentPath = this.directoryModel_.getCurrentDirPath();
1292 this.directoryModel_.getCurrentDirPath();
1293 if (this.isOnGData() && this.directoryModel_.isSearching()) 1279 if (this.isOnGData() && this.directoryModel_.isSearching())
1294 return; 1280 return;
1295 1281
1296 function inCurrentDirectory(entry) { 1282 function inCurrentDirectory(entry) {
1297 var fullPath = entry.fullPath; 1283 var fullPath = entry.fullPath;
1298 var dirPath = fullPath.substr(0, fullPath.length - 1284 var dirPath = fullPath.substr(0, fullPath.length -
1299 entry.name.length - 1); 1285 entry.name.length - 1);
1300 return dirPath == currentPath; 1286 return dirPath == currentPath;
1301 } 1287 }
1302 for (var i = 0; i < event.affectedEntries.length; i++) { 1288 for (var i = 0; i < event.affectedEntries.length; i++) {
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
1338 1324
1339 case 'delete': 1325 case 'delete':
1340 this.deleteEntries(this.selection.entries); 1326 this.deleteEntries(this.selection.entries);
1341 return; 1327 return;
1342 1328
1343 case 'newfolder': 1329 case 'newfolder':
1344 this.onNewFolderCommand_(event); 1330 this.onNewFolderCommand_(event);
1345 return; 1331 return;
1346 1332
1347 case 'unmount': 1333 case 'unmount':
1348 this.unmountVolume_(this.directoryModel_.getCurrentRootDirEntry()); 1334 this.unmountVolume_(
1335 this.directoryModel_.getCurrentRootDirEntry());
1349 return; 1336 return;
1350 1337
1351 case 'format': 1338 case 'format':
1352 var entry = this.directoryModel_.getCurrentRootDirEntry(); 1339 var entry = this.directoryModel_.getCurrentRootDirEntry();
1353 this.confirm.show(str('FORMATTING_WARNING'), function() { 1340 this.confirm.show(str('FORMATTING_WARNING'), function() {
1354 chrome.fileBrowserPrivate.formatDevice(entry.toURL()); 1341 chrome.fileBrowserPrivate.formatDevice(entry.toURL());
1355 }); 1342 });
1356 1343
1357 return; 1344 return;
1358 } 1345 }
1359 }; 1346 };
1360 1347
1361 /** 1348 /**
1362 * Respond to the back and forward buttons. 1349 * Respond to the back and forward buttons.
1363 */ 1350 */
1364 FileManager.prototype.onPopState_ = function(event) { 1351 FileManager.prototype.onPopState_ = function(event) {
1365 this.closeFilePopup_(); 1352 this.closeFilePopup_();
1366 // Nothing left to do if the current directory is not changing. This happens 1353 this.setupCurrentDirectory_(false /* page loading */);
1367 // if we are exiting the Gallery.
1368 if (this.getPathFromUrlOrParams_() ==
1369 this.directoryModel_.getCurrentDirEntry().fullPath)
1370 return;
1371 this.setupCurrentDirectory_(true /* invokeHandler */);
1372 }; 1354 };
1373 1355
1374 FileManager.prototype.requestResize_ = function(timeout) { 1356 FileManager.prototype.requestResize_ = function(timeout) {
1375 setTimeout(this.onResize_.bind(this), timeout || 0); 1357 setTimeout(this.onResize_.bind(this), timeout || 0);
1376 }; 1358 };
1377 1359
1378 /** 1360 /**
1379 * Resize details and thumb views to fit the new window size. 1361 * Resize details and thumb views to fit the new window size.
1380 */ 1362 */
1381 FileManager.prototype.onResize_ = function() { 1363 FileManager.prototype.onResize_ = function() {
(...skipping 19 matching lines...) Expand all
1401 this.rootsList_.redraw(); 1383 this.rootsList_.redraw();
1402 this.truncateBreadcrumbs_(); 1384 this.truncateBreadcrumbs_();
1403 }; 1385 };
1404 1386
1405 FileManager.prototype.resolvePath = function( 1387 FileManager.prototype.resolvePath = function(
1406 path, resultCallback, errorCallback) { 1388 path, resultCallback, errorCallback) {
1407 return util.resolvePath(this.filesystem_.root, path, resultCallback, 1389 return util.resolvePath(this.filesystem_.root, path, resultCallback,
1408 errorCallback); 1390 errorCallback);
1409 }; 1391 };
1410 1392
1411 FileManager.prototype.getPathFromUrlOrParams_ = function() {
1412 return location.hash ? // Location hash has the highest priority.
1413 decodeURI(location.hash.substr(1)) :
1414 this.params_.defaultPath;
1415 };
1416
1417 /** 1393 /**
1418 * Restores current directory and may be a selected item after page load (or 1394 * Restores current directory and may be a selected item after page load (or
1419 * reload) or popping a state (after click on back/forward). If location.hash 1395 * reload) or popping a state (after click on back/forward). If location.hash
1420 * is present it means that the user has navigated somewhere and that place 1396 * is present it means that the user has navigated somewhere and that place
1421 * will be restored. defaultPath primarily is used with save/open dialogs. 1397 * will be restored. defaultPath primarily is used with save/open dialogs.
1422 * Default path may also contain a file name. Freshly opened file manager 1398 * Default path may also contain a file name. Freshly opened file manager
1423 * window has neither. 1399 * window has neither.
1424 * 1400 *
1425 * @param {boolean} invokeHandler Whether to invoke the default handler on 1401 * @param {boolean} pageLoading True if the page is loading,
1426 * the selected file. 1402 false if popping state.
1427 * @param {boolean} opt_blankWhileOpeningAFile Whether to show fade over
1428 * the file manager.
1429 */ 1403 */
1430 FileManager.prototype.setupCurrentDirectory_ = 1404 FileManager.prototype.setupCurrentDirectory_ = function(pageLoading) {
1431 function(invokeHandler, opt_blankWhileOpeningAFile) { 1405 var path = location.hash ? // Location hash has the highest priority.
1432 var path = this.getPathFromUrlOrParams_(); 1406 decodeURI(location.hash.substr(1)) :
1407 this.params_.defaultPath;
1408
1409 if (!pageLoading && path == this.directoryModel_.getCurrentDirPath())
1410 return;
1433 1411
1434 if (!path) { 1412 if (!path) {
1435 this.directoryModel_.setupDefaultPath(); 1413 this.directoryModel_.setupDefaultPath();
1436 return; 1414 return;
1437 } 1415 }
1438 1416
1439 // In the FULL_PAGE mode if the hash path points to a file we might have 1417 // In the FULL_PAGE mode if the hash path points to a file we might have
1440 // to invoke a task after selecting it. 1418 // to invoke a task after selecting it.
1441 // If the file path is in params_ we only want to select the file. 1419 // If the file path is in params_ we only want to select the file.
1442 if (invokeHandler && location.hash && 1420 var invokeHandlers = pageLoading && !this.params_.selectOnly &&
1443 this.dialogType_ == FileManager.DialogType.FULL_PAGE) { 1421 this.dialogType_ == FileManager.DialogType.FULL_PAGE &&
1444 // To prevent the file list flickering for a moment before the action 1422 !!location.hash;
1445 // is executed we hide it under a white div. 1423
1446 var shade; 1424 if (DirectoryModel.getRootType(path) == DirectoryModel.RootType.GDATA) {
1447 if (opt_blankWhileOpeningAFile) { 1425 var tracker = this.directoryModel_.createDirectoryChangeTracker();
1448 shade = this.document_.createElement('div'); 1426 // Expected finish of setupPath to GData.
1449 shade.className = 'overlay-pane'; 1427 tracker.exceptInitialChange = true;
1450 shade.style.backgroundColor = 'white'; 1428 tracker.start();
1451 this.document_.body.appendChild(shade); 1429 if (!this.isGDataEnabled()) {
1430 if (pageLoading)
1431 this.show_();
1432 this.directoryModel_.setupDefaultPath();
1433 return;
1452 } 1434 }
1453 function removeShade() { 1435 var gdataPath = '/' + DirectoryModel.GDATA_DIRECTORY;
1454 if (shade) 1436 if (this.volumeManager_.isMounted(gdataPath)) {
1455 shade.parentNode.removeChild(shade); 1437 this.finishSetupCurrentDirectory_(path, invokeHandlers);
1438 return;
1439 }
1440 if (pageLoading)
1441 this.delayShow_(500);
1442 // Reflect immediatelly in the UI we are on GData and display
1443 // mounting UI.
1444 this.directoryModel_.setupPath(gdataPath);
1445
1446 if (!this.isOnGData()) {
1447 // Since GDATA is not mounted it should be resolved synchronously
1448 // (no need in asynchronous calls to filesystem API). It is important
1449 // to prevent race condition.
1450 console.error('Expected path set up synchronously');
1456 } 1451 }
1457 1452
1453 var self = this;
1454 this.volumeManager_.mountGData(function() {
1455 tracker.stop();
1456 if (!tracker.hasChanged) {
1457 self.finishSetupCurrentDirectory_(path, invokeHandlers);
1458 }
1459 }, function(error) {
1460 tracker.stop();
1461 });
1462 } else {
1463 if (invokeHandlers && pageLoading)
1464 this.delayShow_(500);
1465 this.finishSetupCurrentDirectory_(path, invokeHandlers);
1466 }
1467 };
1468
1469 /**
1470 * @param {string} path Path to setup.
1471 * @param {boolean} invokeHandlers If thrue and |path| points to a file
1472 * then default handler is triggered.
1473 */
1474 FileManager.prototype.finishSetupCurrentDirectory_ = function(
1475 path, invokeHandlers) {
1476 if (invokeHandlers) {
1458 // Keep track of whether the path is identified as an existing leaf 1477 // Keep track of whether the path is identified as an existing leaf
1459 // node. Note that onResolve is guaranteed to be called (exactly once) 1478 // node. Note that onResolve is guaranteed to be called (exactly once)
1460 // before onLoadedActivateLeaf. 1479 // before onLoadedActivateLeaf.
1461 var foundLeaf = true; 1480 var foundLeaf = true;
1462 function onResolve(baseName, leafName, exists) { 1481 function onResolve(baseName, leafName, exists) {
1463 if (!exists || leafName == '') { 1482 if (!exists || leafName == '') {
1464 // Non-existent file or a directory. Remove the shade immediately. 1483 // Non-existent file or a directory. Remove the shade immediately.
1465 removeShade();
1466 foundLeaf = false; 1484 foundLeaf = false;
1485 self.show_();
1467 } 1486 }
1468 } 1487 }
1469 1488
1470 // TODO(kaznacheev): refactor dispatchDefaultTask to accept an array 1489 // TODO(kaznacheev): refactor dispatchDefaultTask to accept an array
1471 // of urls instead of a selection. This will remove the need to wait 1490 // of urls instead of a selection. This will remove the need to wait
1472 // until the selection is done. 1491 // until the selection is done.
1473 var self = this; 1492 var self = this;
1474 function onLoadedActivateLeaf() { 1493 function onLoadedActivateLeaf() {
1475 if (foundLeaf) { 1494 if (foundLeaf) {
1476 // There are 3 ways we can get here: 1495 // There are 3 ways we can get here:
1477 // 1. Invoked from file_manager_util::ViewFile. This can only 1496 // 1. Invoked from file_manager_util::ViewFile. This can only
1478 // happen for 'gallery' and 'mount-archive' actions. 1497 // happen for 'gallery' and 'mount-archive' actions.
1479 // 2. Reloading a Gallery page. Must be an image or a video file. 1498 // 2. Reloading a Gallery page. Must be an image or a video file.
1480 // 3. A user manually entered a URL pointing to a file. 1499 // 3. A user manually entered a URL pointing to a file.
1481 if (FileType.isImageOrVideo(path)) { 1500 if (FileType.isImageOrVideo(path)) {
1482 self.dispatchInternalTask_('gallery', self.selection.urls); 1501 self.dispatchInternalTask_('gallery', self.selection.urls);
1483 } else if (FileType.getMediaType(path) == 'archive') { 1502 } else if (FileType.getMediaType(path) == 'archive') {
1503 self.show_();
1484 self.dispatchInternalTask_('mount-archive', self.selection.urls); 1504 self.dispatchInternalTask_('mount-archive', self.selection.urls);
1485 } else { 1505 } else {
1486 // Manually entered path, do nothing, remove the shade ASAP. 1506 self.show_();
1487 removeShade();
1488 return; 1507 return;
1489 } 1508 }
1490 setTimeout(removeShade, 1000);
1491 } 1509 }
1492 } 1510 }
1493 this.directoryModel_.setupPath(path, onLoadedActivateLeaf, onResolve); 1511 this.directoryModel_.setupPath(path, onLoadedActivateLeaf, onResolve);
1494 return; 1512 return;
1495 } 1513 }
1496 1514
1497 if (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) { 1515 if (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) {
1498 this.directoryModel_.setupPath(path, undefined, 1516 this.directoryModel_.setupPath(path, undefined,
1499 function(basePath, leafName) { 1517 function(basePath, leafName) {
1500 this.filenameInput_.value = leafName; 1518 this.filenameInput_.value = leafName;
1501 this.selectDefaultPathInFilenameInput_(); 1519 this.selectDefaultPathInFilenameInput_();
1502 }.bind(this)); 1520 }.bind(this));
1503 return; 1521 return;
1504 } 1522 }
1505 1523
1524 this.show_();
1506 this.directoryModel_.setupPath(path); 1525 this.directoryModel_.setupPath(path);
1507 }; 1526 };
1508 1527
1509 /** 1528 /**
1510 * Tweak the UI to become a particular kind of dialog, as determined by the 1529 * Tweak the UI to become a particular kind of dialog, as determined by the
1511 * dialog type parameter passed to the constructor. 1530 * dialog type parameter passed to the constructor.
1512 */ 1531 */
1513 FileManager.prototype.initDialogType_ = function() { 1532 FileManager.prototype.initDialogType_ = function() {
1514 var defaultTitle; 1533 var defaultTitle;
1515 var okLabel = str('OPEN_LABEL'); 1534 var okLabel = str('OPEN_LABEL');
(...skipping 259 matching lines...) Expand 10 before | Expand all | Expand 10 after
1775 } 1794 }
1776 }; 1795 };
1777 li.addEventListener('mousedown', handleClick); 1796 li.addEventListener('mousedown', handleClick);
1778 li.addEventListener(cr.ui.TouchHandler.EventType.TOUCH_START, handleClick); 1797 li.addEventListener(cr.ui.TouchHandler.EventType.TOUCH_START, handleClick);
1779 1798
1780 var rootType = DirectoryModel.getRootType(entry.fullPath); 1799 var rootType = DirectoryModel.getRootType(entry.fullPath);
1781 1800
1782 var div = this.document_.createElement('div'); 1801 var div = this.document_.createElement('div');
1783 div.className = 'root-label'; 1802 div.className = 'root-label';
1784 1803
1785 var icon = rootType;
1786 var deviceNumber = this.getDeviceNumber(entry);
1787
1788 if (deviceNumber != undefined) {
1789 var mountCondition = this.mountPoints_[deviceNumber].mountCondition;
1790 if (mountCondition == 'unknown_filesystem' ||
1791 mountCondition == 'unsupported_filesystem')
1792 icon = 'unreadable';
1793 }
1794
1795 div.setAttribute('icon', icon);
1796
1797 div.textContent = this.getRootLabel_(entry.fullPath); 1804 div.textContent = this.getRootLabel_(entry.fullPath);
1798 li.appendChild(div); 1805 li.appendChild(div);
1799 1806
1800 if (rootType == DirectoryModel.RootType.ARCHIVE || 1807 if (rootType == DirectoryModel.RootType.ARCHIVE ||
1801 rootType == DirectoryModel.RootType.REMOVABLE) { 1808 rootType == DirectoryModel.RootType.REMOVABLE) {
1802 if (entry.unmounting) { 1809 var eject = this.document_.createElement('div');
1803 li.setAttribute('disabled', 'disabled'); 1810 eject.className = 'root-eject';
1804 } else { 1811 eject.addEventListener('click', function(event) {
1805 var eject = this.document_.createElement('div'); 1812 event.stopPropagation();
1806 eject.className = 'root-eject'; 1813 this.unmountVolume_(entry);
1807 eject.addEventListener('click', function(event) { 1814 }.bind(this));
1808 event.stopPropagation(); 1815 // Block other mouse handlers.
1809 this.unmountVolume_(entry); 1816 eject.addEventListener('mouseup', function(e) { e.stopPropagation() });
1810 }.bind(this)); 1817 eject.addEventListener('mousedown', function(e) { e.stopPropagation() });
1811 // Block other mouse handlers. 1818 li.appendChild(eject);
1812 eject.addEventListener('mouseup', function(e) { e.stopPropagation() });
1813 eject.addEventListener('mousedown', function(e) { e.stopPropagation() }) ;
1814 li.appendChild(eject);
1815 1819
1816 cr.ui.contextMenuHandler.setContextMenu(li, this.rootsContextMenu_); 1820 cr.ui.contextMenuHandler.setContextMenu(li, this.rootsContextMenu_);
1817 }
1818 } 1821 }
1819 1822
1820 cr.defineProperty(li, 'lead', cr.PropertyKind.BOOL_ATTR); 1823 cr.defineProperty(li, 'lead', cr.PropertyKind.BOOL_ATTR);
1821 cr.defineProperty(li, 'selected', cr.PropertyKind.BOOL_ATTR); 1824 cr.defineProperty(li, 'selected', cr.PropertyKind.BOOL_ATTR);
1825
1826 var icon = rootType;
1827 if (this.volumeManager_.isUnreadable(entry.fullPath)) {
1828 icon = 'unreadable';
1829 }
1830 div.setAttribute('icon', icon);
1831
1822 return li; 1832 return li;
1823 }; 1833 };
1824 1834
1825 /** 1835 /**
1826 * Unmounts device. 1836 * Unmounts device.
1827 * @param {Entry} entry The entry to unmount. 1837 * @param {Entry} entry The entry to unmount.
1828 */ 1838 */
1829 FileManager.prototype.unmountVolume_ = function(entry) { 1839 FileManager.prototype.unmountVolume_ = function(entry) {
1830 this.directoryModel_.prepareUnmount(entry.fullPath); 1840 listItem = this.rootsList_.getListItem(entry);
1831 this.unmountRequests_.push(entry.fullPath); 1841 if (listItem)
1832 chrome.fileBrowserPrivate.removeMount(entry.toURL()); 1842 listItem.setAttribute('disabled', '');
1843 var self = this;
1844 this.volumeManager_.unmount(entry.fullPath, function() {},
1845 function(error) {
1846 if (listItem)
1847 listItem.removeAttribute('disabled');
1848 self.alert.show(strf('UNMOUNT_FAILED', error.message));
1849 });
1833 }; 1850 };
1834 1851
1835 FileManager.prototype.updateGDataStyle_ = function( 1852 FileManager.prototype.updateGDataStyle_ = function(
1836 listItem, entry, gdata) { 1853 listItem, entry, gdata) {
1837 if (!this.isOnGData() || !gdata) 1854 if (!this.isOnGData() || !gdata)
1838 return; 1855 return;
1839 1856
1840 if (!entry.isDirectory) { 1857 if (!entry.isDirectory) {
1841 if (!gdata.availableOffline) 1858 if (!gdata.availableOffline)
1842 listItem.classList.add('dim-offline'); 1859 listItem.classList.add('dim-offline');
(...skipping 350 matching lines...) Expand 10 before | Expand all | Expand 10 after
2193 2210
2194 if (entry.isFile) { 2211 if (entry.isFile) {
2195 selection.bytes += filesystem.size; 2212 selection.bytes += filesystem.size;
2196 } 2213 }
2197 } 2214 }
2198 2215
2199 this.dispatchEvent(new cr.Event('selection-summarized')); 2216 this.dispatchEvent(new cr.Event('selection-summarized'));
2200 }.bind(this)); 2217 }.bind(this));
2201 2218
2202 if (this.isOnGData()) { 2219 if (this.isOnGData()) {
2220 function predicate(p) {
2221 return !(p && p.availableOffline);
2222 }
2203 this.metadataCache_.get(selection.urls, 'gdata', function(props) { 2223 this.metadataCache_.get(selection.urls, 'gdata', function(props) {
2204 selection.allGDataFilesPresent = 2224 selection.allGDataFilesPresent =
2205 props.filter(function(p) {return !p.availableOffline}).length == 0; 2225 props.filter(predicate).length == 0;
2206 this.updateOkButton_(); 2226 this.updateOkButton_();
2207 }.bind(this)); 2227 }.bind(this));
2208 } 2228 }
2209 }; 2229 };
2210 2230
2211 /** 2231 /**
2212 * Check if all the files in the current selection are available. The only 2232 * Check if all the files in the current selection are available. The only
2213 * case when files might be not available is when the selection contains 2233 * case when files might be not available is when the selection contains
2214 * uncached GData files and the browser is offline. 2234 * uncached GData files and the browser is offline.
2215 * @return {boolean} True if all files in the current selection are 2235 * @return {boolean} True if all files in the current selection are
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after
2334 setVisibility('visible'); 2354 setVisibility('visible');
2335 self.previewThumbnails_.textContent = ''; 2355 self.previewThumbnails_.textContent = '';
2336 self.onResize_(); 2356 self.onResize_();
2337 } 2357 }
2338 2358
2339 function setVisibility(visibility) { 2359 function setVisibility(visibility) {
2340 panel.setAttribute('visibility', visibility); 2360 panel.setAttribute('visibility', visibility);
2341 } 2361 }
2342 }; 2362 };
2343 2363
2344 FileManager.prototype.isGDataEnabled = function() {
2345 return this.getGDataPreferences_().driveEnabled;
2346 };
2347
2348 FileManager.prototype.updateGDataAccess_ = function() {
2349 if (this.isGDataEnabled())
2350 this.setupGDataWelcome_();
2351 else
2352 this.cleanupGDataWelcome_();
2353
2354 var changeDirectory = !this.isGDataEnabled() && this.isOnGData();
2355
2356 this.directoryModel_.updateRoots(function() {
2357 if (changeDirectory)
2358 this.directoryModel_.changeDirectory(
2359 this.directoryModel_.getDefaultDirectory());
2360 }.bind(this), this.getGDataAccessMode_());
2361 };
2362
2363 FileManager.prototype.getGDataAccessMode_ = function() {
2364 if (!this.isGDataEnabled())
2365 return DirectoryModel.GDATA_ACCESS_DISABLED;
2366 if (!this.gdataMounted_)
2367 return DirectoryModel.GDATA_ACCESS_LAZY;
2368 return DirectoryModel.GDATA_ACCESS_FULL;
2369 };
2370
2371 FileManager.prototype.isOnGData = function() { 2364 FileManager.prototype.isOnGData = function() {
2372 return this.directoryModel_ && 2365 return this.directoryModel_.getCurrentRootType() ==
2373 this.directoryModel_.getCurrentRootPath() == 2366 DirectoryModel.RootType.GDATA;
2374 '/' + DirectoryModel.GDATA_DIRECTORY;
2375 };
2376
2377 FileManager.prototype.isStartingOnGData_ = function() {
2378 var path = this.getPathFromUrlOrParams_();
2379 return path &&
2380 this.isGDataEnabled() &&
2381 DirectoryModel.getRootType(path) == DirectoryModel.RootType.GDATA;
2382 }; 2367 };
2383 2368
2384 FileManager.prototype.getMetadataProvider = function() { 2369 FileManager.prototype.getMetadataProvider = function() {
2385 return this.metadataProvider_; 2370 return this.metadataProvider_;
2386 }; 2371 };
2387 2372
2388 /** 2373 /**
2389 * Callback called when tasks for selected files are determined. 2374 * Callback called when tasks for selected files are determined.
2390 * @param {Object} selection Selection is passed here, since this.selection 2375 * @param {Object} selection Selection is passed here, since this.selection
2391 * can change before tasks were found, and we should be accurate. 2376 * can change before tasks were found, and we should be accurate.
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after
2603 }.bind(this)); 2588 }.bind(this));
2604 return; 2589 return;
2605 } 2590 }
2606 callback(urls); 2591 callback(urls);
2607 }.bind(this)); 2592 }.bind(this));
2608 } else { 2593 } else {
2609 callback(urls); 2594 callback(urls);
2610 } 2595 }
2611 }; 2596 };
2612 2597
2613 FileManager.prototype.getGDataPreferences_ = function() { 2598 FileManager.prototype.updateNetworkStateAndGDataPreferences_ = function(
2614 return this.gdataPreferences_ || 2599 callback) {
2615 { driveEnabled: str('ENABLE_GDATA') == '1' }; 2600 var self = this;
2601 var downcount = 2;
2602 function done() {
2603 if (--downcount == 0)
2604 callback();
2605 }
2606
2607 chrome.fileBrowserPrivate.getGDataPreferences(function(prefs) {
2608 self.gdataPreferences_ = prefs;
2609 done();
2610 });
2611
2612 chrome.fileBrowserPrivate.getNetworkConnectionState(function(netwokState) {
2613 self.networkState_ = netwokState;
2614 done();
2615 });
2616 }; 2616 };
2617 2617
2618 FileManager.prototype.getNetworkConnectionState_ = function() { 2618 FileManager.prototype.onNetworkStateOrGDataPreferencesChanged_ = function() {
2619 return this.networkConnectionState_ || {}; 2619 var self = this;
2620 }; 2620 this.updateNetworkStateAndGDataPreferences_(function() {
2621 var gdata = self.gdataPreferences_;
2622 var network = self.networkState_;
2621 2623
2622 FileManager.prototype.onNetworkConnectionChanged_ = function(state) { 2624 self.directoryModel_.setGDataEnabled(self.isGDataEnabled());
2623 console.log(state.online ? 'online' : 'offline', state.type);
2624 this.networkConnectionState_ = state;
2625 this.directoryModel_.setOffline(!state.online);
2626 this.updateConnectionState_();
2627 };
2628 2625
2629 FileManager.prototype.onGDataPreferencesChanged_ = function(preferences) { 2626 if (self.isGDataEnabled())
2630 var gdataWasEnabled = this.isGDataEnabled(); 2627 self.setupGDataWelcome_();
2631 this.gdataPreferences_ = preferences; 2628 else
2632 if (gdataWasEnabled != this.isGDataEnabled()) 2629 self.cleanupGDataWelcome_();
2633 this.updateGDataAccess_();
2634 2630
2635 if (preferences.cellularDisabled) 2631 if (gdata.cellularDisabled)
2636 this.syncButton.setAttribute('checked', 'checked'); 2632 self.syncButton.setAttribute('checked', '');
2637 else 2633 else
2638 this.syncButton.removeAttribute('checked'); 2634 self.syncButton.removeAttribute('checked');
2639 2635
2640 if (!preferences.hostedFilesDisabled) 2636 if (!gdata.hostedFilesDisabled)
2641 this.hostedButton.setAttribute('checked', 'checked'); 2637 self.hostedButton.setAttribute('checked', '');
2642 else 2638 else
2643 this.hostedButton.removeAttribute('checked'); 2639 self.hostedButton.removeAttribute('checked');
2644 2640
2645 this.updateConnectionState_(); 2641 if (network.online) {
2646 }; 2642 if (gdata.cellularDisabled && network.type == 'cellular')
2647 2643 self.dialogContainer_.setAttribute('connection', 'metered');
2648 FileManager.prototype.updateConnectionState_ = function() { 2644 else
2649 if (this.isOffline()) 2645 self.dialogContainer_.removeAttribute('connection');
2650 this.dialogContainer_.setAttribute('connection', 'offline'); 2646 } else {
2651 else if (this.isOnMeteredConnection()) 2647 self.dialogContainer_.setAttribute('connection', 'offline');
2652 this.dialogContainer_.setAttribute('connection', 'metered'); 2648 }
2653 else 2649 });
2654 this.dialogContainer_.removeAttribute('connection');
2655 }; 2650 };
2656 2651
2657 FileManager.prototype.isOnMeteredConnection = function() { 2652 FileManager.prototype.isOnMeteredConnection = function() {
2658 return this.getGDataPreferences_().cellularDisabled && 2653 return this.gdataPreferences_.cellularDisabled &&
2659 this.getNetworkConnectionState_().online && 2654 this.networkState_.online &&
2660 this.getNetworkConnectionState_().type == 'cellular'; 2655 this.networkState_.type == 'cellular';
2661 }; 2656 };
2662 2657
2663 FileManager.prototype.isOffline = function() { 2658 FileManager.prototype.isOffline = function() {
2664 return !this.getNetworkConnectionState_().online; 2659 return !this.networkState_.online;
2660 };
2661
2662 FileManager.prototype.isGDataEnabled = function() {
2663 return !('driveEnabled' in this.gdataPreferences_) ||
2664 this.gdataPreferences_.driveEnabled;
2665 }; 2665 };
2666 2666
2667 FileManager.prototype.isOnReadonlyDirectory = function() { 2667 FileManager.prototype.isOnReadonlyDirectory = function() {
2668 return this.directoryModel_.isReadOnly(); 2668 return this.directoryModel_.isReadOnly();
2669 }; 2669 };
2670 2670
2671 /** 2671 FileManager.prototype.onUnexpectedlyUnmounted_ = function(event) {
2672 * Event handler called when some volume was mounted or unmouted. 2672 if (event.mountPath == this.directoryModel_.getCurrentRootPath()) {
2673 */ 2673 if (this.params_.mountTriggered) {
2674 FileManager.prototype.onMountCompleted_ = function(event) { 2674 // TODO(serya): What if 2 USB sticks plugged?
2675 var changeDirectoryTo = null; 2675 chrome.tabs.getCurrent(function(tab) {
2676 2676 chrome.tabs.remove(tab.id);
2677 if (event && event.mountType == 'gdata') { 2677 });
2678 var mounted = (event.eventType == 'mount');
2679 metrics.recordInterval('Load.GData');
2680 console.log('GData ' + (mounted ? 'mounted' : 'unmounted'));
2681 if (mounted && event.status == 'success') {
2682 this.gdataMounted_ = true;
2683 this.gdataMountInfo_ = {
2684 'mountPath': event.mountPath,
2685 'sourcePath': event.sourcePath,
2686 'mountType': event.mountType,
2687 'mountCondition': event.status
2688 };
2689 // Not calling clearGDataLoadingTimer_ here because we want to keep
2690 // "Loading Google Docs" message until the directory loads. It is OK if
2691 // the timer fires after the mount because onDirectoryChanged_ will hide
2692 // the unmounted panel.
2693 if (this.setupCurrentDirectoryPostponed_) {
2694 this.setupCurrentDirectoryPostponed_(false /* execute */);
2695 } else if (this.isOnGData() &&
2696 this.directoryModel_.getCurrentDirEntry().unmounted) {
2697 // We are currently on an unmounted GData directory, force a rescan.
2698 changeDirectoryTo = this.directoryModel_.getCurrentRootPath();
2699 }
2700 } else {
2701 this.onGDataUnreachable_('GData ' +
2702 (mounted ? ('mount failed: ' + event.status) : 'unmounted'));
2703 if (this.setupCurrentDirectoryPostponed_) {
2704 this.setupCurrentDirectoryPostponed_(true /* cancel */);
2705 // Change to unmounted GData root.
2706 changeDirectoryTo = '/' + DirectoryModel.GDATA_DIRECTORY;
2707 }
2708 } 2678 }
2709 } 2679 }
2710
2711 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) {
2712 this.setMountPoints_(mountPoints);
2713
2714 if (event.eventType == 'mount' && event.mountType != 'gdata') {
2715 // Mount request finished - remove it.
2716 // Currently we only request mounts for archive files.
2717 var index = this.mountRequests_.indexOf(event.sourcePath);
2718 if (index != -1) {
2719 this.mountRequests_.splice(index, 1);
2720 if (event.status == 'success') {
2721 // Successful mount requested from this tab, go to the drive root.
2722 changeDirectoryTo = event.mountPath;
2723 } else {
2724 // Request initiated from this tab failed, report the error.
2725 var fileName = event.sourcePath.split('/').pop();
2726 this.alert.show(
2727 strf('ARCHIVE_MOUNT_FAILED', fileName, event.status));
2728 }
2729 }
2730 }
2731
2732 if (event.eventType == 'unmount' && event.mountType != 'gdata') {
2733 // Unmount request finished - remove it.
2734 var index = this.unmountRequests_.indexOf(event.mountPath);
2735 if (index != -1) {
2736 this.unmountRequests_.splice(index, 1);
2737 if (event.status != 'success')
2738 this.alert.show(strf('UNMOUNT_FAILED', event.status));
2739 }
2740
2741 if (event.status == 'success' &&
2742 event.mountPath == this.directoryModel_.getCurrentRootPath()) {
2743 if (this.params_.mountTriggered && index == -1) {
2744 // This device mount was the reason this File Manager instance was
2745 // created. Now the device is unmounted from another instance
2746 // or the user removed the device manually. Close this instance.
2747 // window.close() sometimes doesn't work.
2748 chrome.tabs.getCurrent(function(tab) {
2749 chrome.tabs.remove(tab.id);
2750 });
2751 return;
2752 }
2753 // Current directory just unmounted. Move to the 'Downloads'.
2754 changeDirectoryTo = this.directoryModel_.getDefaultDirectory();
2755 }
2756 }
2757
2758 // Even if something failed root list should be rescanned.
2759 // Failed mounts can "give" us new devices which might be formatted,
2760 // so we have to refresh root list then.
2761 this.directoryModel_.updateRoots(function() {
2762 if (changeDirectoryTo) {
2763 this.directoryModel_.changeDirectory(changeDirectoryTo);
2764 }
2765 }.bind(this), this.getGDataAccessMode_());
2766 }.bind(this));
2767 }; 2680 };
2768 2681
2769 /** 2682 /**
2770 * Event handler called when some internal task should be executed. 2683 * Event handler called when some internal task should be executed.
2771 */ 2684 */
2772 FileManager.prototype.onFileTaskExecute_ = function(id, urls) { 2685 FileManager.prototype.onFileTaskExecute_ = function(id, urls) {
2773 if (id == 'play') { 2686 if (id == 'play') {
2774 var position = 0; 2687 var position = 0;
2775 if (urls.length == 1) { 2688 if (urls.length == 1) {
2776 // If just a single audio file is selected pass along every audio file 2689 // If just a single audio file is selected pass along every audio file
2777 // in the directory. 2690 // in the directory.
2778 var selectedUrl = urls[0]; 2691 var selectedUrl = urls[0];
2779 urls = this.getAllUrlsInCurrentDirectory_().filter(FileType.isAudio); 2692 urls = this.getAllUrlsInCurrentDirectory_().filter(FileType.isAudio);
2780 position = urls.indexOf(selectedUrl); 2693 position = urls.indexOf(selectedUrl);
2781 } 2694 }
2782 chrome.mediaPlayerPrivate.play(urls, position); 2695 chrome.mediaPlayerPrivate.play(urls, position);
2783 } else if (id == 'mount-archive') { 2696 } else if (id == 'mount-archive') {
2784 var self = this; 2697 var self = this;
2698 var tracker = this.directoryModel_.createDirectoryChangeTracker();
2699 tracker.start();
2785 this.resolveSelectResults_(urls, function(urls) { 2700 this.resolveSelectResults_(urls, function(urls) {
2786 for (var index = 0; index < urls.length; ++index) { 2701 for (var index = 0; index < urls.length; ++index) {
2787 // Url in MountCompleted event won't be escaped, so let's make sure 2702 var path = /^filesystem:[\w-]*:\/\/[\w]*\/external(\/.*)$/.
2788 // we don't use escaped one in mountRequests_. 2703 exec(urls[index])[1];
2789 var unescapedUrl = unescape(urls[index]); 2704 if (!path)
2790 chrome.fileBrowserPrivate.addMount(unescapedUrl, 'file', {}, 2705 continue;
2791 function(sourcePath) { 2706 path = decodeURIComponent(path);
2792 self.mountRequests_.push(sourcePath); 2707 self.volumeManager_.mountArchive(path, function(mountPath) {
2708 console.log('Mounted at: ', mountPath);
2709 tracker.stop();
2710 if (!tracker.hasChanged)
2711 self.directoryModel_.changeDirectory(mountPath);
2712 }, function(error) {
2713 tracker.stop();
2714 var namePos = path.lastIndexOf('/');
2715 self.alert.show(strf('ARCHIVE_MOUNT_FAILED',
2716 path.substr(namePos + 1), error));
2793 }); 2717 });
2794 } 2718 }
2795 }); 2719 });
2796 } else if (id == 'format-device') { 2720 } else if (id == 'format-device') {
2797 this.confirm.show(str('FORMATTING_WARNING'), function() { 2721 this.confirm.show(str('FORMATTING_WARNING'), function() {
2798 chrome.fileBrowserPrivate.formatDevice(urls[0]); 2722 chrome.fileBrowserPrivate.formatDevice(urls[0]);
2799 }); 2723 });
2800 } else if (id == 'gallery') { 2724 } else if (id == 'gallery') {
2801 this.openGallery_(urls); 2725 this.openGallery_(urls);
2802 } else if (id == 'view-pdf' || id == 'view-in-browser' || 2726 } else if (id == 'view-pdf' || id == 'view-in-browser' ||
2803 id == 'install-crx' || id == 'open-hosted') { 2727 id == 'install-crx' || id == 'open-hosted') {
2804 chrome.fileBrowserPrivate.viewFiles(urls, 'default', function(success) { 2728 chrome.fileBrowserPrivate.viewFiles(urls, 'default', function(success) {
2805 if (!success) 2729 if (!success)
2806 console.error('chrome.fileBrowserPrivate.viewFiles failed', urls); 2730 console.error('chrome.fileBrowserPrivate.viewFiles failed', urls);
2807 }); 2731 });
2808 } 2732 }
2809 }; 2733 };
2810 2734
2811 FileManager.prototype.getDeviceNumber = function(entry) {
2812 if (!entry.isDirectory) return undefined;
2813 for (var i = 0; i < this.mountPoints_.length; i++) {
2814 if (normalizeAbsolutePath(entry.fullPath) ==
2815 normalizeAbsolutePath(this.mountPoints_[i].mountPath)) {
2816 return i;
2817 }
2818 }
2819 return undefined;
2820 };
2821
2822 /** 2735 /**
2823 * Show a modal-like file viewer/editor on top of the File Manager UI. 2736 * Show a modal-like file viewer/editor on top of the File Manager UI.
2824 * 2737 *
2825 * @param {HTMLElement} popup Popup element. 2738 * @param {HTMLElement} popup Popup element.
2826 * @param {function} closeCallback Function to call after the popup is closed. 2739 * @param {function} closeCallback Function to call after the popup is closed.
2827 */ 2740 */
2828 FileManager.prototype.openFilePopup_ = function(popup, closeCallback) { 2741 FileManager.prototype.openFilePopup_ = function(popup, closeCallback) {
2829 this.closeFilePopup_(); 2742 this.closeFilePopup_();
2830 this.filePopup_ = popup; 2743 this.filePopup_ = popup;
2831 this.filePopupCloseCallback_ = closeCallback; 2744 this.filePopupCloseCallback_ = closeCallback;
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after
2922 galleryFrame.className = 'overlay-pane'; 2835 galleryFrame.className = 'overlay-pane';
2923 galleryFrame.scrolling = 'no'; 2836 galleryFrame.scrolling = 'no';
2924 galleryFrame.setAttribute('webkitallowfullscreen', true); 2837 galleryFrame.setAttribute('webkitallowfullscreen', true);
2925 2838
2926 var dirPath = this.directoryModel_.getCurrentDirEntry().fullPath; 2839 var dirPath = this.directoryModel_.getCurrentDirEntry().fullPath;
2927 // Push a temporary state which will be replaced every time an individual 2840 // Push a temporary state which will be replaced every time an individual
2928 // item is selected in the Gallery. 2841 // item is selected in the Gallery.
2929 this.updateLocation_(false /*push*/, dirPath); 2842 this.updateLocation_(false /*push*/, dirPath);
2930 2843
2931 galleryFrame.onload = function() { 2844 galleryFrame.onload = function() {
2845 self.show_();
2932 galleryFrame.contentWindow.ImageUtil.metrics = metrics; 2846 galleryFrame.contentWindow.ImageUtil.metrics = metrics;
2933 galleryFrame.contentWindow.FileType = FileType; 2847 galleryFrame.contentWindow.FileType = FileType;
2934 galleryFrame.contentWindow.util = util; 2848 galleryFrame.contentWindow.util = util;
2935 2849
2936 // Gallery shoud treat GData folder as readonly even when online 2850 // Gallery shoud treat GData folder as readonly even when online
2937 // until we learn to save files directly to GData. 2851 // until we learn to save files directly to GData.
2938 var readonly = self.isOnReadonlyDirectory() || self.isOnGData(); 2852 var readonly = self.isOnReadonlyDirectory() || self.isOnGData();
2939 var currentDir = self.directoryModel_.getCurrentDirEntry(); 2853 var currentDir = self.directoryModel_.getCurrentDirEntry();
2940 var downloadsDir = self.directoryModel_.getRootsList().item(0); 2854 var downloadsDir = self.directoryModel_.getRootsList().item(0);
2941 2855
(...skipping 29 matching lines...) Expand all
2971 }; 2885 };
2972 2886
2973 /** 2887 /**
2974 * Update the breadcrumb display to reflect the current directory. 2888 * Update the breadcrumb display to reflect the current directory.
2975 */ 2889 */
2976 FileManager.prototype.updateBreadcrumbs_ = function() { 2890 FileManager.prototype.updateBreadcrumbs_ = function() {
2977 var bc = this.dialogDom_.querySelector('.breadcrumbs'); 2891 var bc = this.dialogDom_.querySelector('.breadcrumbs');
2978 removeChildren(bc); 2892 removeChildren(bc);
2979 2893
2980 var rootPath = this.directoryModel_.getCurrentRootPath(); 2894 var rootPath = this.directoryModel_.getCurrentRootPath();
2981 var relativePath = this.directoryModel_.getCurrentDirEntry().fullPath. 2895 var relativePath = this.directoryModel_.getCurrentDirPath().
2982 substring(rootPath.length).replace(/\/$/, ''); 2896 substring(rootPath.length).replace(/\/$/, '');
2983 2897
2984 var pathNames = relativePath.replace(/\/$/, '').split('/'); 2898 var pathNames = relativePath.replace(/\/$/, '').split('/');
2985 if (pathNames[0] == '') 2899 if (pathNames[0] == '')
2986 pathNames.splice(0, 1); 2900 pathNames.splice(0, 1);
2987 2901
2988 // We need a first breadcrumb for root, so placing last name from 2902 // We need a first breadcrumb for root, so placing last name from
2989 // rootPath as first name of relativePath. 2903 // rootPath as first name of relativePath.
2990 var rootPathNames = rootPath.replace(/\/$/, '').split('/'); 2904 var rootPathNames = rootPath.replace(/\/$/, '').split('/');
2991 pathNames.splice(0, 0, rootPathNames[rootPathNames.length - 1]); 2905 pathNames.splice(0, 0, rootPathNames[rootPathNames.length - 1]);
(...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after
3143 this.table_.focus(); 3057 this.table_.focus();
3144 else // this.listType_ == FileManager.ListType.THUMBNAIL) 3058 else // this.listType_ == FileManager.ListType.THUMBNAIL)
3145 this.grid_.focus(); 3059 this.grid_.focus();
3146 }; 3060 };
3147 3061
3148 /** 3062 /**
3149 * Return full path of the current directory or null. 3063 * Return full path of the current directory or null.
3150 */ 3064 */
3151 FileManager.prototype.getCurrentDirectory = function() { 3065 FileManager.prototype.getCurrentDirectory = function() {
3152 return this.directoryModel_ && 3066 return this.directoryModel_ &&
3153 this.directoryModel_.getCurrentDirEntry().fullPath; 3067 this.directoryModel_.getCurrentDirPath();
3154 }; 3068 };
3155 3069
3156 /** 3070 /**
3157 * Return URL of the current directory or null. 3071 * Return URL of the current directory or null.
3158 */ 3072 */
3159 FileManager.prototype.getCurrentDirectoryURL = function() { 3073 FileManager.prototype.getCurrentDirectoryURL = function() {
3160 return this.directoryModel_ && 3074 return this.directoryModel_ &&
3161 this.directoryModel_.getCurrentDirEntry().toURL(); 3075 this.directoryModel_.getCurrentDirEntry().toURL();
3162 }; 3076 };
3163 3077
(...skipping 291 matching lines...) Expand 10 before | Expand all | Expand 10 after
3455 3369
3456 /** 3370 /**
3457 * Executes directory action (i.e. changes directory). If new directory is a 3371 * Executes directory action (i.e. changes directory). If new directory is a
3458 * search result directory, we'll have to calculate its real path before we 3372 * search result directory, we'll have to calculate its real path before we
3459 * actually do the operation. 3373 * actually do the operation.
3460 * 3374 *
3461 * @param {DirectoryEntry} entry Directory entry to which directory should be 3375 * @param {DirectoryEntry} entry Directory entry to which directory should be
3462 * changed. 3376 * changed.
3463 */ 3377 */
3464 FileManager.prototype.onDirectoryAction = function(entry) { 3378 FileManager.prototype.onDirectoryAction = function(entry) {
3379 var mountError = this.volumeManager_.getMountError(
3380 DirectoryModel.getRootPath(entry.fullPath));
3381 if (mountError == VolumeManager.Error.UNKNOWN_FILESYSTEM) {
3382 return this.showButter(str('UNKNOWN_FILESYSTEM_WARNING'));
3383 } else if (mountError == VolumeManager.Error.UNSUPPORTED_FILESYSTEM) {
3384 return this.showButter(str('UNSUPPORTED_FILESYSTEM_WARNING'));
3385 }
3465 if (!DirectoryModel.isGDataSearchPath(entry.fullPath)) 3386 if (!DirectoryModel.isGDataSearchPath(entry.fullPath))
3466 return this.directoryModel_.changeDirectory(entry.fullPath); 3387 return this.directoryModel_.changeDirectory(entry.fullPath);
3467 3388
3468 // If we are under gdata search path, the real entries file path may be 3389 // If we are under gdata search path, the real entries file path may be
3469 // different from |entry.fullPath|. 3390 // different from |entry.fullPath|.
3470 var self = this; 3391 var self = this;
3471 chrome.fileBrowserPrivate.getPathForDriveSearchResult(entry.toURL(), 3392 chrome.fileBrowserPrivate.getPathForDriveSearchResult(entry.toURL(),
3472 function(path) { 3393 function(path) {
3473 // |path| may be undefined if there was an error. If that is the case, 3394 // |path| may be undefined if there was an error. If that is the case,
3474 // change to the original file path. 3395 // change to the original file path.
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
3538 * 3459 *
3539 * @param {cr.Event} event The directory-changed event. 3460 * @param {cr.Event} event The directory-changed event.
3540 */ 3461 */
3541 FileManager.prototype.onDirectoryChanged_ = function(event) { 3462 FileManager.prototype.onDirectoryChanged_ = function(event) {
3542 this.updateCommonActionButtons_(); 3463 this.updateCommonActionButtons_();
3543 this.updateOkButton_(); 3464 this.updateOkButton_();
3544 this.updateBreadcrumbs_(); 3465 this.updateBreadcrumbs_();
3545 this.updateColumnModel_(); 3466 this.updateColumnModel_();
3546 this.updateSearchBoxOnDirChange_(); 3467 this.updateSearchBoxOnDirChange_();
3547 3468
3548 // Sometimes we rescan the same directory (when mounting GData lazily first, 3469 this.updateLocation_(event.initial, this.getCurrentDirectory());
3549 // then for real). Do not update the location then.
3550 if (event.newDirEntry.fullPath != event.previousDirEntry.fullPath) {
3551 this.updateLocation_(event.initial, event.newDirEntry.fullPath);
3552 }
3553
3554 this.checkFreeSpace_(this.getCurrentDirectory()); 3470 this.checkFreeSpace_(this.getCurrentDirectory());
3555 3471
3556 this.updateTitle_(); 3472 this.updateTitle_();
3557 3473 this.updateGDataUnmountedPanel_();
3558 if (this.filesystemObserverId_) 3474 if (this.isOnGData())
3559 this.metadataCache_.removeObserver(this.filesystemObserverId_); 3475 this.unmountedPanel_.classList.remove('retry-enabled');
3560 if (this.gdataObserverId_)
3561 this.metadataCache_.removeObserver(this.gdataObserverId_);
3562
3563 this.filesystemObserverId_ = this.metadataCache_.addObserver(
3564 this.directoryModel_.getCurrentDirEntry(),
3565 MetadataCache.CHILDREN,
3566 'filesystem',
3567 this.updateMetadataInUI_.bind(this, 'filesystem'));
3568
3569 if (this.isOnGData()) {
3570 this.gdataObserverId_ = this.metadataCache_.addObserver(
3571 this.directoryModel_.getCurrentDirEntry(),
3572 MetadataCache.CHILDREN,
3573 'gdata',
3574 this.updateMetadataInUI_.bind(this, 'gdata'));
3575 }
3576
3577 var self = this;
3578
3579 if (this.watchedDirectoryUrl_) {
3580 if (this.watchedDirectoryUrl_ != event.previousDirEntry.toURL()) {
3581 console.warn('event.previousDirEntry does not match File Manager state',
3582 event, this.watchedDirectoryUrl_);
3583 }
3584 chrome.fileBrowserPrivate.removeFileWatch(this.watchedDirectoryUrl_,
3585 function(result) {
3586 if (!result) {
3587 console.log('Failed to remove file watch');
3588 }
3589 });
3590 this.watchedDirectoryUrl_ = null;
3591 }
3592
3593 if (event.newDirEntry.fullPath != '/' && !event.newDirEntry.unmounted) {
3594 this.watchedDirectoryUrl_ = event.newDirEntry.toURL();
3595 chrome.fileBrowserPrivate.addFileWatch(this.watchedDirectoryUrl_,
3596 function(result) {
3597 if (!result) {
3598 console.log('Failed to add file watch');
3599 this.watchedDirectoryUrl_ = null;
3600 }
3601 }.bind(this));
3602 }
3603
3604 if (event.newDirEntry.unmounted)
3605 this.dialogContainer_.setAttribute('unmounted', true);
3606 else {
3607 this.dialogContainer_.removeAttribute('unmounted');
3608 // Need to resize explicitly because the list container had display:none.
3609 this.onResize_();
3610 }
3611
3612 if (this.isOnGData()) {
3613 this.dialogContainer_.setAttribute('gdata', true);
3614 if (event.newDirEntry.unmounted) {
3615 if (event.newDirEntry.error)
3616 this.onGDataUnreachable_('File error ' + event.newDirEntry.error);
3617 else
3618 this.initGData_(true /* directory changed */);
3619 }
3620 } else {
3621 this.dialogContainer_.removeAttribute('gdata');
3622 }
3623 }; 3476 };
3624 3477
3625 FileManager.prototype.findListItemForEvent_ = function(event) { 3478 FileManager.prototype.findListItemForEvent_ = function(event) {
3626 return this.findListItemForNode_(event.srcElement); 3479 return this.findListItemForNode_(event.srcElement);
3627 }; 3480 };
3628 3481
3629 FileManager.prototype.findListItemForNode_ = function(node) { 3482 FileManager.prototype.findListItemForNode_ = function(node) {
3630 var item = this.currentList_.getListItemAncestor(node); 3483 var item = this.currentList_.getListItemAncestor(node);
3631 // TODO(serya): list should check that. 3484 // TODO(serya): list should check that.
3632 return item && this.currentList_.isItem(item) ? item : null; 3485 return item && this.currentList_.isItem(item) ? item : null;
3633 }; 3486 };
3634 3487
3635 /** 3488 /**
3636 * Unload handler for the page. May be called manually for the file picker 3489 * Unload handler for the page. May be called manually for the file picker
3637 * dialog, because it closes by calling extension API functions that do not 3490 * dialog, because it closes by calling extension API functions that do not
3638 * return. 3491 * return.
3639 */ 3492 */
3640 FileManager.prototype.onUnload_ = function() { 3493 FileManager.prototype.onUnload_ = function() {
3641 if (this.watchedDirectoryUrl_) { 3494 this.fileWatcher_.stop();
3642 chrome.fileBrowserPrivate.removeFileWatch(
3643 this.watchedDirectoryUrl_,
3644 function(result) {
3645 if (!result) {
3646 console.log('Failed to remove file watch');
3647 }
3648 });
3649 this.watchedDirectoryUrl_ = null;
3650 }
3651 };
3652
3653 FileManager.prototype.onFileChanged_ = function(event) {
3654 // We receive a lot of events even in folders we are not interested in.
3655 if (encodeURI(event.fileUrl) == this.getSearchOrCurrentDirectoryURL())
3656 this.directoryModel_.rescanLater();
3657 }; 3495 };
3658 3496
3659 FileManager.prototype.initiateRename_ = function() { 3497 FileManager.prototype.initiateRename_ = function() {
3660 var item = this.currentList_.ensureLeadItemExists(); 3498 var item = this.currentList_.ensureLeadItemExists();
3661 if (!item) 3499 if (!item)
3662 return; 3500 return;
3663 var label = item.querySelector('.filename-label'); 3501 var label = item.querySelector('.filename-label');
3664 var input = this.renameInput_; 3502 var input = this.renameInput_;
3665 3503
3666 input.value = label.textContent; 3504 input.value = label.textContent;
(...skipping 830 matching lines...) Expand 10 before | Expand all | Expand 10 after
4497 handleSplitterDragEnd: function(e) { 4335 handleSplitterDragEnd: function(e) {
4498 Splitter.prototype.handleSplitterDragEnd.apply(this, arguments); 4336 Splitter.prototype.handleSplitterDragEnd.apply(this, arguments);
4499 this.ownerDocument.documentElement.classList.remove('col-resize'); 4337 this.ownerDocument.documentElement.classList.remove('col-resize');
4500 } 4338 }
4501 }; 4339 };
4502 4340
4503 customSplitter.decorate(splitterElement); 4341 customSplitter.decorate(splitterElement);
4504 }; 4342 };
4505 4343
4506 FileManager.prototype.setupGDataWelcome_ = function() { 4344 FileManager.prototype.setupGDataWelcome_ = function() {
4345 if (this.gdataWelcomeHandler_)
4346 return;
4507 this.gdataWelcomeHandler_ = this.createGDataWelcomeHandler_(); 4347 this.gdataWelcomeHandler_ = this.createGDataWelcomeHandler_();
4508 if (this.gdataWelcomeHandler_) { 4348 if (this.gdataWelcomeHandler_) {
4509 this.directoryModel_.addEventListener('scan-completed', 4349 this.directoryModel_.addEventListener('scan-completed',
4510 this.gdataWelcomeHandler_); 4350 this.gdataWelcomeHandler_);
4511 this.directoryModel_.addEventListener('rescan-completed', 4351 this.directoryModel_.addEventListener('rescan-completed',
4512 this.gdataWelcomeHandler_); 4352 this.gdataWelcomeHandler_);
4513 } 4353 }
4514 }; 4354 };
4515 4355
4516 FileManager.prototype.cleanupGDataWelcome_ = function() { 4356 FileManager.prototype.cleanupGDataWelcome_ = function() {
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
4626 4466
4627 function closeBanner() { 4467 function closeBanner() {
4628 self.cleanupGDataWelcome_(); 4468 self.cleanupGDataWelcome_();
4629 // Stop showing the welcome banner. 4469 // Stop showing the welcome banner.
4630 localStorage[WELCOME_HEADER_COUNTER_KEY] = WELCOME_HEADER_COUNTER_LIMIT; 4470 localStorage[WELCOME_HEADER_COUNTER_KEY] = WELCOME_HEADER_COUNTER_LIMIT;
4631 } 4471 }
4632 4472
4633 return maybeShowBanner; 4473 return maybeShowBanner;
4634 }; 4474 };
4635 })(); 4475 })();
4476
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698