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

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

Issue 7745051: Added refresh on filesystem change (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: Rollback one file Created 9 years, 3 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) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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 // Setting the src of an img to an empty string can crash the browser, so we 5 // Setting the src of an img to an empty string can crash the browser, so we
6 // use an empty 1x1 gif instead. 6 // use an empty 1x1 gif instead.
7 const EMPTY_IMAGE_URI = 'data:image/gif;base64,' 7 const EMPTY_IMAGE_URI = 'data:image/gif;base64,'
8 + 'R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw%3D%3D'; 8 + 'R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw%3D%3D';
9 9
10 var g_slideshow_data = null; 10 var g_slideshow_data = null;
11 11
12 const IMAGE_EDITOR_ENABLED = false; 12 const IMAGE_EDITOR_ENABLED = false;
13 13
14 // If directory files changes too often, don't rescan directory more than once
15 // per specified interva;
rginda 2011/08/31 00:32:58 typo: s/;/l./
Dmitry Zvorygin 2011/09/12 11:12:23 Done.
16 const SIMULTANEOUS_RESCAN_INTERVAL = 1000;
17
14 /** 18 /**
15 * FileManager constructor. 19 * FileManager constructor.
16 * 20 *
17 * FileManager objects encapsulate the functionality of the file selector 21 * FileManager objects encapsulate the functionality of the file selector
18 * dialogs, as well as the full screen file manager application (though the 22 * dialogs, as well as the full screen file manager application (though the
19 * latter is not yet implemented). 23 * latter is not yet implemented).
20 * 24 *
21 * @param {HTMLElement} dialogDom The DOM node containing the prototypical 25 * @param {HTMLElement} dialogDom The DOM node containing the prototypical
22 * dialog UI. 26 * dialog UI.
23 * @param {DOMFileSystem} filesystem The HTML5 filesystem object representing 27 * @param {DOMFileSystem} filesystem The HTML5 filesystem object representing
(...skipping 21 matching lines...) Expand all
45 49
46 this.selection = null; 50 this.selection = null;
47 51
48 this.clipboard_ = null; // Current clipboard, or null if empty. 52 this.clipboard_ = null; // Current clipboard, or null if empty.
49 53
50 this.butterTimer_ = null; 54 this.butterTimer_ = null;
51 this.currentButter_ = null; 55 this.currentButter_ = null;
52 56
53 // True if we should filter out files that start with a dot. 57 // True if we should filter out files that start with a dot.
54 this.filterFiles_ = true; 58 this.filterFiles_ = true;
59 this.subscribedOnDirectoryChanges_ = false;
60 this.rescanPending_ = [];
rginda 2011/08/31 00:32:58 rescanPending_ and rescanRunning_ are *very* simil
Dmitry Zvorygin 2011/09/12 11:12:23 We need both array and flag because we empty array
61 this.rescanRunning_ = false;
55 62
56 this.commands_ = {}; 63 this.commands_ = {};
57 64
58 this.document_ = dialogDom.ownerDocument; 65 this.document_ = dialogDom.ownerDocument;
59 this.dialogType_ = this.params_.type || FileManager.DialogType.FULL_PAGE; 66 this.dialogType_ = this.params_.type || FileManager.DialogType.FULL_PAGE;
60 67
61 this.alert = new cr.ui.dialogs.AlertDialog(this.dialogDom_); 68 this.alert = new cr.ui.dialogs.AlertDialog(this.dialogDom_);
62 this.confirm = new cr.ui.dialogs.ConfirmDialog(this.dialogDom_); 69 this.confirm = new cr.ui.dialogs.ConfirmDialog(this.dialogDom_);
63 this.prompt = new cr.ui.dialogs.PromptDialog(this.dialogDom_); 70 this.prompt = new cr.ui.dialogs.PromptDialog(this.dialogDom_);
64 71
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
101 this.dialogType_ == FileManager.DialogType.SELECT_OPEN_MULTI_FILE); 108 this.dialogType_ == FileManager.DialogType.SELECT_OPEN_MULTI_FILE);
102 109
103 // DirectoryEntry representing the current directory of the dialog. 110 // DirectoryEntry representing the current directory of the dialog.
104 this.currentDirEntry_ = null; 111 this.currentDirEntry_ = null;
105 112
106 this.copyManager_ = new FileCopyManager(); 113 this.copyManager_ = new FileCopyManager();
107 this.copyManager_.addEventListener('copy-progress', 114 this.copyManager_.addEventListener('copy-progress',
108 this.onCopyProgress_.bind(this)); 115 this.onCopyProgress_.bind(this));
109 116
110 window.addEventListener('popstate', this.onPopState_.bind(this)); 117 window.addEventListener('popstate', this.onPopState_.bind(this));
118 window.addEventListener('unload', this.onUnload_.bind(this));
119
111 this.addEventListener('directory-changed', 120 this.addEventListener('directory-changed',
112 this.onDirectoryChanged_.bind(this)); 121 this.onDirectoryChanged_.bind(this));
113 this.addEventListener('selection-summarized', 122 this.addEventListener('selection-summarized',
114 this.onSelectionSummarized_.bind(this)); 123 this.onSelectionSummarized_.bind(this));
115 124
116 // The list of archives requested to mount. We will show contents once 125 // The list of archives requested to mount. We will show contents once
117 // archive is mounted, but only for mounts from within this filebrowser tab. 126 // archive is mounted, but only for mounts from within this filebrowser tab.
118 this.mountRequests_ = []; 127 this.mountRequests_ = [];
119 chrome.fileBrowserPrivate.onMountCompleted.addListener( 128 chrome.fileBrowserPrivate.onMountCompleted.addListener(
120 this.onMountCompleted_.bind(this)); 129 this.onMountCompleted_.bind(this));
121 130
131 chrome.fileBrowserPrivate.onFileChanged.addListener(
132 this.onFileChanged_.bind(this));
133
122 var self = this; 134 var self = this;
123 135
124 // The list of active mount points to distinct them from other directories. 136 // The list of active mount points to distinct them from other directories.
125 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { 137 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) {
126 self.mountPoints_ = mountPoints; 138 self.mountPoints_ = mountPoints;
127 }); 139 });
128 140
129 chrome.fileBrowserHandler.onExecute.addListener( 141 chrome.fileBrowserHandler.onExecute.addListener(
130 this.onFileTaskExecute_.bind(this)); 142 this.onFileTaskExecute_.bind(this));
131 143
(...skipping 2206 matching lines...) Expand 10 before | Expand all | Expand 10 after
2338 2350
2339 this.updateCommands_(); 2351 this.updateCommands_();
2340 this.updateOkButton_(); 2352 this.updateOkButton_();
2341 2353
2342 // New folder should never be enabled in the root or media/ directories. 2354 // New folder should never be enabled in the root or media/ directories.
2343 this.newFolderButton_.disabled = isSystemDirEntry(this.currentDirEntry_); 2355 this.newFolderButton_.disabled = isSystemDirEntry(this.currentDirEntry_);
2344 2356
2345 this.document_.title = this.currentDirEntry_.fullPath; 2357 this.document_.title = this.currentDirEntry_.fullPath;
2346 2358
2347 var self = this; 2359 var self = this;
2360
2361 if (this.subscribedOnDirectoryChanges_) {
2362 chrome.fileBrowserPrivate.removeFileWatch(event.previousDirEntry.toURL(),
2363 function(result) {
2364 if (!result) {
2365 console.log('Failed to remove file watch');
2366 }
2367 });
2368 }
2369
2370 this.subscribedOnDirectoryChanges_ = true;
2371 chrome.fileBrowserPrivate.addFileWatch(event.newDirEntry.toURL(),
2372 function(result) {
2373 if (!result) {
2374 console.log('Failed to add file watch');
2375 }
2376 });
2377
2348 this.rescanDirectory_(function() { 2378 this.rescanDirectory_(function() {
2349 if (event.selectedEntry) 2379 if (event.selectedEntry)
2350 self.selectEntry(event.selectedEntry); 2380 self.selectEntry(event.selectedEntry);
2351 // For tests that open the dialog to empty directories, everything 2381 // For tests that open the dialog to empty directories, everything
2352 // is loaded at this point. 2382 // is loaded at this point.
2353 chrome.test.sendMessage('directory-change-complete'); 2383 chrome.test.sendMessage('directory-change-complete');
2354 }); 2384 });
2355 }; 2385 };
2356 2386
2357 /** 2387 /**
2358 * Update the UI when a disk is mounted or unmounted. 2388 * Update the UI when a disk is mounted or unmounted.
2359 * 2389 *
2360 * @param {string} path The path that has been mounted or unmounted. 2390 * @param {string} path The path that has been mounted or unmounted.
2361 */ 2391 */
2362 FileManager.prototype.onDiskChanged_ = function(event) { 2392 FileManager.prototype.onDiskChanged_ = function(event) {
2363 if (event.eventType == 'added') { 2393 if (event.eventType == 'added') {
2364 this.changeDirectory(event.volumeInfo.mountPath); 2394 this.changeDirectory(event.volumeInfo.mountPath);
2365 } else if (event.eventType == 'removed') { 2395 } else if (event.eventType == 'removed') {
2366 if (this.currentDirEntry_ && 2396 if (this.currentDirEntry_ &&
2367 isParentPath(event.volumeInfo.mountPath, 2397 isParentPath(event.volumeInfo.mountPath,
2368 this.currentDirEntry_.fullPath)) { 2398 this.currentDirEntry_.fullPath)) {
2369 this.changeDirectory(getParentPath(event.volumeInfo.mountPath)); 2399 this.changeDirectory(getParentPath(event.volumeInfo.mountPath));
2370 } 2400 }
2371 } 2401 }
2372 }; 2402 };
2373 2403
2374 /** 2404 /**
2375 * Rescan the current directory, refreshing the list. 2405 * Rescan the current directory, refreshing the list.
2376 * 2406 *
rginda 2011/08/31 00:32:58 Please add some description of the new queuing nat
Dmitry Zvorygin 2011/09/12 11:12:23 Done.
2377 * @param {function()} opt_callback Optional function to invoke when the 2407 * @param {function()} opt_callback Optional function to invoke when the
2378 * rescan is complete. 2408 * rescan is complete.
2409 *
2410 * @param {function()} opt_onError Optional function to invoke when the
2411 * rescan fails.
2379 */ 2412 */
2380 FileManager.prototype.rescanDirectory_ = function(opt_callback) { 2413 FileManager.prototype.rescanDirectory_ = function(opt_callback, opt_onError) {
2381 var self = this;
2382 var reader;
2383
2384 function onReadSome(entries) {
2385 if (entries.length == 0) {
2386 if (opt_callback)
2387 opt_callback();
2388 return;
2389 }
2390
2391 // Splice takes the to-be-spliced-in array as individual parameters,
2392 // rather than as an array, so we need to perform some acrobatics...
2393 var spliceArgs = [].slice.call(entries);
2394
2395 // Hide files that start with a dot ('.').
2396 // TODO(rginda): User should be able to override this. Support for other
2397 // commonly hidden patterns might be nice too.
2398 if (self.filterFiles_) {
2399 spliceArgs = spliceArgs.filter(function(e) {
2400 return e.name.substr(0, 1) != '.';
2401 });
2402 }
2403
2404 spliceArgs.unshift(0, 0); // index, deleteCount
2405 self.dataModel_.splice.apply(self.dataModel_, spliceArgs);
2406
2407 // Keep reading until entries.length is 0.
2408 reader.readEntries(onReadSome);
2409 };
2410
2411 // Updated when a user clicks on the label of a file, used to detect 2414 // Updated when a user clicks on the label of a file, used to detect
2412 // when a click is eligible to trigger a rename. Can be null, or 2415 // when a click is eligible to trigger a rename. Can be null, or
2413 // an object with 'path' and 'date' properties. 2416 // an object with 'path' and 'date' properties.
2414 this.lastLabelClick_ = null; 2417 this.lastLabelClick_ = null;
2415 2418
2416 // Clear the table first. 2419 // Clear the table first.
2417 this.dataModel_.splice(0, this.dataModel_.length); 2420 this.dataModel_.splice(0, this.dataModel_.length);
2418 this.currentList_.selectionModel.clear(); 2421 this.currentList_.selectionModel.clear();
2419 2422
2420 this.updateBreadcrumbs_(); 2423 this.updateBreadcrumbs_();
2421 2424
2422 if (this.currentDirEntry_.fullPath != '/') { 2425 if (this.currentDirEntry_.fullPath != '/') {
2426 // Add current request to pending result list
rginda 2011/08/31 00:32:58 rescanDirectory_ is getting pretty large, can this
Dmitry Zvorygin 2011/09/12 11:12:23 I think it's little useless because that function
2427 this.rescanPending_.push({onSuccess:opt_callback, onError:opt_onError});
2428
2429 if (this.rescanRunning_) {
rginda 2011/08/31 00:32:58 No braces necessary here.
Dmitry Zvorygin 2011/09/12 11:12:23 Done.
2430 return;
2431 }
2432
2433 this.rescanRunning_ = true;
2434
2435 // Save list of items to notify locally in case we'll receive new requests
rginda 2011/08/31 00:32:58 I don't think this comment is enough to describe w
Dmitry Zvorygin 2011/09/12 11:12:23 Done.
2436 // to refresh while refreshing
2437 var callbacks = this.rescanPending_;
2438
2439 this.rescanPending_ = [];
2440
2441 var self = this;
2442 var reader;
2443
2444 function onError() {
2445
2446 for (var i= 0; i < callbacks.length; i++) {
2447 if (callbacks[i].onError) {
rginda 2011/08/31 00:32:58 No braces.
Dmitry Zvorygin 2011/09/12 11:12:23 Done.
2448 callbacks[i].onError();
rginda 2011/08/31 00:32:58 If any of these callbacks throw, rescanRunning wil
Dmitry Zvorygin 2011/09/12 11:12:23 Done.
2449 }
2450 }
2451
2452 if (self.rescanPending_.length > 0) {
2453 setTimeout(self.rescanDirectory_.bind(self),
2454 SIMULTANEOUS_RESCAN_INTERVAL);
2455 }
2456
2457 self.rescanRunning_ = false;
2458 }
2459
2460 function onReadSome(entries) {
2461 if (entries.length == 0) {
2462
2463 for (var i= 0; i < callbacks.length; i++) {
2464 if (callbacks[i].onSuccess) {
2465 callbacks[i].onSuccess();
rginda 2011/08/31 00:32:58 Same issue with failed callbacks here. Also, unne
Dmitry Zvorygin 2011/09/12 11:12:23 Done.
2466 }
2467 }
2468
2469 if (self.rescanPending_.length > 0) {
2470 setTimeout(self.rescanDirectory_.bind(self),
2471 SIMULTANEOUS_RESCAN_INTERVAL);
2472 }
2473
2474 self.rescanRunning_ = false;
2475 return;
2476 }
2477
2478 // Splice takes the to-be-spliced-in array as individual parameters,
2479 // rather than as an array, so we need to perform some acrobatics...
2480 var spliceArgs = [].slice.call(entries);
2481
2482 // Hide files that start with a dot ('.').
2483 // TODO(rginda): User should be able to override this. Support for other
2484 // commonly hidden patterns might be nice too.
2485 if (self.filterFiles_) {
2486 spliceArgs = spliceArgs.filter(function(e) {
2487 return e.name.substr(0, 1) != '.';
2488 });
2489 }
2490
2491 spliceArgs.unshift(0, 0); // index, deleteCount
2492 self.dataModel_.splice.apply(self.dataModel_, spliceArgs);
2493
2494 // Keep reading until entries.length is 0.
2495 reader.readEntries(onReadSome, onError);
2496 };
2497
2423 // If not the root directory, just read the contents. 2498 // If not the root directory, just read the contents.
2424 reader = this.currentDirEntry_.createReader(); 2499 reader = this.currentDirEntry_.createReader();
2425 reader.readEntries(onReadSome); 2500 reader.readEntries(onReadSome, onError);
2426 return; 2501 return;
2427 } 2502 }
2428 2503
2429 // Otherwise, use the provided list of root subdirectories, since the 2504 // Otherwise, use the provided list of root subdirectories, since the
2430 // real local filesystem root directory (the one we use outside the 2505 // real local filesystem root directory (the one we use outside the
2431 // harness) can't be enumerated yet. 2506 // harness) can't be enumerated yet.
2432 var spliceArgs = [].slice.call(this.rootEntries_); 2507 var spliceArgs = [].slice.call(this.rootEntries_);
2433 spliceArgs.unshift(0, 0); // index, deleteCount 2508 spliceArgs.unshift(0, 0); // index, deleteCount
2434 self.dataModel_.splice.apply(self.dataModel_, spliceArgs); 2509 self.dataModel_.splice.apply(self.dataModel_, spliceArgs);
2435 2510
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
2476 if (event.button != 1) 2551 if (event.button != 1)
2477 return; 2552 return;
2478 2553
2479 var li = this.findListItem_(event); 2554 var li = this.findListItem_(event);
2480 if (!li) { 2555 if (!li) {
2481 console.log('li not found', event); 2556 console.log('li not found', event);
2482 return; 2557 return;
2483 } 2558 }
2484 }; 2559 };
2485 2560
2561 FileManager.prototype.onUnload_ = function(event) {
2562 if (this.subscribedOnDirectoryChanges_) {
2563 chrome.fileBrowserPrivate.removeFileWatch(event.previousDirEntry.toURL(),
2564 function(result) {
2565 if (!result) {
2566 console.log('Failed to remove file watch');
2567 }
2568 });
2569 }
2570 };
2571
2572 FileManager.prototype.onFileChanged_ = function(event) {
2573 // It might be massive change, so let's note somehow, that we need
rginda 2011/08/31 00:32:58 How about just moving this code into a 'rescanDire
2574 // rescanning and then wait some time
2575
2576 if (this.rescanPending_.length == 0) {
2577 this.rescanPending_.push({onSuccess:undefined, onError:undefined});
2578
2579 // If rescan isn't going to run without
2580 // our interruption, then say that we need to run it
2581 if (!this.rescanRunning_) {
2582 setTimeout(this.rescanDirectory_.bind(this),
2583 SIMULTANEOUS_RESCAN_INTERVAL);
2584 }
2585 }
2586 };
2587
2486 /** 2588 /**
2487 * Determine whether or not a click should initiate a rename. 2589 * Determine whether or not a click should initiate a rename.
2488 * 2590 *
2489 * Renames can happen on mouse click if the user clicks on a label twice, 2591 * Renames can happen on mouse click if the user clicks on a label twice,
2490 * at least a half second apart. 2592 * at least a half second apart.
2491 */ 2593 */
2492 FileManager.prototype.allowRenameClick_ = function(event, row) { 2594 FileManager.prototype.allowRenameClick_ = function(event, row) {
2493 if (this.dialogType_ != FileManager.DialogType.FULL_PAGE || 2595 if (this.dialogType_ != FileManager.DialogType.FULL_PAGE ||
2494 this.currentDirEntry_.name == '' || 2596 this.currentDirEntry_.name == '' ||
2495 isSystemDirEntry(this.currentDirEntry_)) { 2597 isSystemDirEntry(this.currentDirEntry_)) {
(...skipping 499 matching lines...) Expand 10 before | Expand all | Expand 10 after
2995 3097
2996 if (msg) { 3098 if (msg) {
2997 console.log('no no no'); 3099 console.log('no no no');
2998 this.alert.show(msg, onAccept); 3100 this.alert.show(msg, onAccept);
2999 return false; 3101 return false;
3000 } 3102 }
3001 3103
3002 return true; 3104 return true;
3003 }; 3105 };
3004 })(); 3106 })();
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698