Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / ui / file_manager / file_manager / foreground / js / file_manager.js
index c48491c..3f3597f 100644 (file)
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-'use strict';
-
 /**
  * FileManager constructor.
  *
  * latter is not yet implemented).
  *
  * @constructor
+ * @struct
  */
 function FileManager() {
-  this.initializeQueue_ = new AsyncUtil.Group();
+  // --------------------------------------------------------------------------
+  // Services FileManager depends on.
+
+  /**
+   * Volume manager.
+   * @type {VolumeManagerWrapper}
+   * @private
+   */
+  this.volumeManager_ = null;
+
+  /**
+   * Metadata cache.
+   * @type {MetadataCache}
+   * @private
+   */
+  this.metadataCache_ = null;
+
+  /**
+   * File operation manager.
+   * @type {FileOperationManager}
+   * @private
+   */
+  this.fileOperationManager_ = null;
+
+  /**
+   * File transfer controller.
+   * @type {FileTransferController}
+   * @private
+   */
+  this.fileTransferController_ = null;
+
+  /**
+   * File filter.
+   * @type {FileFilter}
+   * @private
+   */
+  this.fileFilter_ = null;
+
+  /**
+   * File watcher.
+   * @type {FileWatcher}
+   * @private
+   */
+  this.fileWatcher_ = null;
+
+  /**
+   * Model of current directory.
+   * @type {DirectoryModel}
+   * @private
+   */
+  this.directoryModel_ = null;
+
+  /**
+   * Model of folder shortcuts.
+   * @type {FolderShortcutsDataModel}
+   * @private
+   */
+  this.folderShortcutsModel_ = null;
+
+  /**
+   * VolumeInfo of the current volume.
+   * @type {VolumeInfo}
+   * @private
+   */
+  this.currentVolumeInfo_ = null;
+
+  /**
+   * Handler for command events.
+   * @type {CommandHandler}
+   */
+  this.commandHandler = null;
+
+  /**
+   * Handler for the change of file selection.
+   * @type {FileSelectionHandler}
+   * @private
+   */
+  this.selectionHandler_ = null;
+
+  /**
+   * Dialog action controller.
+   * @type {DialogActionController}
+   * @private
+   */
+  this.dialogActionController_ = null;
+
+  // --------------------------------------------------------------------------
+  // Parameters determining the type of file manager.
+
+  /**
+   * Dialog type of this window.
+   * @type {DialogType}
+   */
+  this.dialogType = DialogType.FULL_PAGE;
+
+  /**
+   * List of acceptable file types for open dialog.
+   * @type {!Array.<Object>}
+   * @private
+   */
+  this.fileTypes_ = [];
+
+  /**
+   * Startup parameters for this application.
+   * @type {?{includeAllFiles:boolean,
+   *          action:string,
+   *          shouldReturnLocalPath:boolean}}
+   * @private
+   */
+  this.params_ = null;
+
+  /**
+   * Startup preference about the view.
+   * @type {Object}
+   * @private
+   */
+  this.viewOptions_ = {};
+
+  /**
+   * The user preference.
+   * @type {Object}
+   * @private
+   */
+  this.preferences_ = null;
+
+  // --------------------------------------------------------------------------
+  // UI components.
+
+  /**
+   * UI management class of file manager.
+   * @type {FileManagerUI}
+   * @private
+   */
+  this.ui_ = null;
+
+  /**
+   * Progress center panel.
+   * @type {ProgressCenterPanel}
+   * @private
+   */
+  this.progressCenterPanel_ = null;
+
+  /**
+   * Directory tree.
+   * @type {DirectoryTree}
+   * @private
+   */
+  this.directoryTree_ = null;
+
+  /**
+   * Naming controller.
+   * @type {NamingController}
+   * @private
+   */
+  this.namingController_ = null;
+
+  /**
+   * Controller for search UI.
+   * @type {SearchController}
+   * @private
+   */
+  this.searchController_ = null;
+
+  /**
+   * Controller for directory scan.
+   * @type {ScanController}
+   * @private
+   */
+  this.scanController_ = null;
+
+  /**
+   * Controller for spinner.
+   * @type {SpinnerController}
+   * @private
+   */
+  this.spinnerController_ = null;
+
+  /**
+   * Banners in the file list.
+   * @type {FileListBannerController}
+   * @private
+   */
+  this.bannersController_ = null;
+
+  // --------------------------------------------------------------------------
+  // Dialogs.
+
+  /**
+   * Error dialog.
+   * @type {ErrorDialog}
+   */
+  this.error = null;
+
+  /**
+   * Alert dialog.
+   * @type {cr.ui.dialogs.AlertDialog}
+   */
+  this.alert = null;
+
+  /**
+   * Confirm dialog.
+   * @type {cr.ui.dialogs.ConfirmDialog}
+   */
+  this.confirm = null;
+
+  /**
+   * Prompt dialog.
+   * @type {cr.ui.dialogs.PromptDialog}
+   */
+  this.prompt = null;
+
+  /**
+   * Share dialog.
+   * @type {ShareDialog}
+   * @private
+   */
+  this.shareDialog_ = null;
+
+  /**
+   * Default task picker.
+   * @type {cr.filebrowser.DefaultActionDialog}
+   */
+  this.defaultTaskPicker = null;
+
+  /**
+   * Suggest apps dialog.
+   * @type {SuggestAppsDialog}
+   */
+  this.suggestAppsDialog = null;
+
+  // --------------------------------------------------------------------------
+  // Menus.
+
+  /**
+   * Context menu for texts.
+   * @type {cr.ui.Menu}
+   * @private
+   */
+  this.textContextMenu_ = null;
+
+  // --------------------------------------------------------------------------
+  // DOM elements.
+
+  /**
+   * Background page.
+   * @type {BackgroundWindow}
+   * @private
+   */
+  this.backgroundPage_ = null;
+
+  /**
+   * The root DOM element of this app.
+   * @type {HTMLBodyElement}
+   * @private
+   */
+  this.dialogDom_ = null;
+
+  /**
+   * The document object of this app.
+   * @type {HTMLDocument}
+   * @private
+   */
+  this.document_ = null;
+
+  /**
+   * The menu item to toggle "Do not use mobile data for sync".
+   * @type {HTMLMenuItemElement}
+   */
+  this.syncButton = null;
+
+  /**
+   * The menu item to toggle "Show Google Docs files".
+   * @type {HTMLMenuItemElement}
+   */
+  this.hostedButton = null;
+
+  /**
+   * The menu item for doing an action.
+   * @type {HTMLMenuItemElement}
+   * @private
+   */
+  this.actionMenuItem_ = null;
+
+  /**
+   * The button to open gear menu.
+   * @type {cr.ui.MenuButton}
+   * @private
+   */
+  this.gearButton_ = null;
+
+  /**
+   * The combo button to specify the task.
+   * @type {HTMLButtonElement}
+   * @private
+   */
+  this.taskItems_ = null;
+
+  /**
+   * The container element of the dialog.
+   * @type {HTMLDivElement}
+   * @private
+   */
+  this.dialogContainer_ = null;
+
+  /**
+   * Open-with command in the context menu.
+   * @type {cr.ui.Command}
+   * @private
+   */
+  this.openWithCommand_ = null;
+
+  // --------------------------------------------------------------------------
+  // Bound functions.
+
+  /**
+   * Bound function for onCopyProgress_.
+   * @type {?function(this:FileManager, Event)}
+   * @private
+   */
+  this.onCopyProgressBound_ = null;
+
+  /**
+   * Bound function for onEntriesChanged_.
+   * @type {?function(this:FileManager, Event)}
+   * @private
+   */
+  this.onEntriesChangedBound_ = null;
+
+  // --------------------------------------------------------------------------
+  // Miscellaneous FileManager's states.
 
   /**
-   * Current list type.
-   * @type {ListType}
+   * Queue for ordering FileManager's initialization process.
+   * @type {AsyncUtil.Group}
    * @private
    */
-  this.listType_ = null;
+  this.initializeQueue_ = new AsyncUtil.Group();
 
   /**
    * True while a user is pressing <Tab>.
@@ -36,7 +364,7 @@ function FileManager() {
    * True while a user is pressing <Ctrl>.
    *
    * TODO(fukino): This key is used only for controlling gear menu, so it
-   * shoudl be moved to GearMenu class. crbug.com/366032.
+   * should be moved to GearMenu class. crbug.com/366032.
    *
    * @type {boolean}
    * @private
@@ -55,40 +383,113 @@ function FileManager() {
   this.isSecretGearMenuShown_ = false;
 
   /**
-   * SelectionHandler.
-   * @type {SelectionHandler}
+   * The last clicked item in the file list.
+   * @type {HTMLLIElement}
    * @private
    */
-  this.selectionHandler_ = null;
+  this.lastClickedItem_ = null;
 
   /**
-   * VolumeInfo of the current volume.
-   * @type {VolumeInfo}
+   * Count of the SourceNotFound error.
+   * @type {number}
    * @private
    */
-  this.currentVolumeInfo_ = null;
+  this.sourceNotFoundErrorCount_ = 0;
+
+  /**
+   * Whether the app should be closed on unmount.
+   * @type {boolean}
+   * @private
+   */
+  this.closeOnUnmount_ = false;
+
+  /**
+   * The key for storing startup preference.
+   * @type {string}
+   * @private
+   */
+  this.startupPrefName_ = '';
+
+  /**
+   * URL of directory which should be initial current directory.
+   * @type {string}
+   * @private
+   */
+  this.initCurrentDirectoryURL_ = '';
+
+  /**
+   * URL of entry which should be initially selected.
+   * @type {string}
+   * @private
+   */
+  this.initSelectionURL_ = '';
+
+  /**
+   * The name of target entry (not URL).
+   * @type {string}
+   * @private
+   */
+  this.initTargetName_ = '';
+
+
+  // Object.seal() has big performance/memory overhead for now, so we use
+  // Object.preventExtensions() here. crbug.com/412239.
+  Object.preventExtensions(this);
 }
 
-FileManager.prototype = {
+FileManager.prototype = /** @struct */ {
   __proto__: cr.EventTarget.prototype,
+  /**
+   * @return {DirectoryModel}
+   */
   get directoryModel() {
     return this.directoryModel_;
   },
-  get navigationList() {
-    return this.navigationList_;
+  /**
+   * @return {DirectoryTree}
+   */
+  get directoryTree() {
+    return this.directoryTree_;
   },
+  /**
+   * @return {HTMLDocument}
+   */
   get document() {
     return this.document_;
   },
+  /**
+   * @return {FileTransferController}
+   */
   get fileTransferController() {
     return this.fileTransferController_;
   },
+  /**
+   * @return {NamingController}
+   */
+  get namingController() {
+    return this.namingController_;
+  },
+  /**
+   * @return {FileOperationManager}
+   */
+  get fileOperationManager() {
+    return this.fileOperationManager_;
+  },
+  /**
+   * @return {BackgroundWindow}
+   */
   get backgroundPage() {
     return this.backgroundPage_;
   },
+  /**
+   * @return {VolumeManagerWrapper}
+   */
   get volumeManager() {
     return this.volumeManager_;
   },
+  /**
+   * @return {FileManagerUI}
+   */
   get ui() {
     return this.ui_;
   }
@@ -101,6 +502,7 @@ FileManager.prototype = {
  * FULL_PAGE which is specific to this code.
  *
  * @enum {string}
+ * @const
  */
 var DialogType = {
   SELECT_FOLDER: 'folder',
@@ -112,7 +514,7 @@ var DialogType = {
 };
 
 /**
- * @param {string} type Dialog type.
+ * @param {DialogType} type Dialog type.
  * @return {boolean} Whether the type is modal.
  */
 DialogType.isModal = function(type) {
@@ -124,7 +526,7 @@ DialogType.isModal = function(type) {
 };
 
 /**
- * @param {string} type Dialog type.
+ * @param {DialogType} type Dialog type.
  * @return {boolean} Whether the type is open dialog.
  */
 DialogType.isOpenDialog = function(type) {
@@ -135,7 +537,16 @@ DialogType.isOpenDialog = function(type) {
 };
 
 /**
- * @param {string} type Dialog type.
+ * @param {DialogType} type Dialog type.
+ * @return {boolean} Whether the type is open dialog for file(s).
+ */
+DialogType.isOpenFileDialog = function(type) {
+  return type == DialogType.SELECT_OPEN_FILE ||
+         type == DialogType.SELECT_OPEN_MULTI_FILE;
+};
+
+/**
+ * @param {DialogType} type Dialog type.
  * @return {boolean} Whether the type is folder selection dialog.
  */
 DialogType.isFolderDialog = function(type) {
@@ -143,6 +554,8 @@ DialogType.isFolderDialog = function(type) {
          type == DialogType.SELECT_UPLOAD_FOLDER;
 };
 
+Object.freeze(DialogType);
+
 /**
  * Bottom margin of the list and tree for transparent preview panel.
  * @const
@@ -151,7 +564,6 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
 
 // Anonymous "namespace".
 (function() {
-
   // Private variables and helper functions.
 
   /**
@@ -167,14 +579,17 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
   var DOUBLE_CLICK_TIMEOUT = 200;
 
   /**
-   * Update the element to display the information about remaining space for
+   * Updates the element to display the information about remaining space for
    * the storage.
+   *
+   * @param {!Object<string, number>} sizeStatsResult Map containing remaining
+   *     space information.
    * @param {!Element} spaceInnerBar Block element for a percentage bar
-   *                                 representing the remaining space.
+   *     representing the remaining space.
    * @param {!Element} spaceInfoLabel Inline element to contain the message.
    * @param {!Element} spaceOuterBar Block element around the percentage bar.
    */
-   var updateSpaceInfo = function(
+  var updateSpaceInfo = function(
       sizeStatsResult, spaceInnerBar, spaceInfoLabel, spaceOuterBar) {
     spaceInnerBar.removeAttribute('pending');
     if (sizeStatsResult) {
@@ -193,13 +608,6 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
     }
   };
 
-  // Public statics.
-
-  FileManager.ListType = {
-    DETAIL: 'detail',
-    THUMBNAIL: 'thumb'
-  };
-
   FileManager.prototype.initPreferences_ = function(callback) {
     var group = new AsyncUtil.Group();
 
@@ -208,9 +616,13 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
     group.add(this.getPreferences_.bind(this));
 
     // Get startup preferences.
-    this.viewOptions_ = {};
     group.add(function(done) {
-      util.platform.getPreference(this.startupPrefName_, function(value) {
+      chrome.storage.local.get(this.startupPrefName_, function(values) {
+        var value = values[this.startupPrefName_];
+        if (!value) {
+          done();
+          return;
+        }
         // Load the global default options.
         try {
           this.viewOptions_ = JSON.parse(value);
@@ -236,15 +648,14 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
    * @private
    */
   FileManager.prototype.initFileSystemUI_ = function(callback) {
-    this.table_.startBatchUpdates();
-    this.grid_.startBatchUpdates();
+    this.ui_.listContainer.startBatchUpdates();
 
     this.initFileList_();
     this.setupCurrentDirectory_();
 
     // PyAuto tests monitor this state by polling this variable
     this.__defineGetter__('workerInitialized_', function() {
-       return this.metadataCache_.isInitialized();
+      return this.metadataCache_.isInitialized();
     }.bind(this));
 
     this.initDateTimeFormatters_();
@@ -255,7 +666,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
     // FileListBannerController.
     this.getPreferences_(function(pref) {
       /** @type {boolean} */
-      var showOffers = pref['allowRedeemOffers'];
+      var showOffers = !!pref['allowRedeemOffers'];
       self.bannersController_ = new FileListBannerController(
           self.directoryModel_, self.volumeManager_, self.document_,
           showOffers);
@@ -266,20 +677,32 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
     var dm = this.directoryModel_;
     dm.addEventListener('directory-changed',
                         this.onDirectoryChanged_.bind(this));
+
+    var listBeingUpdated = null;
     dm.addEventListener('begin-update-files', function() {
-      self.currentList_.startBatchUpdates();
+      self.ui_.listContainer.currentList.startBatchUpdates();
+      // Remember the list which was used when updating files started, so
+      // endBatchUpdates() is called on the same list.
+      listBeingUpdated = self.ui_.listContainer.currentList;
     });
     dm.addEventListener('end-update-files', function() {
-      self.restoreItemBeingRenamed_();
-      self.currentList_.endBatchUpdates();
+      self.namingController_.restoreItemBeingRenamed();
+      listBeingUpdated.endBatchUpdates();
+      listBeingUpdated = null;
     });
-    dm.addEventListener('scan-started', this.onScanStarted_.bind(this));
-    dm.addEventListener('scan-completed', this.onScanCompleted_.bind(this));
-    dm.addEventListener('scan-failed', this.onScanCancelled_.bind(this));
-    dm.addEventListener('scan-cancelled', this.onScanCancelled_.bind(this));
-    dm.addEventListener('scan-updated', this.onScanUpdated_.bind(this));
-    dm.addEventListener('rescan-completed',
-                        this.onRescanCompleted_.bind(this));
+
+    this.initContextMenus_();
+    this.initCommands_();
+    assert(this.directoryModel_);
+    assert(this.spinnerController_);
+    assert(this.commandHandler);
+    assert(this.selectionHandler_);
+    this.scanController_ = new ScanController(
+        this.directoryModel_,
+        this.ui_.listContainer,
+        this.spinnerController_,
+        this.commandHandler,
+        this.selectionHandler_);
 
     this.directoryTree_.addEventListener('change', function() {
       this.ensureDirectoryTreeItemNotBehindPreviewPanel_();
@@ -287,7 +710,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
 
     var stateChangeHandler =
         this.onPreferencesChanged_.bind(this);
-    chrome.fileBrowserPrivate.onPreferencesChanged.addListener(
+    chrome.fileManagerPrivate.onPreferencesChanged.addListener(
         stateChangeHandler);
     stateChangeHandler();
 
@@ -310,15 +733,9 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
 
     this.initDataTransferOperations_();
 
-    this.initContextMenus_();
-    this.initCommands_();
-
     this.updateFileTypeFilter_();
-
     this.selectionHandler_.onFileSelectionChanged();
-
-    this.table_.endBatchUpdates();
-    this.grid_.endBatchUpdates();
+    this.ui_.listContainer.endBatchUpdates();
 
     callback();
   };
@@ -365,7 +782,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
    */
   FileManager.prototype.initDateTimeFormatters_ = function() {
     var use12hourClock = !this.preferences_['use24hourClock'];
-    this.table_.setDateTimeFormat(use12hourClock);
+    this.ui_.listContainer.table.setDateTimeFormat(use12hourClock);
   };
 
   /**
@@ -385,23 +802,24 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
     this.fileOperationManager_.addEventListener(
         'copy-progress', this.onCopyProgressBound_);
 
-    this.onEntryChangedBound_ = this.onEntryChanged_.bind(this);
+    this.onEntriesChangedBound_ = this.onEntriesChanged_.bind(this);
     this.fileOperationManager_.addEventListener(
-        'entry-changed', this.onEntryChangedBound_);
+        'entries-changed', this.onEntriesChangedBound_);
 
     var controller = this.fileTransferController_ =
-        new FileTransferController(this.document_,
-                                   this.fileOperationManager_,
-                                   this.metadataCache_,
-                                   this.directoryModel_,
-                                   this.volumeManager_,
-                                   this.ui_.multiProfileShareDialog);
-    controller.attachDragSource(this.table_.list);
-    controller.attachFileListDropTarget(this.table_.list);
-    controller.attachDragSource(this.grid_);
-    controller.attachFileListDropTarget(this.grid_);
+        new FileTransferController(
+                this.document_,
+                this.fileOperationManager_,
+                this.metadataCache_,
+                this.directoryModel_,
+                this.volumeManager_,
+                this.ui_.multiProfileShareDialog,
+                this.backgroundPage_.background.progressCenter);
+    controller.attachDragSource(this.ui_.listContainer.table.list);
+    controller.attachFileListDropTarget(this.ui_.listContainer.table.list);
+    controller.attachDragSource(this.ui_.listContainer.grid);
+    controller.attachFileListDropTarget(this.ui_.listContainer.grid);
     controller.attachTreeDropTarget(this.directoryTree_);
-    controller.attachNavigationListDropTarget(this.navigationList_, true);
     controller.attachCopyPasteHandlers();
     controller.addEventListener('selection-copied',
         this.blinkSelection.bind(this));
@@ -416,8 +834,6 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
    * @private
    */
   FileManager.prototype.onSourceNotFound_ = function(event) {
-    // Ensure this.sourceNotFoundErrorCount_ is integer.
-    this.sourceNotFoundErrorCount_ = ~~this.sourceNotFoundErrorCount_;
     var item = new ProgressCenterItem();
     item.id = 'source-not-found-' + this.sourceNotFoundErrorCount_;
     if (event.progressType === ProgressItemType.COPY)
@@ -434,70 +850,59 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
    * @private
    */
   FileManager.prototype.initContextMenus_ = function() {
-    this.fileContextMenu_ = this.dialogDom_.querySelector('#file-context-menu');
-    cr.ui.Menu.decorate(this.fileContextMenu_);
+    assert(this.ui_.listContainer.grid);
+    assert(this.ui_.listContainer.table);
+    assert(this.document_);
+    assert(this.dialogDom_);
 
-    cr.ui.contextMenuHandler.setContextMenu(this.grid_, this.fileContextMenu_);
-    cr.ui.contextMenuHandler.setContextMenu(this.table_.querySelector('.list'),
-        this.fileContextMenu_);
-    cr.ui.contextMenuHandler.setContextMenu(
-        this.document_.querySelector('.drive-welcome.page'),
-        this.fileContextMenu_);
-
-    this.rootsContextMenu_ =
-        this.dialogDom_.querySelector('#roots-context-menu');
-    cr.ui.Menu.decorate(this.rootsContextMenu_);
-    this.navigationList_.setContextMenu(this.rootsContextMenu_);
-
-    this.directoryTreeContextMenu_ =
-        this.dialogDom_.querySelector('#directory-tree-context-menu');
-    cr.ui.Menu.decorate(this.directoryTreeContextMenu_);
-    this.directoryTree_.contextMenuForSubitems = this.directoryTreeContextMenu_;
-
-    this.textContextMenu_ =
-        this.dialogDom_.querySelector('#text-context-menu');
-    cr.ui.Menu.decorate(this.textContextMenu_);
-
-    this.gearButton_ = this.dialogDom_.querySelector('#gear-button');
-    this.gearButton_.addEventListener('menushow',
-        this.onShowGearMenu_.bind(this));
-    chrome.fileBrowserPrivate.onDesktopChanged.addListener(function() {
-      this.updateVisitDesktopMenus_();
-      this.ui_.updateProfileBadge();
-    }.bind(this));
-    chrome.fileBrowserPrivate.onProfileAdded.addListener(
-        this.updateVisitDesktopMenus_.bind(this));
-    this.updateVisitDesktopMenus_();
+    // Set up the context menu for the file list.
+    var fileContextMenu = queryRequiredElement(
+        this.dialogDom_, '#file-context-menu');
+    cr.ui.Menu.decorate(fileContextMenu);
+    fileContextMenu = /** @type {!cr.ui.Menu} */ (fileContextMenu);
 
+    cr.ui.contextMenuHandler.setContextMenu(
+        this.ui_.listContainer.grid, fileContextMenu);
+    cr.ui.contextMenuHandler.setContextMenu(
+        this.ui_.listContainer.table.list, fileContextMenu);
+    cr.ui.contextMenuHandler.setContextMenu(
+        queryRequiredElement(this.document_, '.drive-welcome.page'),
+        fileContextMenu);
+
+    // Set up the context menu for the volume/shortcut items in directory tree.
+    var rootsContextMenu = queryRequiredElement(
+        this.dialogDom_, '#roots-context-menu');
+    cr.ui.Menu.decorate(rootsContextMenu);
+    rootsContextMenu = /** @type {!cr.ui.Menu} */ (rootsContextMenu);
+
+    this.directoryTree_.contextMenuForRootItems = rootsContextMenu;
+
+    // Set up the context menu for the folder items in directory tree.
+    var directoryTreeContextMenu = queryRequiredElement(
+        this.dialogDom_, '#directory-tree-context-menu');
+    cr.ui.Menu.decorate(directoryTreeContextMenu);
+    directoryTreeContextMenu =
+        /** @type {!cr.ui.Menu} */ (directoryTreeContextMenu);
+
+    this.directoryTree_.contextMenuForSubitems = directoryTreeContextMenu;
+
+    // Set up the context menu for the text editing.
+    var textContextMenu = queryRequiredElement(
+        this.dialogDom_, '#text-context-menu');
+    cr.ui.Menu.decorate(textContextMenu);
+    this.textContextMenu_ = /** @type {!cr.ui.Menu} */ (textContextMenu);
+
+    var gearButton = queryRequiredElement(this.dialogDom_, '#gear-button');
+    gearButton.addEventListener('menushow', this.onShowGearMenu_.bind(this));
     this.dialogDom_.querySelector('#gear-menu').menuItemSelector =
         'menuitem, hr';
-    cr.ui.decorate(this.gearButton_, cr.ui.MenuButton);
-
-    if (this.dialogType == DialogType.FULL_PAGE) {
-      // This is to prevent the buttons from stealing focus on mouse down.
-      var preventFocus = function(event) {
-        event.preventDefault();
-      };
-
-      var minimizeButton = this.dialogDom_.querySelector('#minimize-button');
-      minimizeButton.addEventListener('click', this.onMinimize.bind(this));
-      minimizeButton.addEventListener('mousedown', preventFocus);
-
-      var maximizeButton = this.dialogDom_.querySelector('#maximize-button');
-      maximizeButton.addEventListener('click', this.onMaximize.bind(this));
-      maximizeButton.addEventListener('mousedown', preventFocus);
-
-      var closeButton = this.dialogDom_.querySelector('#close-button');
-      closeButton.addEventListener('click', this.onClose.bind(this));
-      closeButton.addEventListener('mousedown', preventFocus);
-    }
+    cr.ui.decorate(gearButton, cr.ui.MenuButton);
+    this.gearButton_ = /** @type {!cr.ui.MenuButton} */ (gearButton);
 
     this.syncButton.checkable = true;
     this.hostedButton.checkable = true;
-    this.detailViewButton_.checkable = true;
-    this.thumbnailViewButton_.checkable = true;
 
-    if (util.platform.runningInBrowser()) {
+    if (util.runningInBrowser()) {
       // Suppresses the default context menu.
       this.dialogDom_.addEventListener('contextmenu', function(e) {
         e.preventDefault();
@@ -506,22 +911,6 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
     }
   };
 
-  FileManager.prototype.onMinimize = function() {
-    chrome.app.window.current().minimize();
-  };
-
-  FileManager.prototype.onMaximize = function() {
-    var appWindow = chrome.app.window.current();
-    if (appWindow.isMaximized())
-      appWindow.restore();
-    else
-      appWindow.maximize();
-  };
-
-  FileManager.prototype.onClose = function() {
-    window.close();
-  };
-
   FileManager.prototype.onShowGearMenu_ = function() {
     this.refreshRemainingSpace_(false);  /* Without loading caption. */
 
@@ -549,6 +938,8 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
    * @private
    */
   FileManager.prototype.initCommands_ = function() {
+    assert(this.textContextMenu_);
+
     this.commandHandler = new CommandHandler(this);
 
     // TODO(hirono): Move the following block to the UI part.
@@ -563,11 +954,12 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
       this.registerInputCommands_(inputs[i]);
     }
 
-    cr.ui.contextMenuHandler.setContextMenu(this.renameInput_,
+    cr.ui.contextMenuHandler.setContextMenu(this.ui_.listContainer.renameInput,
                                             this.textContextMenu_);
-    this.registerInputCommands_(this.renameInput_);
-    this.document_.addEventListener('command',
-                                    this.setNoHover_.bind(this, true));
+    this.registerInputCommands_(this.ui_.listContainer.renameInput);
+    this.document_.addEventListener(
+        'command',
+        this.ui_.listContainer.clearHover.bind(this.ui_.listContainer));
   };
 
   /**
@@ -671,6 +1063,8 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
       this.backgroundPage_ = backgroundPage;
       this.backgroundPage_.background.ready(function() {
         loadTimeData.data = this.backgroundPage_.background.stringData;
+        if (util.runningInBrowser())
+          this.backgroundPage_.registerDialog(window);
         callback();
       }.bind(this));
     }.bind(this));
@@ -696,7 +1090,8 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
     var driveEnabled =
         !noLocalPathResolution || !this.params_.shouldReturnLocalPath;
     this.volumeManager_ = new VolumeManagerWrapper(
-        driveEnabled, this.backgroundPage_);
+        /** @type {VolumeManagerWrapper.DriveEnabledStatus} */ (driveEnabled),
+        this.backgroundPage_);
     callback();
   };
 
@@ -725,14 +1120,11 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
     this.metadataCache_ = MetadataCache.createFull(this.volumeManager_);
 
     // Create the root view of FileManager.
+    assert(this.dialogDom_);
     this.ui_ = new FileManagerUI(this.dialogDom_, this.dialogType);
-    this.fileTypeSelector_ = this.ui_.fileTypeSelector;
-    this.okButton_ = this.ui_.okButton;
-    this.cancelButton_ = this.ui_.cancelButton;
 
     // Show the window as soon as the UI pre-initialization is done.
-    if (this.dialogType == DialogType.FULL_PAGE &&
-        !util.platform.runningInBrowser()) {
+    if (this.dialogType == DialogType.FULL_PAGE && !util.runningInBrowser()) {
       chrome.app.window.current().show();
       setTimeout(callback, 100);  // Wait until the animation is finished.
     } else {
@@ -769,104 +1161,84 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
    * @private
    */
   FileManager.prototype.initAdditionalUI_ = function(callback) {
+    // Cache nodes we'll be manipulating.
+    var dom = this.dialogDom_;
+    assert(dom);
+
     this.initDialogs_();
-    this.ui_.initAdditionalUI();
 
-    this.dialogDom_.addEventListener('drop', function(e) {
-      // Prevent opening an URL by dropping it onto the page.
-      e.preventDefault();
-    });
+    var table = queryRequiredElement(dom, '.detail-table');
+    FileTable.decorate(
+        table,
+        this.metadataCache_,
+        this.volumeManager_,
+        this.dialogType == DialogType.FULL_PAGE);
+    var grid = queryRequiredElement(dom, '.thumbnail-grid');
+    FileGrid.decorate(grid, this.metadataCache_, this.volumeManager_);
+
+    this.ui_.initAdditionalUI(
+        assertInstanceof(table, FileTable),
+        assertInstanceof(grid, FileGrid),
+        new PreviewPanel(
+            queryRequiredElement(dom, '.preview-panel'),
+            DialogType.isOpenDialog(this.dialogType) ?
+                PreviewPanel.VisibilityType.ALWAYS_VISIBLE :
+                PreviewPanel.VisibilityType.AUTO,
+            this.metadataCache_,
+            this.volumeManager_));
 
     this.dialogDom_.addEventListener('click',
                                      this.onExternalLinkClick_.bind(this));
-    // Cache nodes we'll be manipulating.
-    var dom = this.dialogDom_;
 
-    this.filenameInput_ = dom.querySelector('#filename-input-box input');
-    this.taskItems_ = dom.querySelector('#tasks');
 
-    this.table_ = dom.querySelector('.detail-table');
-    this.grid_ = dom.querySelector('.thumbnail-grid');
-    this.spinner_ = dom.querySelector('#list-container > .spinner-layer');
-    this.showSpinner_(true);
+    var taskItems = queryRequiredElement(dom, '#tasks');
+    this.taskItems_ = /** @type {HTMLButtonElement} */ (taskItems);
 
-    var fullPage = this.dialogType == DialogType.FULL_PAGE;
-    FileTable.decorate(this.table_, this.metadataCache_, fullPage);
-    FileGrid.decorate(this.grid_, this.metadataCache_, this.volumeManager_);
-
-    this.previewPanel_ = new PreviewPanel(
-        dom.querySelector('.preview-panel'),
-        DialogType.isOpenDialog(this.dialogType) ?
-            PreviewPanel.VisibilityType.ALWAYS_VISIBLE :
-            PreviewPanel.VisibilityType.AUTO,
+    this.ui_.locationLine = new LocationLine(
+        queryRequiredElement(dom, '#location-breadcrumbs'),
+        queryRequiredElement(dom, '#location-volume-icon'),
         this.metadataCache_,
         this.volumeManager_);
-    this.previewPanel_.addEventListener(
-        PreviewPanel.Event.VISIBILITY_CHANGE,
-        this.onPreviewPanelVisibilityChange_.bind(this));
-    this.previewPanel_.initialize();
-
-    this.previewPanel_.breadcrumbs.addEventListener(
-         'pathclick', this.onBreadcrumbClick_.bind(this));
+    this.ui_.locationLine.addEventListener(
+        'pathclick', this.onBreadcrumbClick_.bind(this));
 
     // Initialize progress center panel.
     this.progressCenterPanel_ = new ProgressCenterPanel(
-        dom.querySelector('#progress-center'));
+        queryRequiredElement(dom, '#progress-center'));
     this.backgroundPage_.background.progressCenter.addPanel(
         this.progressCenterPanel_);
 
     this.document_.addEventListener('keydown', this.onKeyDown_.bind(this));
     this.document_.addEventListener('keyup', this.onKeyUp_.bind(this));
 
-    this.renameInput_ = this.document_.createElement('input');
-    this.renameInput_.className = 'rename';
-
-    this.renameInput_.addEventListener(
-        'keydown', this.onRenameInputKeyDown_.bind(this));
-    this.renameInput_.addEventListener(
-        'blur', this.onRenameInputBlur_.bind(this));
+    this.ui_.listContainer.element.addEventListener(
+        'keydown', this.onListKeyDown_.bind(this));
+    this.ui_.listContainer.element.addEventListener(
+        ListContainer.EventType.TEXT_SEARCH, this.onTextSearch_.bind(this));
 
     // TODO(hirono): Rename the handler after creating the DialogFooter class.
-    this.filenameInput_.addEventListener(
+    this.ui_.dialogFooter.filenameInput.addEventListener(
         'input', this.onFilenameInputInput_.bind(this));
-    this.filenameInput_.addEventListener(
+    this.ui_.dialogFooter.filenameInput.addEventListener(
         'keydown', this.onFilenameInputKeyDown_.bind(this));
-    this.filenameInput_.addEventListener(
+    this.ui_.dialogFooter.filenameInput.addEventListener(
         'focus', this.onFilenameInputFocus_.bind(this));
 
-    this.listContainer_ = this.dialogDom_.querySelector('#list-container');
-    this.listContainer_.addEventListener(
-        'keydown', this.onListKeyDown_.bind(this));
-    this.listContainer_.addEventListener(
-        'keypress', this.onListKeyPress_.bind(this));
-    this.listContainer_.addEventListener(
-        'mousemove', this.onListMouseMove_.bind(this));
-
-    this.okButton_.addEventListener('click', this.onOk_.bind(this));
-    this.onCancelBound_ = this.onCancel_.bind(this);
-    this.cancelButton_.addEventListener('click', this.onCancelBound_);
-
     this.decorateSplitter(
         this.dialogDom_.querySelector('#navigation-list-splitter'));
-    this.decorateSplitter(
-        this.dialogDom_.querySelector('#middlebar-splitter'));
 
-    this.dialogContainer_ = this.dialogDom_.querySelector('.dialog-container');
+    this.dialogContainer_ = /** @type {!HTMLDivElement} */
+        (this.dialogDom_.querySelector('.dialog-container'));
 
-    this.syncButton = this.dialogDom_.querySelector(
-        '#gear-menu-drive-sync-settings');
-    this.hostedButton = this.dialogDom_.querySelector(
-        '#gear-menu-drive-hosted-settings');
+    this.syncButton = /** @type {!HTMLMenuItemElement} */
+        (queryRequiredElement(this.dialogDom_,
+                              '#gear-menu-drive-sync-settings'));
+    this.hostedButton = /** @type {!HTMLMenuItemElement} */
+        (queryRequiredElement(this.dialogDom_,
+                             '#gear-menu-drive-hosted-settings'));
 
-    this.detailViewButton_ =
-        this.dialogDom_.querySelector('#detail-view');
-    this.detailViewButton_.addEventListener('activate',
-        this.onDetailViewButtonClick_.bind(this));
-
-    this.thumbnailViewButton_ =
-        this.dialogDom_.querySelector('#thumbnail-view');
-    this.thumbnailViewButton_.addEventListener('activate',
-        this.onThumbnailViewButtonClick_.bind(this));
+    this.ui_.toggleViewButton.addEventListener('click',
+        this.onToggleViewButtonClick_.bind(this));
 
     cr.ui.ComboButton.decorate(this.taskItems_);
     this.taskItems_.showMenu = function(shouldSetFocus) {
@@ -881,47 +1253,19 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
     this.dialogDom_.ownerDocument.defaultView.addEventListener(
         'resize', this.onResize_.bind(this));
 
-    this.filePopup_ = null;
-
-    this.searchBoxWrapper_ = this.ui_.searchBox.element;
-    this.searchBox_ = this.ui_.searchBox.inputElement;
-    this.searchBox_.addEventListener(
-        'input', this.onSearchBoxUpdate_.bind(this));
-    this.ui_.searchBox.clearButton.addEventListener(
-        'click', this.onSearchClearButtonClick_.bind(this));
-
-    this.lastSearchQuery_ = '';
-
-    this.autocompleteList_ = this.ui_.searchBox.autocompleteList;
-    this.autocompleteList_.requestSuggestions =
-        this.requestAutocompleteSuggestions_.bind(this);
-
-    // Instead, open the suggested item when Enter key is pressed or
-    // mouse-clicked.
-    this.autocompleteList_.handleEnterKeydown = function(event) {
-      this.openAutocompleteSuggestion_();
-      this.lastAutocompleteQuery_ = '';
-      this.autocompleteList_.suggestions = [];
-    }.bind(this);
-    this.autocompleteList_.addEventListener('mousedown', function(event) {
-      this.openAutocompleteSuggestion_();
-      this.lastAutocompleteQuery_ = '';
-      this.autocompleteList_.suggestions = [];
-    }.bind(this));
-
-    this.defaultActionMenuItem_ =
-        this.dialogDom_.querySelector('#default-action');
+    this.actionMenuItem_ = /** @type {!HTMLMenuItemElement} */
+        (queryRequiredElement(this.dialogDom_, '#default-action'));
 
-    this.openWithCommand_ =
-        this.dialogDom_.querySelector('#open-with');
+    this.openWithCommand_ = /** @type {cr.ui.Command} */
+        (this.dialogDom_.querySelector('#open-with'));
 
-    this.driveBuyMoreStorageCommand_ =
-        this.dialogDom_.querySelector('#drive-buy-more-space');
+    this.actionMenuItem_.addEventListener('activate',
+        this.onActionMenuItemActivated_.bind(this));
 
-    this.defaultActionMenuItem_.addEventListener('activate',
-        this.dispatchSelectionAction_.bind(this));
-
-    this.initFileTypeFilter_();
+    this.ui_.dialogFooter.initFileTypeFilter(
+        this.fileTypes_, this.params_.includeAllFiles);
+    this.ui_.dialogFooter.fileTypeSelector.addEventListener(
+        'change', this.updateFileTypeFilter_.bind(this));
 
     util.addIsFocusedMethod();
 
@@ -929,8 +1273,8 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
     i18nTemplate.process(this.document_, loadTimeData);
 
     // Arrange the file list.
-    this.table_.normalizeColumns();
-    this.table_.redraw();
+    this.ui_.listContainer.table.normalizeColumns();
+    this.ui_.listContainer.table.redraw();
 
     callback();
   };
@@ -948,21 +1292,16 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
    * @private
    **/
   FileManager.prototype.initFileList_ = function() {
-    // Always sharing the data model between the detail/thumb views confuses
-    // them.  Instead we maintain this bogus data model, and hook it up to the
-    // view that is not in use.
-    this.emptyDataModel_ = new cr.ui.ArrayDataModel([]);
-    this.emptySelectionModel_ = new cr.ui.ListSelectionModel();
-
     var singleSelection =
         this.dialogType == DialogType.SELECT_OPEN_FILE ||
         this.dialogType == DialogType.SELECT_FOLDER ||
         this.dialogType == DialogType.SELECT_UPLOAD_FOLDER ||
         this.dialogType == DialogType.SELECT_SAVEAS_FILE;
 
+    assert(this.metadataCache_);
     this.fileFilter_ = new FileFilter(
         this.metadataCache_,
-        false  /* Don't show dot files by default. */);
+        false  /* Don't show dot files and *.crdownload by default. */);
 
     this.fileWatcher_ = new FileWatcher(this.metadataCache_);
     this.fileWatcher_.addEventListener(
@@ -982,9 +1321,6 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
     this.selectionHandler_ = new FileSelectionHandler(this);
 
     var dataModel = this.directoryModel_.getFileList();
-
-    this.table_.setupCompareFunctions(dataModel);
-
     dataModel.addEventListener('permuted',
                                this.updateStartupPrefs_.bind(this));
 
@@ -992,30 +1328,22 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
         this.selectionHandler_.onFileSelectionChanged.bind(
             this.selectionHandler_));
 
-    this.initList_(this.grid_);
-    this.initList_(this.table_.list);
+    var onDetailClickBound = this.onDetailClick_.bind(this);
+    this.ui_.listContainer.table.list.addEventListener(
+        'click', onDetailClickBound);
+    this.ui_.listContainer.grid.addEventListener(
+        'click', onDetailClickBound);
 
     var fileListFocusBound = this.onFileListFocus_.bind(this);
-    this.table_.list.addEventListener('focus', fileListFocusBound);
-    this.grid_.addEventListener('focus', fileListFocusBound);
-
-    var dragStartBound = this.onDragStart_.bind(this);
-    this.table_.list.addEventListener('dragstart', dragStartBound);
-    this.grid_.addEventListener('dragstart', dragStartBound);
-
-    var dragEndBound = this.onDragEnd_.bind(this);
-    this.table_.list.addEventListener('dragend', dragEndBound);
-    this.grid_.addEventListener('dragend', dragEndBound);
-    // This event is published by DragSelector because drag end event is not
-    // published at the end of drag selection.
-    this.table_.list.addEventListener('dragselectionend', dragEndBound);
-    this.grid_.addEventListener('dragselectionend', dragEndBound);
+    this.ui_.listContainer.table.list.addEventListener(
+        'focus', fileListFocusBound);
+    this.ui_.listContainer.grid.addEventListener('focus', fileListFocusBound);
 
     // TODO(mtomasz, yoshiki): Create navigation list earlier, and here just
     // attach the directory model.
-    this.initNavigationList_();
+    this.initDirectoryTree_();
 
-    this.table_.addEventListener('column-resize-end',
+    this.ui_.listContainer.table.addEventListener('column-resize-end',
                                  this.updateStartupPrefs_.bind(this));
 
     // Restore preferences.
@@ -1023,21 +1351,61 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
         this.viewOptions_.sortField || 'modificationTime',
         this.viewOptions_.sortDirection || 'desc');
     if (this.viewOptions_.columns) {
-      var cm = this.table_.columnModel;
+      var cm = this.ui_.listContainer.table.columnModel;
       for (var i = 0; i < cm.totalSize; i++) {
         if (this.viewOptions_.columns[i] > 0)
           cm.setWidth(i, this.viewOptions_.columns[i]);
       }
     }
-    this.setListType(this.viewOptions_.listType || FileManager.ListType.DETAIL);
 
-    this.textSearchState_ = {text: '', date: new Date()};
+    this.ui_.listContainer.dataModel = this.directoryModel_.getFileList();
+    this.ui_.listContainer.selectionModel =
+        this.directoryModel_.getFileListSelection();
+    this.setListType(
+        this.viewOptions_.listType || ListContainer.ListType.DETAIL);
+
     this.closeOnUnmount_ = (this.params_.action == 'auto-open');
 
     if (this.closeOnUnmount_) {
       this.volumeManager_.addEventListener('externally-unmounted',
-         this.onExternallyUnmounted_.bind(this));
-    }
+          this.onExternallyUnmounted_.bind(this));
+    }
+
+    // Create search controller.
+    this.searchController_ = new SearchController(
+        this.ui_.searchBox,
+        this.ui_.locationLine,
+        this.directoryModel_,
+        this.volumeManager_,
+        {
+          // TODO (hirono): Make the real task controller and pass it here.
+          doAction: this.doEntryAction_.bind(this)
+        });
+
+    // Create naming controller.
+    assert(this.ui_.alertDialog);
+    assert(this.ui_.confirmDialog);
+    this.namingController_ = new NamingController(
+        this.ui_.listContainer,
+        this.ui_.alertDialog,
+        this.ui_.confirmDialog,
+        this.directoryModel_,
+        this.fileFilter_,
+        this.selectionHandler_);
+
+    // Create spinner controller.
+    this.spinnerController_ = new SpinnerController(
+        this.ui_.listContainer.spinner, this.directoryModel_);
+    this.spinnerController_.show();
+
+    // Create dialog action controller.
+    this.dialogActionController_ = new DialogActionController(
+        this.dialogType,
+        this.ui_.dialogFooter,
+        this.directoryModel_,
+        this.metadataCache_,
+        this.namingController_,
+        this.params_.shouldReturnLocalPath);
 
     // Update metadata to change 'Today' and 'Yesterday' dates.
     var today = new Date();
@@ -1052,36 +1420,27 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
   /**
    * @private
    */
-  FileManager.prototype.initNavigationList_ = function() {
-    this.directoryTree_ = this.dialogDom_.querySelector('#directory-tree');
+  FileManager.prototype.initDirectoryTree_ = function() {
+    var fakeEntriesVisible =
+        this.dialogType !== DialogType.SELECT_SAVEAS_FILE;
+    this.directoryTree_ = /** @type {DirectoryTree} */
+        (this.dialogDom_.querySelector('#directory-tree'));
     DirectoryTree.decorate(this.directoryTree_,
                            this.directoryModel_,
-                           this.volumeManager_);
-
-    this.navigationList_ = this.dialogDom_.querySelector('#navigation-list');
-    NavigationList.decorate(this.navigationList_,
-                            this.volumeManager_,
-                            this.directoryModel_);
-    this.navigationList_.fileManager = this;
-    this.navigationList_.dataModel = new NavigationListModel(
+                           this.volumeManager_,
+                           this.metadataCache_,
+                           fakeEntriesVisible);
+    this.directoryTree_.dataModel = new NavigationListModel(
         this.volumeManager_, this.folderShortcutsModel_);
-  };
-
-  /**
-   * @private
-   */
-  FileManager.prototype.updateMiddleBarVisibility_ = function() {
-    var entry = this.directoryModel_.getCurrentDirEntry();
-    if (!entry)
-      return;
 
-    var driveVolume = this.volumeManager_.getVolumeInfo(entry);
-    var visible = driveVolume && !driveVolume.error &&
-        driveVolume.volumeType === VolumeManagerCommon.VolumeType.DRIVE;
-    this.dialogDom_.
-        querySelector('.dialog-middlebar-contents').hidden = !visible;
-    this.dialogDom_.querySelector('#middlebar-splitter').hidden = !visible;
-    this.onResize_();
+    // Visible height of the directory tree depends on the size of progress
+    // center panel. When the size of progress center panel changes, directory
+    // tree has to be notified to adjust its components (e.g. progress bar).
+    var observer = new MutationObserver(
+        this.directoryTree_.relayout.bind(this.directoryTree_));
+    observer.observe(this.progressCenterPanel_.element,
+                     /** @type {MutationObserverInit} */
+                     ({subtree: true, attributes: true, childList: true}));
   };
 
   /**
@@ -1093,14 +1452,16 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
       sortField: sortStatus.field,
       sortDirection: sortStatus.direction,
       columns: [],
-      listType: this.listType_
+      listType: this.ui_.listContainer.currentListType
     };
-    var cm = this.table_.columnModel;
+    var cm = this.ui_.listContainer.table.columnModel;
     for (var i = 0; i < cm.totalSize; i++) {
       prefs.columns.push(cm.getWidth(i));
     }
     // Save the global default.
-    util.platform.setPreference(this.startupPrefName_, JSON.stringify(prefs));
+    var items = {};
+    items[this.startupPrefName_] = JSON.stringify(prefs);
+    chrome.storage.local.set(items);
 
     // Save the window-specific preference.
     if (window.appState) {
@@ -1112,9 +1473,9 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
   FileManager.prototype.refocus = function() {
     var targetElement;
     if (this.dialogType == DialogType.SELECT_SAVEAS_FILE)
-      targetElement = this.filenameInput_;
+      targetElement = this.ui_.dialogFooter.filenameInput;
     else
-      targetElement = this.currentList_;
+      targetElement = this.ui.listContainer.currentList;
 
     // Hack: if the tabIndex is disabled, we can assume a modal dialog is
     // shown. Focus to a button on the dialog instead.
@@ -1141,83 +1502,18 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
   };
 
   /**
-   * Index of selected item in the typeList of the dialog params.
-   *
-   * @return {number} 1-based index of selected type or 0 if no type selected.
-   * @private
+   * Sets the current list type.
+   * @param {ListContainer.ListType} type New list type.
    */
-  FileManager.prototype.getSelectedFilterIndex_ = function() {
-    var index = Number(this.fileTypeSelector_.selectedIndex);
-    if (index < 0)  // Nothing selected.
-      return 0;
-    if (this.params_.includeAllFiles)  // Already 1-based.
-      return index;
-    return index + 1;  // Convert to 1-based;
-  };
-
   FileManager.prototype.setListType = function(type) {
-    if (type && type == this.listType_)
+    if ((type && type == this.ui_.listContainer.currentListType) ||
+        !this.directoryModel_) {
       return;
-
-    this.table_.list.startBatchUpdates();
-    this.grid_.startBatchUpdates();
-
-    // TODO(dzvorygin): style.display and dataModel setting order shouldn't
-    // cause any UI bugs. Currently, the only right way is first to set display
-    // style and only then set dataModel.
-
-    if (type == FileManager.ListType.DETAIL) {
-      this.table_.dataModel = this.directoryModel_.getFileList();
-      this.table_.selectionModel = this.directoryModel_.getFileListSelection();
-      this.table_.hidden = false;
-      this.grid_.hidden = true;
-      this.grid_.selectionModel = this.emptySelectionModel_;
-      this.grid_.dataModel = this.emptyDataModel_;
-      this.table_.hidden = false;
-      /** @type {cr.ui.List} */
-      this.currentList_ = this.table_.list;
-      this.detailViewButton_.setAttribute('checked', '');
-      this.thumbnailViewButton_.removeAttribute('checked');
-      this.detailViewButton_.setAttribute('disabled', '');
-      this.thumbnailViewButton_.removeAttribute('disabled');
-    } else if (type == FileManager.ListType.THUMBNAIL) {
-      this.grid_.dataModel = this.directoryModel_.getFileList();
-      this.grid_.selectionModel = this.directoryModel_.getFileListSelection();
-      this.grid_.hidden = false;
-      this.table_.hidden = true;
-      this.table_.selectionModel = this.emptySelectionModel_;
-      this.table_.dataModel = this.emptyDataModel_;
-      this.grid_.hidden = false;
-      /** @type {cr.ui.List} */
-      this.currentList_ = this.grid_;
-      this.thumbnailViewButton_.setAttribute('checked', '');
-      this.detailViewButton_.removeAttribute('checked');
-      this.thumbnailViewButton_.setAttribute('disabled', '');
-      this.detailViewButton_.removeAttribute('disabled');
-    } else {
-      throw new Error('Unknown list type: ' + type);
     }
 
-    this.listType_ = type;
+    this.ui_.setCurrentListType(type);
     this.updateStartupPrefs_();
     this.onResize_();
-
-    this.table_.list.endBatchUpdates();
-    this.grid_.endBatchUpdates();
-  };
-
-  /**
-   * Initialize the file list table or grid.
-   *
-   * @param {cr.ui.List} list The list.
-   * @private
-   */
-  FileManager.prototype.initList_ = function(list) {
-    // Overriding the default role 'list' to 'listbox' for better accessibility
-    // on ChromeOS.
-    list.setAttribute('role', 'listbox');
-    list.addEventListener('click', this.onDetailClick_.bind(this));
-    list.id = 'file-list';
   };
 
   /**
@@ -1248,86 +1544,38 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
    * @param {Event} event An event for the entry change.
    * @private
    */
-  FileManager.prototype.onEntryChanged_ = function(event) {
+  FileManager.prototype.onEntriesChanged_ = function(event) {
     var kind = event.kind;
-    var entry = event.entry;
-    this.directoryModel_.onEntryChanged(kind, entry);
+    var entries = event.entries;
+    this.directoryModel_.onEntriesChanged(kind, entries);
     this.selectionHandler_.onFileSelectionChanged();
 
-    if (kind === util.EntryChangedKind.CREATED && FileType.isImage(entry)) {
-      // Preload a thumbnail if the new copied entry an image.
+    if (kind !== util.EntryChangedKind.CREATED)
+      return;
+
+    var preloadThumbnail = function(entry) {
       var locationInfo = this.volumeManager_.getLocationInfo(entry);
       if (!locationInfo)
         return;
-      this.metadataCache_.get(entry, 'thumbnail|drive', function(metadata) {
-        var thumbnailLoader_ = new ThumbnailLoader(
-            entry,
-            ThumbnailLoader.LoaderType.CANVAS,
-            metadata,
-            undefined,  // Media type.
-            // TODO(mtomasz): Use Entry instead of paths.
-            locationInfo.isDriveBased ?
-                ThumbnailLoader.UseEmbedded.USE_EMBEDDED :
-                ThumbnailLoader.UseEmbedded.NO_EMBEDDED,
-            10);  // Very low priority.
-        thumbnailLoader_.loadDetachedImage(function(success) {});
-      });
-    }
-  };
-
-  /**
-   * Fills the file type list or hides it.
-   * @private
-   */
-  FileManager.prototype.initFileTypeFilter_ = function() {
-    if (this.params_.includeAllFiles) {
-      var option = this.document_.createElement('option');
-      option.innerText = str('ALL_FILES_FILTER');
-      this.fileTypeSelector_.appendChild(option);
-      option.value = 0;
-    }
-
-    for (var i = 0; i !== this.fileTypes_.length; i++) {
-      var fileType = this.fileTypes_[i];
-      var option = this.document_.createElement('option');
-      var description = fileType.description;
-      if (!description) {
-        // See if all the extensions in the group have the same description.
-        for (var j = 0; j !== fileType.extensions.length; j++) {
-          var currentDescription = FileType.typeToString(
-              FileType.getTypeForName('.' + fileType.extensions[j]));
-          if (!description)  // Set the first time.
-            description = currentDescription;
-          else if (description != currentDescription) {
-            // No single description, fall through to the extension list.
-            description = null;
-            break;
-          }
-        }
-
-        if (!description)
-          // Convert ['jpg', 'png'] to '*.jpg, *.png'.
-          description = fileType.extensions.map(function(s) {
-           return '*.' + s;
-          }).join(', ');
-       }
-       option.innerText = description;
-
-       option.value = i + 1;
-
-       if (fileType.selected)
-         option.selected = true;
-
-       this.fileTypeSelector_.appendChild(option);
-    }
-
-    var options = this.fileTypeSelector_.querySelectorAll('option');
-    if (options.length >= 2) {
-      // There is in fact no choice, show the selector.
-      this.fileTypeSelector_.hidden = false;
+      this.metadataCache_.getOne(entry, 'thumbnail|external',
+          function(metadata) {
+            var thumbnailLoader_ = new ThumbnailLoader(
+                entry,
+                ThumbnailLoader.LoaderType.CANVAS,
+                metadata,
+                undefined,  // Media type.
+                locationInfo.isDriveBased ?
+                    ThumbnailLoader.UseEmbedded.USE_EMBEDDED :
+                    ThumbnailLoader.UseEmbedded.NO_EMBEDDED,
+                10);  // Very low priority.
+            thumbnailLoader_.loadDetachedImage(function(success) {});
+          });
+    }.bind(this);
 
-      this.fileTypeSelector_.addEventListener('change',
-          this.updateFileTypeFilter_.bind(this));
+    for (var i = 0; i < entries.length; i++) {
+      // Preload a thumbnail if the new copied entry an image.
+      if (FileType.isImage(entries[i]))
+        preloadThumbnail(entries[i]);
     }
   };
 
@@ -1337,14 +1585,28 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
    */
   FileManager.prototype.updateFileTypeFilter_ = function() {
     this.fileFilter_.removeFilter('fileType');
-    var selectedIndex = this.getSelectedFilterIndex_();
+    var selectedIndex = this.ui_.dialogFooter.selectedFilterIndex;
     if (selectedIndex > 0) { // Specific filter selected.
-      var regexp = new RegExp('.*(' +
+      var regexp = new RegExp('\\.(' +
           this.fileTypes_[selectedIndex - 1].extensions.join('|') + ')$', 'i');
       var filter = function(entry) {
         return entry.isDirectory || regexp.test(entry.name);
       };
       this.fileFilter_.addFilter('fileType', filter);
+
+      // In save dialog, update the destination name extension.
+      if (this.dialogType === DialogType.SELECT_SAVEAS_FILE) {
+        var current = this.ui_.dialogFooter.filenameInput.value;
+        var newExt = this.fileTypes_[selectedIndex - 1].extensions[0];
+        if (newExt && !regexp.test(current)) {
+          var i = current.lastIndexOf('.');
+          if (i >= 0) {
+            this.ui_.dialogFooter.filenameInput.value =
+                current.substr(0, i) + '.' + newExt;
+            this.selectTargetNameInFilenameInput_();
+          }
+        }
+      }
     }
   };
 
@@ -1353,75 +1615,22 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
    * @private
    */
   FileManager.prototype.onResize_ = function() {
-    if (this.listType_ == FileManager.ListType.THUMBNAIL)
-      this.grid_.relayout();
-    else
-      this.table_.relayout();
-
     // May not be available during initialization.
     if (this.directoryTree_)
       this.directoryTree_.relayout();
 
-    // TODO(mtomasz, yoshiki): Initialize navigation list earlier, before
-    // file system is available.
-    if (this.navigationList_)
-      this.navigationList_.redraw();
-
-    this.previewPanel_.breadcrumbs.truncate();
+    this.ui_.relayout();
   };
 
   /**
    * Handles local metadata changes in the currect directory.
    * @param {Event} event Change event.
+   * @this {FileManager}
    * @private
    */
   FileManager.prototype.onWatcherMetadataChanged_ = function(event) {
-    this.updateMetadataInUI_(
-        event.metadataType, event.entries, event.properties);
-  };
-
-  /**
-   * Resize details and thumb views to fit the new window size.
-   * @private
-   */
-  FileManager.prototype.onPreviewPanelVisibilityChange_ = function() {
-    // This method may be called on initialization. Some object may not be
-    // initialized.
-
-    var panelHeight = this.previewPanel_.visible ?
-        this.previewPanel_.height : 0;
-    if (this.grid_)
-      this.grid_.setBottomMarginForPanel(panelHeight);
-    if (this.table_)
-      this.table_.setBottomMarginForPanel(panelHeight);
-
-    if (this.directoryTree_) {
-      this.directoryTree_.setBottomMarginForPanel(panelHeight);
-      this.ensureDirectoryTreeItemNotBehindPreviewPanel_();
-    }
-  };
-
-  /**
-   * Invoked when the drag is started on the list or the grid.
-   * @private
-   */
-  FileManager.prototype.onDragStart_ = function() {
-    // On open file dialog, the preview panel is always shown.
-    if (DialogType.isOpenDialog(this.dialogType))
-      return;
-    this.previewPanel_.visibilityType =
-        PreviewPanel.VisibilityType.ALWAYS_HIDDEN;
-  };
-
-  /**
-   * Invoked when the drag is ended on the list or the grid.
-   * @private
-   */
-  FileManager.prototype.onDragEnd_ = function() {
-    // On open file dialog, the preview panel is always shown.
-    if (DialogType.isOpenDialog(this.dialogType))
-      return;
-    this.previewPanel_.visibilityType = PreviewPanel.VisibilityType.AUTO;
+    this.ui_.listContainer.currentView.updateListItemsMetadata(
+        event.metadataType, event.entries);
   };
 
   /**
@@ -1442,7 +1651,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
     var selectionEntry;
 
     // Resolve the selectionURL to selectionEntry or to currentDirectoryEntry
-    // in case of being a display root.
+    // in case of being a display root or a default directory to open files.
     queue.run(function(callback) {
       if (!this.initSelectionURL_) {
         callback();
@@ -1463,8 +1672,19 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
             // opening it.
             if (locationInfo.isRootEntry)
               nextCurrentDirEntry = inEntry;
-            else
+
+            // If this dialog attempts to open file(s) and the selection is a
+            // directory, the selection should be the current directory.
+            if (DialogType.isOpenFileDialog(this.dialogType) &&
+                inEntry.isDirectory) {
+              nextCurrentDirEntry = inEntry;
+            }
+
+            // By default, the selection should be selected entry and the
+            // parent directory of it should be the current directory.
+            if (!nextCurrentDirEntry)
               selectionEntry = inEntry;
+
             callback();
           }.bind(this), callback);
     }.bind(this));
@@ -1503,6 +1723,45 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
       }.bind(this));
     }.bind(this));
 
+    // Check if the next current directory is not a virtual directory which is
+    // not available in UI. This may happen to shared on Drive.
+    queue.run(function(callback) {
+      if (!nextCurrentDirEntry) {
+        callback();
+        return;
+      }
+      var locationInfo = this.volumeManager_.getLocationInfo(
+          nextCurrentDirEntry);
+      // If we can't check, assume that the directory is illegal.
+      if (!locationInfo) {
+        nextCurrentDirEntry = null;
+        callback();
+        return;
+      }
+      // Having root directory of DRIVE_OTHER here should be only for shared
+      // with me files. Fallback to Drive root in such case.
+      if (locationInfo.isRootEntry && locationInfo.rootType ===
+              VolumeManagerCommon.RootType.DRIVE_OTHER) {
+        var volumeInfo = this.volumeManager_.getVolumeInfo(nextCurrentDirEntry);
+        if (!volumeInfo) {
+          nextCurrentDirEntry = null;
+          callback();
+          return;
+        }
+        volumeInfo.resolveDisplayRoot().then(
+            function(entry) {
+              nextCurrentDirEntry = entry;
+              callback();
+            }).catch(function(error) {
+              console.error(error.stack || error);
+              nextCurrentDirEntry = null;
+              callback();
+            });
+      } else {
+        callback();
+      }
+    }.bind(this));
+
     // If the directory to be changed to is still not resolved, then fallback
     // to the default display root.
     queue.run(function(callback) {
@@ -1534,19 +1793,18 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
           }, function() {
             // Failed to resolve as a file
             nextCurrentDirEntry.getDirectory(
-              this.initTargetName_,
-              {},
-              function(targetEntry) {
-                selectionEntry = targetEntry;
-                callback();
-              }, function() {
-                // Failed to resolve as either file or directory.
-                callback();
-              });
+                this.initTargetName_,
+                {},
+                function(targetEntry) {
+                  selectionEntry = targetEntry;
+                  callback();
+                }, function() {
+                  // Failed to resolve as either file or directory.
+                  callback();
+                });
           }.bind(this));
     }.bind(this));
 
-
     // Finalize.
     queue.run(function(callback) {
       // Check directory change.
@@ -1590,35 +1848,15 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
         return;
 
       var task = null;
-      // Handle restoring after crash, or the gallery action.
-      // TODO(mtomasz): Use the gallery action instead of just the gallery
-      //     field.
-      if (this.params_.gallery ||
-          this.params_.action === 'gallery' ||
-          this.params_.action === 'gallery-video') {
-        if (!opt_selectionEntry) {
-          // Non-existent file or a directory.
-          // Reloading while the Gallery is open with empty or multiple
-          // selection. Open the Gallery when the directory is scanned.
-          task = function() {
-            new FileTasks(this, this.params_).openGallery([]);
-          }.bind(this);
-        } else {
-          // The file or the directory exists.
-          task = function() {
-            new FileTasks(this, this.params_).openGallery([opt_selectionEntry]);
-          }.bind(this);
-        }
-      } else {
-        // TODO(mtomasz): Implement remounting archives after crash.
-        //                See: crbug.com/333139
-      }
+
+      // TODO(mtomasz): Implement remounting archives after crash.
+      //                See: crbug.com/333139
 
       // If there is a task to be run, run it after the scan is completed.
       if (task) {
         var listener = function() {
           if (!util.isSameEntry(this.directoryModel_.getCurrentDirEntry(),
-                               directoryEntry)) {
+                                directoryEntry)) {
             // Opened on a different URL. Probably fallbacked. Therefore,
             // do not invoke a task.
             return;
@@ -1630,7 +1868,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
         this.directoryModel_.addEventListener('scan-completed', listener);
       }
     } else if (this.dialogType === DialogType.SELECT_SAVEAS_FILE) {
-      this.filenameInput_.value = opt_suggestedName || '';
+      this.ui_.dialogFooter.filenameInput.value = opt_suggestedName || '';
       this.selectTargetNameInFilenameInput_();
     }
   };
@@ -1652,21 +1890,20 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
     var getEntries = (isFakeEntry ? [] : [directoryEntry]).concat(entries);
     if (!isFakeEntry)
       this.metadataCache_.clearRecursively(directoryEntry, '*');
-    this.metadataCache_.get(getEntries, 'filesystem', null);
+    this.metadataCache_.get(getEntries, 'filesystem|external', null);
 
-    if (this.isOnDrive())
-      this.metadataCache_.get(getEntries, 'drive', null);
-
-    var visibleItems = this.currentList_.items;
+    var visibleItems = this.ui.listContainer.currentList.items;
     var visibleEntries = [];
     for (var i = 0; i < visibleItems.length; i++) {
-      var index = this.currentList_.getIndexOfListItem(visibleItems[i]);
+      var index = this.ui.listContainer.currentList.getIndexOfListItem(
+          visibleItems[i]);
       var entry = this.directoryModel_.getFileList().item(index);
       // The following check is a workaround for the bug in list: sometimes item
       // does not have listIndex, and therefore is not found in the list.
       if (entry) visibleEntries.push(entry);
     }
-    this.metadataCache_.get(visibleEntries, 'thumbnail', null);
+    // Refreshes the metadata.
+    this.metadataCache_.getLatest(visibleEntries, 'thumbnail', null);
   };
 
   /**
@@ -1677,69 +1914,25 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
     this.metadataCache_.get(
         entries,
         'filesystem',
-        this.updateMetadataInUI_.bind(this, 'filesystem', entries));
+        function() {
+          this.ui_.listContainer.currentView.updateListItemsMetadata(
+              'filesystem', entries);
+        }.bind(this));
 
     setTimeout(this.dailyUpdateModificationTime_.bind(this),
                MILLISECONDS_IN_DAY);
   };
 
   /**
-   * @param {string} type Type of metadata changed.
-   * @param {Array.<Entry>} entries Array of entries.
-   * @param {Object.<string, Object>} props Map from entry URLs to metadata
-   *     props.
-   * @private
-   */
-  FileManager.prototype.updateMetadataInUI_ = function(
-      type, entries, properties) {
-    if (this.listType_ == FileManager.ListType.DETAIL)
-      this.table_.updateListItemsMetadata(type, properties);
-    else
-      this.grid_.updateListItemsMetadata(type, properties);
-    // TODO: update bottom panel thumbnails.
-  };
-
-  /**
-   * Restore the item which is being renamed while refreshing the file list. Do
-   * nothing if no item is being renamed or such an item disappeared.
-   *
-   * While refreshing file list it gets repopulated with new file entries.
-   * There is not a big difference whether DOM items stay the same or not.
-   * Except for the item that the user is renaming.
-   *
-   * @private
-   */
-  FileManager.prototype.restoreItemBeingRenamed_ = function() {
-    if (!this.isRenamingInProgress())
-      return;
-
-    var dm = this.directoryModel_;
-    var leadIndex = dm.getFileListSelection().leadIndex;
-    if (leadIndex < 0)
-      return;
-
-    var leadEntry = dm.getFileList().item(leadIndex);
-    if (!util.isSameEntry(this.renameInput_.currentEntry, leadEntry))
-      return;
-
-    var leadListItem = this.findListItemForNode_(this.renameInput_);
-    if (this.currentList_ == this.table_.list) {
-      this.table_.updateFileMetadata(leadListItem, leadEntry);
-    }
-    this.currentList_.restoreLeadItem(leadListItem);
-  };
-
-  /**
    * TODO(mtomasz): Move this to a utility function working on the root type.
    * @return {boolean} True if the current directory content is from Google
    *     Drive.
    */
   FileManager.prototype.isOnDrive = function() {
     var rootType = this.directoryModel_.getCurrentRootType();
-    return rootType === VolumeManagerCommon.RootType.DRIVE ||
-           rootType === VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME ||
-           rootType === VolumeManagerCommon.RootType.DRIVE_RECENT ||
-           rootType === VolumeManagerCommon.RootType.DRIVE_OFFLINE;
+    return rootType != null &&
+        VolumeManagerCommon.getVolumeTypeFromRootType(rootType) ==
+            VolumeManagerCommon.VolumeType.DRIVE;
   };
 
   /**
@@ -1748,7 +1941,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
    * @return {boolean} True if those setting items should be shown.
    */
   FileManager.prototype.shouldShowDriveSettings = function() {
-    return this.isOnDrive() && this.isSecretGearMenuShown_;
+    return this.isOnDrive();
   };
 
   /**
@@ -1764,7 +1957,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
       return;
 
     if (this.dialogType != DialogType.FULL_PAGE)
-      this.onCancel_();
+      this.ui_.dialogFooter.cancelButton.click();
   };
 
   /**
@@ -1803,7 +1996,8 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
       selection.tasks.showTaskPicker(this.defaultTaskPicker,
           loadTimeData.getString('CHANGE_DEFAULT_MENU_ITEM'),
           strf('CHANGE_DEFAULT_CAPTION', format),
-          this.onDefaultTaskDone_.bind(this));
+          this.onDefaultTaskDone_.bind(this),
+          true);
     }
   };
 
@@ -1816,7 +2010,9 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
   FileManager.prototype.onDefaultTaskDone_ = function(task) {
     // TODO(dgozman): move this method closer to tasks.
     var selection = this.getSelection();
-    chrome.fileBrowserPrivate.setDefaultTask(
+    // TODO(mtomasz): Move conversion from entry to url to custom bindings.
+    // crbug.com/345527.
+    chrome.fileManagerPrivate.setDefaultTask(
         task.taskId,
         util.entriesToURLs(selection.entries),
         selection.mimeTypes);
@@ -1843,7 +2039,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
 
       if (self.hostedButton.hasAttribute('checked') ===
           prefs.hostedFilesDisabled && self.isOnDrive()) {
-        self.directoryModel_.rescan();
+        self.directoryModel_.rescan(false);
       }
 
       if (!prefs.hostedFilesDisabled)
@@ -1874,7 +2070,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
   };
 
   /**
-   * @param {Event} Unmount event.
+   * @param {Event} event Unmount event.
    * @private
    */
   FileManager.prototype.onExternallyUnmounted_ = function(event) {
@@ -1889,84 +2085,12 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
   };
 
   /**
-   * Shows a modal-like file viewer/editor on top of the File Manager UI.
-   *
-   * @param {HTMLElement} popup Popup element.
-   * @param {function()} closeCallback Function to call after the popup is
-   *     closed.
-   */
-  FileManager.prototype.openFilePopup = function(popup, closeCallback) {
-    this.closeFilePopup();
-    this.filePopup_ = popup;
-    this.filePopupCloseCallback_ = closeCallback;
-    this.dialogDom_.insertBefore(
-        this.filePopup_, this.dialogDom_.querySelector('#iframe-drag-area'));
-    this.filePopup_.focus();
-    this.document_.body.setAttribute('overlay-visible', '');
-    this.document_.querySelector('#iframe-drag-area').hidden = false;
-  };
-
-  /**
-   * Closes the modal-like file viewer/editor popup.
-   */
-  FileManager.prototype.closeFilePopup = function() {
-    if (this.filePopup_) {
-      this.document_.body.removeAttribute('overlay-visible');
-      this.document_.querySelector('#iframe-drag-area').hidden = true;
-      // The window resize would not be processed properly while the relevant
-      // divs had 'display:none', force resize after the layout fired.
-      setTimeout(this.onResize_.bind(this), 0);
-      if (this.filePopup_.contentWindow &&
-          this.filePopup_.contentWindow.unload) {
-        this.filePopup_.contentWindow.unload();
-      }
-
-      if (this.filePopupCloseCallback_) {
-        this.filePopupCloseCallback_();
-        this.filePopupCloseCallback_ = null;
-      }
-
-      // These operations have to be in the end, otherwise v8 crashes on an
-      // assert. See: crbug.com/224174.
-      this.dialogDom_.removeChild(this.filePopup_);
-      this.filePopup_ = null;
-    }
-  };
-
-  /**
-   * Updates visibility of the draggable app region in the modal-like file
-   * viewer/editor.
-   *
-   * @param {boolean} visible True for visible, false otherwise.
-   */
-  FileManager.prototype.onFilePopupAppRegionChanged = function(visible) {
-    if (!this.filePopup_)
-      return;
-
-    this.document_.querySelector('#iframe-drag-area').hidden = !visible;
-  };
-
-  /**
    * @return {Array.<Entry>} List of all entries in the current directory.
    */
   FileManager.prototype.getAllEntriesInCurrentDirectory = function() {
     return this.directoryModel_.getFileList().slice();
   };
 
-  FileManager.prototype.isRenamingInProgress = function() {
-    return !!this.renameInput_.currentEntry;
-  };
-
-  /**
-   * @private
-   */
-  FileManager.prototype.focusCurrentList_ = function() {
-    if (this.listType_ == FileManager.ListType.DETAIL)
-      this.table_.focus();
-    else  // this.listType_ == FileManager.ListType.THUMBNAIL)
-      this.grid_.focus();
-  };
-
   /**
    * Return DirectoryEntry of the current directory or null.
    * @return {DirectoryEntry} DirectoryEntry of the current directory. Returns
@@ -1978,20 +2102,6 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
   };
 
   /**
-   * Deletes the selected file and directories recursively.
-   */
-  FileManager.prototype.deleteSelection = function() {
-    // TODO(mtomasz): Remove this temporary dialog. crbug.com/167364
-    var entries = this.getSelection().entries;
-    var message = entries.length == 1 ?
-        strf('GALLERY_CONFIRM_DELETE_ONE', entries[0].name) :
-        strf('GALLERY_CONFIRM_DELETE_SOME', entries.length);
-    this.confirm.show(message, function() {
-      this.fileOperationManager_.deleteEntries(entries);
-    }.bind(this));
-  };
-
-  /**
    * Shows the share dialog for the selected file or directory.
    */
   FileManager.prototype.shareSelection = function() {
@@ -2047,7 +2157,8 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
 
     for (var i = 0; i < selection.entries.length; i++) {
       var selectedIndex = selection.indexes[i];
-      var listItem = this.currentList_.getListItemByIndex(selectedIndex);
+      var listItem =
+          this.ui.listContainer.currentList.getListItemByIndex(selectedIndex);
       if (listItem)
         this.blinkListItem_(listItem);
     }
@@ -2068,7 +2179,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
    * @private
    */
   FileManager.prototype.selectTargetNameInFilenameInput_ = function() {
-    var input = this.filenameInput_;
+    var input = this.ui_.dialogFooter.filenameInput;
     input.focus();
     var selectionEnd = input.value.lastIndexOf('.');
     if (selectionEnd == -1) {
@@ -2086,12 +2197,13 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
    * @private
    */
   FileManager.prototype.onDetailClick_ = function(event) {
-    if (this.isRenamingInProgress()) {
+    if (this.namingController_.isRenamingInProgress()) {
       // Don't pay attention to clicks during a rename.
       return;
     }
 
-    var listItem = this.findListItemForEvent_(event);
+    var listItem = this.ui_.listContainer.findListItemForNode(
+        event.touchedElement || event.srcElement);
     var selection = this.getSelection();
     if (!listItem || !listItem.selected || selection.totalCount != 1) {
       return;
@@ -2126,14 +2238,25 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
         tasks.executeDefault();
       return true;
     }
-    if (!this.okButton_.disabled) {
-      this.onOk_();
+    if (!this.ui_.dialogFooter.okButton.disabled) {
+      this.ui_.dialogFooter.okButton.click();
       return true;
     }
     return false;
   };
 
   /**
+   * Handles activate event of action menu item.
+   *
+   * @private
+   */
+  FileManager.prototype.onActionMenuItemActivated_ = function() {
+    var tasks = this.getSelection().tasks;
+    if (tasks)
+      tasks.execute(this.actionMenuItem_.taskId);
+  };
+
+  /**
    * Opens the suggest file dialog.
    *
    * @param {Entry} entry Entry of the file.
@@ -2149,8 +2272,8 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
       return;
     }
 
-    this.metadataCache_.get([entry], 'drive', function(props) {
-      if (!props || !props[0] || !props[0].contentMimeType) {
+    this.metadataCache_.getOne(entry, 'external', function(prop) {
+      if (!prop || !prop.contentMimeType) {
         onFailure();
         return;
       }
@@ -2159,7 +2282,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
       var splitted = util.splitExtension(basename);
       var filename = splitted[0];
       var extension = splitted[1];
-      var mime = props[0].contentMimeType;
+      var mime = prop.contentMimeType;
 
       // Returns with failure if the file has neither extension nor mime.
       if (!extension || !mime) {
@@ -2191,7 +2314,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
 
   /**
    * Called when a dialog is shown or hidden.
-   * @param {boolean} flag True if a dialog is shown, false if hidden.
+   * @param {boolean} show True if a dialog is shown, false if hidden.
    */
   FileManager.prototype.onDialogShownOrHidden = function(show) {
     if (show) {
@@ -2239,62 +2362,6 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
   };
 
   /**
-   * Update menus that move the window to the other profile's desktop.
-   * TODO(hirono): Add the GearMenu class and make it a member of the class.
-   * TODO(hirono): Handle the case where a profile is added while the menu is
-   *     opened.
-   * @private
-   */
-  FileManager.prototype.updateVisitDesktopMenus_ = function() {
-    var gearMenu = this.document_.querySelector('#gear-menu');
-    var separator =
-        this.document_.querySelector('#multi-profile-separator');
-
-    // Remove existing menu items.
-    var oldItems =
-        this.document_.querySelectorAll('#gear-menu .visit-desktop');
-    for (var i = 0; i < oldItems.length; i++) {
-      gearMenu.removeChild(oldItems[i]);
-    }
-    separator.hidden = true;
-
-    if (this.dialogType !== DialogType.FULL_PAGE)
-      return;
-
-    // Obtain the profile information.
-    chrome.fileBrowserPrivate.getProfiles(function(profiles,
-                                                   currentId,
-                                                   displayedId) {
-      // Check if the menus are needed or not.
-      var insertingPosition = separator.nextSibling;
-      if (profiles.length === 1 && profiles[0].profileId === displayedId)
-        return;
-
-      separator.hidden = false;
-      for (var i = 0; i < profiles.length; i++) {
-        var profile = profiles[i];
-        if (profile.profileId === displayedId)
-          continue;
-        var item = this.document_.createElement('menuitem');
-        cr.ui.MenuItem.decorate(item);
-        gearMenu.insertBefore(item, insertingPosition);
-        item.className = 'visit-desktop';
-        item.label = strf('VISIT_DESKTOP_OF_USER',
-                          profile.displayName,
-                          profile.profileId);
-        item.addEventListener('activate', function(inProfile, event) {
-          // Stop propagate and hide the menu manually, in order to prevent the
-          // focus from being back to the button. (cf. http://crbug.com/248479)
-          event.stopPropagation();
-          this.gearButton_.hideMenu();
-          this.gearButton_.blur();
-          chrome.fileBrowserPrivate.visitDesktop(inProfile.profileId);
-        }.bind(this, profile));
-      }
-    }.bind(this));
-  };
-
-  /**
    * Refreshes space info of the current volume.
    * @param {boolean} showLoadingCaption Whether show loading caption or not.
    * @private
@@ -2303,13 +2370,30 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
     if (!this.currentVolumeInfo_)
       return;
 
-    var volumeSpaceInfoLabel =
-        this.dialogDom_.querySelector('#volume-space-info-label');
-    var volumeSpaceInnerBar =
-        this.dialogDom_.querySelector('#volume-space-info-bar');
-    var volumeSpaceOuterBar =
-        this.dialogDom_.querySelector('#volume-space-info-bar').parentNode;
+    var volumeSpaceInfo = /** @type {!HTMLElement} */
+        (this.dialogDom_.querySelector('#volume-space-info'));
+    var volumeSpaceInfoSeparator = /** @type {!HTMLElement} */
+        (this.dialogDom_.querySelector('#volume-space-info-separator'));
+    var volumeSpaceInfoLabel = /** @type {!HTMLElement} */
+        (this.dialogDom_.querySelector('#volume-space-info-label'));
+    var volumeSpaceInnerBar = /** @type {!HTMLElement} */
+        (this.dialogDom_.querySelector('#volume-space-info-bar'));
+    var volumeSpaceOuterBar = /** @type {!HTMLElement} */
+        (this.dialogDom_.querySelector('#volume-space-info-bar').parentNode);
+
+    var currentVolumeInfo = this.currentVolumeInfo_;
+
+    // TODO(mtomasz): Add support for remaining space indication for provided
+    // file systems.
+    if (currentVolumeInfo.volumeType ==
+        VolumeManagerCommon.VolumeType.PROVIDED) {
+      volumeSpaceInfo.hidden = true;
+      volumeSpaceInfoSeparator.hidden = true;
+      return;
+    }
 
+    volumeSpaceInfo.hidden = false;
+    volumeSpaceInfoSeparator.hidden = false;
     volumeSpaceInnerBar.setAttribute('pending', '');
 
     if (showLoadingCaption) {
@@ -2317,8 +2401,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
       volumeSpaceInnerBar.style.width = '100%';
     }
 
-    var currentVolumeInfo = this.currentVolumeInfo_;
-    chrome.fileBrowserPrivate.getSizeStats(
+    chrome.fileManagerPrivate.getSizeStats(
         currentVolumeInfo.volumeId, function(result) {
           var volumeInfo = this.volumeManager_.getVolumeInfo(
               this.directoryModel_.getCurrentDirEntry());
@@ -2338,23 +2421,23 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
    * @private
    */
   FileManager.prototype.onDirectoryChanged_ = function(event) {
-    var newCurrentVolumeInfo = this.volumeManager_.getVolumeInfo(
+    var oldCurrentVolumeInfo = this.currentVolumeInfo_;
+
+    // Remember the current volume info.
+    this.currentVolumeInfo_ = this.volumeManager_.getVolumeInfo(
         event.newDirEntry);
 
     // If volume has changed, then update the gear menu.
-    if (this.currentVolumeInfo_ !== newCurrentVolumeInfo) {
+    if (oldCurrentVolumeInfo !== this.currentVolumeInfo_) {
       this.updateGearMenu_();
       // If the volume has changed, and it was previously set, then do not
       // close on unmount anymore.
-      if (this.currentVolumeInfo_)
+      if (oldCurrentVolumeInfo)
         this.closeOnUnmount_ = false;
     }
 
-    // Remember the current volume info.
-    this.currentVolumeInfo_ = newCurrentVolumeInfo;
-
     this.selectionHandler_.onFileSelectionChanged();
-    this.ui_.searchBox.clear();
+    this.searchController_.clear();
     // TODO(mtomasz): Consider remembering the selection.
     util.updateAppState(
         this.getCurrentDirectoryEntry() ?
@@ -2369,7 +2452,8 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
     this.updateTitle_();
 
     var currentEntry = this.getCurrentDirectoryEntry();
-    this.previewPanel_.currentEntry = util.isFakeEntry(currentEntry) ?
+    this.ui_.locationLine.show(currentEntry);
+    this.ui_.previewPanel.currentEntry = util.isFakeEntry(currentEntry) ?
         null : currentEntry;
   };
 
@@ -2397,16 +2481,6 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
     }
   };
 
-  FileManager.prototype.findListItemForEvent_ = function(event) {
-    return this.findListItemForNode_(event.touchedElement || event.srcElement);
-  };
-
-  FileManager.prototype.findListItemForNode_ = function(node) {
-    var item = this.currentList_.getListItemAncestor(node);
-    // TODO(serya): list should check that.
-    return item && this.currentList_.isItem(item) ? item : null;
-  };
-
   /**
    * Unload handler for the page.
    * @private
@@ -2416,21 +2490,30 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
       this.directoryModel_.dispose();
     if (this.volumeManager_)
       this.volumeManager_.dispose();
-    if (this.filePopup_ &&
-        this.filePopup_.contentWindow &&
-        this.filePopup_.contentWindow.unload)
-      this.filePopup_.contentWindow.unload(true /* exiting */);
-    if (this.progressCenterPanel_)
+    if (this.fileTransferController_) {
+      for (var i = 0;
+           i < this.fileTransferController_.pendingTaskIds.length;
+           i++) {
+        var taskId = this.fileTransferController_.pendingTaskIds[i];
+        var item =
+            this.backgroundPage_.background.progressCenter.getItemById(taskId);
+        item.message = '';
+        item.state = ProgressItemState.CANCELED;
+        this.backgroundPage_.background.progressCenter.updateItem(item);
+      }
+    }
+    if (this.progressCenterPanel_) {
       this.backgroundPage_.background.progressCenter.removePanel(
           this.progressCenterPanel_);
+    }
     if (this.fileOperationManager_) {
       if (this.onCopyProgressBound_) {
         this.fileOperationManager_.removeEventListener(
             'copy-progress', this.onCopyProgressBound_);
       }
-      if (this.onEntryChangedBound_) {
+      if (this.onEntriesChangedBound_) {
         this.fileOperationManager_.removeEventListener(
-            'entry-changed', this.onEntryChangedBound_);
+            'entries-changed', this.onEntriesChangedBound_);
       }
     }
     window.closing = true;
@@ -2438,176 +2521,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
       this.backgroundPage_.background.tryClose();
   };
 
-  FileManager.prototype.initiateRename = function() {
-    var item = this.currentList_.ensureLeadItemExists();
-    if (!item)
-      return;
-    var label = item.querySelector('.filename-label');
-    var input = this.renameInput_;
-
-    input.value = label.textContent;
-    item.setAttribute('renaming', '');
-    label.parentNode.appendChild(input);
-    input.focus();
-    var selectionEnd = input.value.lastIndexOf('.');
-    if (selectionEnd == -1) {
-      input.select();
-    } else {
-      input.selectionStart = 0;
-      input.selectionEnd = selectionEnd;
-    }
-
-    // This has to be set late in the process so we don't handle spurious
-    // blur events.
-    input.currentEntry = this.currentList_.dataModel.item(item.listIndex);
-    this.table_.startBatchUpdates();
-    this.grid_.startBatchUpdates();
-  };
-
   /**
-   * @type {Event} Key event.
-   * @private
-   */
-  FileManager.prototype.onRenameInputKeyDown_ = function(event) {
-    if (!this.isRenamingInProgress())
-      return;
-
-    // Do not move selection or lead item in list during rename.
-    if (event.keyIdentifier == 'Up' || event.keyIdentifier == 'Down') {
-      event.stopPropagation();
-    }
-
-    switch (util.getKeyModifiers(event) + event.keyIdentifier) {
-      case 'U+001B':  // Escape
-        this.cancelRename_();
-        event.preventDefault();
-        break;
-
-      case 'Enter':
-        this.commitRename_();
-        event.preventDefault();
-        break;
-    }
-  };
-
-  /**
-   * @type {Event} Blur event.
-   * @private
-   */
-  FileManager.prototype.onRenameInputBlur_ = function(event) {
-    if (this.isRenamingInProgress() && !this.renameInput_.validation_)
-      this.commitRename_();
-  };
-
-  /**
-   * @private
-   */
-  FileManager.prototype.commitRename_ = function() {
-    var input = this.renameInput_;
-    var entry = input.currentEntry;
-    var newName = input.value;
-
-    if (newName == entry.name) {
-      this.cancelRename_();
-      return;
-    }
-
-    var renamedItemElement = this.findListItemForNode_(this.renameInput_);
-    var nameNode = renamedItemElement.querySelector('.filename-label');
-
-    input.validation_ = true;
-    var validationDone = function(valid) {
-      input.validation_ = false;
-
-      if (!valid) {
-        // Cancel rename if it fails to restore focus from alert dialog.
-        // Otherwise, just cancel the commitment and continue to rename.
-        if (this.document_.activeElement != input)
-          this.cancelRename_();
-        return;
-      }
-
-      // Validation succeeded. Do renaming.
-      this.renameInput_.currentEntry = null;
-      if (this.renameInput_.parentNode)
-        this.renameInput_.parentNode.removeChild(this.renameInput_);
-      renamedItemElement.setAttribute('renaming', 'provisional');
-
-      // Optimistically apply new name immediately to avoid flickering in
-      // case of success.
-      nameNode.textContent = newName;
-
-      util.rename(
-          entry, newName,
-          function(newEntry) {
-            this.directoryModel_.onRenameEntry(entry, newEntry);
-            renamedItemElement.removeAttribute('renaming');
-            this.table_.endBatchUpdates();
-            this.grid_.endBatchUpdates();
-          }.bind(this),
-          function(error) {
-            // Write back to the old name.
-            nameNode.textContent = entry.name;
-            renamedItemElement.removeAttribute('renaming');
-            this.table_.endBatchUpdates();
-            this.grid_.endBatchUpdates();
-
-            // Show error dialog.
-            var message;
-            if (error.name == util.FileError.PATH_EXISTS_ERR ||
-                error.name == util.FileError.TYPE_MISMATCH_ERR) {
-              // Check the existing entry is file or not.
-              // 1) If the entry is a file:
-              //   a) If we get PATH_EXISTS_ERR, a file exists.
-              //   b) If we get TYPE_MISMATCH_ERR, a directory exists.
-              // 2) If the entry is a directory:
-              //   a) If we get PATH_EXISTS_ERR, a directory exists.
-              //   b) If we get TYPE_MISMATCH_ERR, a file exists.
-              message = strf(
-                  (entry.isFile && error.name ==
-                      util.FileError.PATH_EXISTS_ERR) ||
-                  (!entry.isFile && error.name ==
-                      util.FileError.TYPE_MISMATCH_ERR) ?
-                      'FILE_ALREADY_EXISTS' :
-                      'DIRECTORY_ALREADY_EXISTS',
-                  newName);
-            } else {
-              message = strf('ERROR_RENAMING', entry.name,
-                             util.getFileErrorString(error.name));
-            }
-
-            this.alert.show(message);
-          }.bind(this));
-    };
-
-    // TODO(haruki): this.getCurrentDirectoryEntry() might not return the actual
-    // parent if the directory content is a search result. Fix it to do proper
-    // validation.
-    this.validateFileName_(this.getCurrentDirectoryEntry(),
-                           newName,
-                           validationDone.bind(this));
-  };
-
-  /**
-   * @private
-   */
-  FileManager.prototype.cancelRename_ = function() {
-    this.renameInput_.currentEntry = null;
-
-    var item = this.findListItemForNode_(this.renameInput_);
-    if (item)
-      item.removeAttribute('renaming');
-
-    var parent = this.renameInput_.parentNode;
-    if (parent)
-      parent.removeChild(this.renameInput_);
-
-    this.table_.endBatchUpdates();
-    this.grid_.endBatchUpdates();
-  };
-
-  /**
-   * @param {Event} Key event.
    * @private
    */
   FileManager.prototype.onFilenameInputInput_ = function() {
@@ -2615,205 +2529,33 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
   };
 
   /**
-   * @param {Event} Key event.
+   * @param {Event} event Key event.
    * @private
    */
   FileManager.prototype.onFilenameInputKeyDown_ = function(event) {
     if ((util.getKeyModifiers(event) + event.keyCode) === '13' /* Enter */)
-      this.okButton_.click();
+      this.ui_.dialogFooter.okButton.click();
   };
 
   /**
-   * @param {Event} Focus event.
+   * @param {Event} event Focus event.
    * @private
    */
   FileManager.prototype.onFilenameInputFocus_ = function(event) {
-    var input = this.filenameInput_;
+    var input = this.ui_.dialogFooter.filenameInput;
 
     // On focus we want to select everything but the extension, but
     // Chrome will select-all after the focus event completes.  We
     // schedule a timeout to alter the focus after that happens.
     setTimeout(function() {
-        var selectionEnd = input.value.lastIndexOf('.');
-        if (selectionEnd == -1) {
-          input.select();
-        } else {
-          input.selectionStart = 0;
-          input.selectionEnd = selectionEnd;
-        }
-    }, 0);
-  };
-
-  /**
-   * @private
-   */
-  FileManager.prototype.onScanStarted_ = function() {
-    if (this.scanInProgress_) {
-      this.table_.list.endBatchUpdates();
-      this.grid_.endBatchUpdates();
-    }
-
-    if (this.commandHandler)
-      this.commandHandler.updateAvailability();
-    this.table_.list.startBatchUpdates();
-    this.grid_.startBatchUpdates();
-    this.scanInProgress_ = true;
-
-    this.scanUpdatedAtLeastOnceOrCompleted_ = false;
-    if (this.scanCompletedTimer_) {
-      clearTimeout(this.scanCompletedTimer_);
-      this.scanCompletedTimer_ = null;
-    }
-
-    if (this.scanUpdatedTimer_) {
-      clearTimeout(this.scanUpdatedTimer_);
-      this.scanUpdatedTimer_ = null;
-    }
-
-    if (this.spinner_.hidden) {
-      this.cancelSpinnerTimeout_();
-      this.showSpinnerTimeout_ =
-          setTimeout(this.showSpinner_.bind(this, true), 500);
-    }
-  };
-
-  /**
-   * @private
-   */
-  FileManager.prototype.onScanCompleted_ = function() {
-    if (!this.scanInProgress_) {
-      console.error('Scan-completed event recieved. But scan is not started.');
-      return;
-    }
-
-    if (this.commandHandler)
-      this.commandHandler.updateAvailability();
-    this.hideSpinnerLater_();
-
-    if (this.scanUpdatedTimer_) {
-      clearTimeout(this.scanUpdatedTimer_);
-      this.scanUpdatedTimer_ = null;
-    }
-
-    // To avoid flickering postpone updating the ui by a small amount of time.
-    // There is a high chance, that metadata will be received within 50 ms.
-    this.scanCompletedTimer_ = setTimeout(function() {
-      // Check if batch updates are already finished by onScanUpdated_().
-      if (!this.scanUpdatedAtLeastOnceOrCompleted_) {
-        this.scanUpdatedAtLeastOnceOrCompleted_ = true;
-        this.updateMiddleBarVisibility_();
-      }
-
-      this.scanInProgress_ = false;
-      this.table_.list.endBatchUpdates();
-      this.grid_.endBatchUpdates();
-      this.scanCompletedTimer_ = null;
-    }.bind(this), 50);
-  };
-
-  /**
-   * @private
-   */
-  FileManager.prototype.onScanUpdated_ = function() {
-    if (!this.scanInProgress_) {
-      console.error('Scan-updated event recieved. But scan is not started.');
-      return;
-    }
-
-    if (this.scanUpdatedTimer_ || this.scanCompletedTimer_)
-      return;
-
-    // Show contents incrementally by finishing batch updated, but only after
-    // 200ms elapsed, to avoid flickering when it is not necessary.
-    this.scanUpdatedTimer_ = setTimeout(function() {
-      // We need to hide the spinner only once.
-      if (!this.scanUpdatedAtLeastOnceOrCompleted_) {
-        this.scanUpdatedAtLeastOnceOrCompleted_ = true;
-        this.hideSpinnerLater_();
-        this.updateMiddleBarVisibility_();
-      }
-
-      // Update the UI.
-      if (this.scanInProgress_) {
-        this.table_.list.endBatchUpdates();
-        this.grid_.endBatchUpdates();
-        this.table_.list.startBatchUpdates();
-        this.grid_.startBatchUpdates();
+      var selectionEnd = input.value.lastIndexOf('.');
+      if (selectionEnd == -1) {
+        input.select();
+      } else {
+        input.selectionStart = 0;
+        input.selectionEnd = selectionEnd;
       }
-      this.scanUpdatedTimer_ = null;
-    }.bind(this), 200);
-  };
-
-  /**
-   * @private
-   */
-  FileManager.prototype.onScanCancelled_ = function() {
-    if (!this.scanInProgress_) {
-      console.error('Scan-cancelled event recieved. But scan is not started.');
-      return;
-    }
-
-    if (this.commandHandler)
-      this.commandHandler.updateAvailability();
-    this.hideSpinnerLater_();
-    if (this.scanCompletedTimer_) {
-      clearTimeout(this.scanCompletedTimer_);
-      this.scanCompletedTimer_ = null;
-    }
-    if (this.scanUpdatedTimer_) {
-      clearTimeout(this.scanUpdatedTimer_);
-      this.scanUpdatedTimer_ = null;
-    }
-    // Finish unfinished batch updates.
-    if (!this.scanUpdatedAtLeastOnceOrCompleted_) {
-      this.scanUpdatedAtLeastOnceOrCompleted_ = true;
-      this.updateMiddleBarVisibility_();
-    }
-
-    this.scanInProgress_ = false;
-    this.table_.list.endBatchUpdates();
-    this.grid_.endBatchUpdates();
-  };
-
-  /**
-   * Handle the 'rescan-completed' from the DirectoryModel.
-   * @private
-   */
-  FileManager.prototype.onRescanCompleted_ = function() {
-    this.selectionHandler_.onFileSelectionChanged();
-  };
-
-  /**
-   * @private
-   */
-  FileManager.prototype.cancelSpinnerTimeout_ = function() {
-    if (this.showSpinnerTimeout_) {
-      clearTimeout(this.showSpinnerTimeout_);
-      this.showSpinnerTimeout_ = null;
-    }
-  };
-
-  /**
-   * @private
-   */
-  FileManager.prototype.hideSpinnerLater_ = function() {
-    this.cancelSpinnerTimeout_();
-    this.showSpinner_(false);
-  };
-
-  /**
-   * @param {boolean} on True to show, false to hide.
-   * @private
-   */
-  FileManager.prototype.showSpinner_ = function(on) {
-    if (on && this.directoryModel_ && this.directoryModel_.isScanning())
-      this.spinner_.hidden = false;
-
-    if (!on && (!this.directoryModel_ ||
-                !this.directoryModel_.isScanning() ||
-                this.directoryModel_.getFileList().length != 0)) {
-      this.spinner_.hidden = true;
-    }
+    }, 0);
   };
 
   FileManager.prototype.createNewFolder = function() {
@@ -2851,35 +2593,29 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
     }
 
     var self = this;
-    var list = self.currentList_;
-    var tryCreate = function() {
-    };
+    var list = self.ui_.listContainer.currentList;
 
     var onSuccess = function(entry) {
       metrics.recordUserAction('CreateNewFolder');
       list.selectedItem = entry;
 
-      self.table_.list.endBatchUpdates();
-      self.grid_.endBatchUpdates();
+      self.ui_.listContainer.endBatchUpdates();
 
-      self.initiateRename();
+      self.namingController_.initiateRename();
     };
 
     var onError = function(error) {
-      self.table_.list.endBatchUpdates();
-      self.grid_.endBatchUpdates();
+      self.ui_.listContainer.endBatchUpdates();
 
       self.alert.show(strf('ERROR_CREATING_FOLDER', current(),
                            util.getFileErrorString(error.name)));
     };
 
     var onAbort = function() {
-      self.table_.list.endBatchUpdates();
-      self.grid_.endBatchUpdates();
+      self.ui_.listContainer.endBatchUpdates();
     };
 
-    this.table_.list.startBatchUpdates();
-    this.grid_.startBatchUpdates();
+    this.ui_.listContainer.startBatchUpdates();
     this.directoryModel_.createDirectory(current(),
                                          onSuccess,
                                          onError,
@@ -2887,29 +2623,19 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
   };
 
   /**
+   * Handles click event on the toggle-view button.
    * @param {Event} event Click event.
    * @private
    */
-  FileManager.prototype.onDetailViewButtonClick_ = function(event) {
-    // Stop propagate and hide the menu manually, in order to prevent the focus
-    // from being back to the button. (cf. http://crbug.com/248479)
-    event.stopPropagation();
-    this.gearButton_.hideMenu();
-    this.gearButton_.blur();
-    this.setListType(FileManager.ListType.DETAIL);
-  };
+  FileManager.prototype.onToggleViewButtonClick_ = function(event) {
+    if (this.ui_.listContainer.currentListType ===
+        ListContainer.ListType.DETAIL) {
+      this.setListType(ListContainer.ListType.THUMBNAIL);
+    } else {
+      this.setListType(ListContainer.ListType.DETAIL);
+    }
 
-  /**
-   * @param {Event} event Click event.
-   * @private
-   */
-  FileManager.prototype.onThumbnailViewButtonClick_ = function(event) {
-    // Stop propagate and hide the menu manually, in order to prevent the focus
-    // from being back to the button. (cf. http://crbug.com/248479)
-    event.stopPropagation();
-    this.gearButton_.hideMenu();
-    this.gearButton_.blur();
-    this.setListType(FileManager.ListType.THUMBNAIL);
+    event.target.blur();
   };
 
   /**
@@ -2923,23 +2649,23 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
     if (event.keyCode === 17)  // Ctrl
       this.pressingCtrl_ = true;
 
-    if (event.srcElement === this.renameInput_) {
+    if (event.srcElement === this.ui_.listContainer.renameInput) {
       // Ignore keydown handler in the rename input box.
       return;
     }
 
-    switch (util.getKeyModifiers(event) + event.keyCode) {
-      case 'Ctrl-190':  // Ctrl-. => Toggle filter files.
+    switch (util.getKeyModifiers(event) + event.keyIdentifier) {
+      case 'Ctrl-U+00BE':  // Ctrl-. => Toggle filter files.
         this.fileFilter_.setFilterHidden(
             !this.fileFilter_.isFilterHiddenOn());
         event.preventDefault();
         return;
 
-      case '27':  // Escape => Cancel dialog.
+      case 'U+001B':  // Escape => Cancel dialog.
         if (this.dialogType != DialogType.FULL_PAGE) {
           // If there is nothing else for ESC to do, then cancel the dialog.
           event.preventDefault();
-          this.cancelButton_.click();
+          this.ui_.dialogFooter.cancelButton.click();
         }
         break;
     }
@@ -2963,13 +2689,8 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
    * @private
    */
   FileManager.prototype.onListKeyDown_ = function(event) {
-    if (event.srcElement.tagName == 'INPUT') {
-      // Ignore keydown handler in the rename input box.
-      return;
-    }
-
-    switch (util.getKeyModifiers(event) + event.keyCode) {
-      case '8':  // Backspace => Up one directory.
+    switch (util.getKeyModifiers(event) + event.keyIdentifier) {
+      case 'U+0008':  // Backspace => Up one directory.
         event.preventDefault();
         // TODO(mtomasz): Use Entry.getParent() instead.
         if (!this.getCurrentDirectoryEntry())
@@ -2985,78 +2706,25 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
         }
         break;
 
-      case '13':  // Enter => Change directory or perform default action.
+      case 'Enter':  // Enter => Change directory or perform default action.
         // TODO(dgozman): move directory action to dispatchSelectionAction.
         var selection = this.getSelection();
-        if (selection.totalCount == 1 &&
+        if (selection.totalCount === 1 &&
             selection.entries[0].isDirectory &&
             !DialogType.isFolderDialog(this.dialogType)) {
-          event.preventDefault();
-          this.onDirectoryAction_(selection.entries[0]);
+          var item = this.ui.listContainer.currentList.getListItemByIndex(
+              selection.indexes[0]);
+          // If the item is in renaming process, we don't allow to change
+          // directory.
+          if (!item.hasAttribute('renaming')) {
+            event.preventDefault();
+            this.onDirectoryAction_(selection.entries[0]);
+          }
         } else if (this.dispatchSelectionAction_()) {
           event.preventDefault();
         }
         break;
     }
-
-    switch (event.keyIdentifier) {
-      case 'Home':
-      case 'End':
-      case 'Up':
-      case 'Down':
-      case 'Left':
-      case 'Right':
-        // When navigating with keyboard we hide the distracting mouse hover
-        // highlighting until the user moves the mouse again.
-        this.setNoHover_(true);
-        break;
-    }
-  };
-
-  /**
-   * Suppress/restore hover highlighting in the list container.
-   * @param {boolean} on True to temporarity hide hover state.
-   * @private
-   */
-  FileManager.prototype.setNoHover_ = function(on) {
-    if (on) {
-      this.listContainer_.classList.add('nohover');
-    } else {
-      this.listContainer_.classList.remove('nohover');
-    }
-  };
-
-  /**
-   * KeyPress event handler for the div#list-container element.
-   * @param {Event} event Key event.
-   * @private
-   */
-  FileManager.prototype.onListKeyPress_ = function(event) {
-    if (event.srcElement.tagName == 'INPUT') {
-      // Ignore keypress handler in the rename input box.
-      return;
-    }
-
-    if (event.ctrlKey || event.metaKey || event.altKey)
-      return;
-
-    var now = new Date();
-    var char = String.fromCharCode(event.charCode).toLowerCase();
-    var text = now - this.textSearchState_.date > 1000 ? '' :
-        this.textSearchState_.text;
-    this.textSearchState_ = {text: text + char, date: now};
-
-    this.doTextSearch_();
-  };
-
-  /**
-   * Mousemove event handler for the div#list-container element.
-   * @param {Event} event Mouse event.
-   * @private
-   */
-  FileManager.prototype.onListMouseMove_ = function(event) {
-    // The user grabbed the mouse, restore the hover highlighting.
-    this.setNoHover_(false);
   };
 
   /**
@@ -3064,372 +2732,43 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
    * starting with entered text (case-insensitive).
    * @private
    */
-  FileManager.prototype.doTextSearch_ = function() {
-    var text = this.textSearchState_.text;
-    if (!text)
-      return;
-
+  FileManager.prototype.onTextSearch_ = function() {
+    var text = this.ui_.listContainer.textSearchState.text;
     var dm = this.directoryModel_.getFileList();
     for (var index = 0; index < dm.length; ++index) {
       var name = dm.item(index).name;
       if (name.substring(0, text.length).toLowerCase() == text) {
-        this.currentList_.selectionModel.selectedIndexes = [index];
+        this.ui.listContainer.currentList.selectionModel.selectedIndexes =
+            [index];
         return;
       }
     }
 
-    this.textSearchState_.text = '';
-  };
-
-  /**
-   * Handle a click of the cancel button.  Closes the window.
-   * TODO(jamescook): Make unload handler work automatically, crbug.com/104811
-   *
-   * @param {Event} event The click event.
-   * @private
-   */
-  FileManager.prototype.onCancel_ = function(event) {
-    chrome.fileBrowserPrivate.cancelDialog();
-    window.close();
-  };
-
-  /**
-   * Resolves selected file urls returned from an Open dialog.
-   *
-   * For drive files this involves some special treatment.
-   * Starts getting drive files if needed.
-   *
-   * @param {Array.<string>} fileUrls Drive URLs.
-   * @param {function(Array.<string>)} callback To be called with fixed URLs.
-   * @private
-   */
-  FileManager.prototype.resolveSelectResults_ = function(fileUrls, callback) {
-    if (this.isOnDrive()) {
-      chrome.fileBrowserPrivate.getDriveFiles(
-        fileUrls,
-        function(localPaths) {
-          callback(fileUrls);
-        });
-    } else {
-      callback(fileUrls);
-    }
-  };
-
-  /**
-   * Closes this modal dialog with some files selected.
-   * TODO(jamescook): Make unload handler work automatically, crbug.com/104811
-   * @param {Object} selection Contains urls, filterIndex and multiple fields.
-   * @private
-   */
-  FileManager.prototype.callSelectFilesApiAndClose_ = function(selection) {
-    var self = this;
-    function callback() {
-      window.close();
-    }
-    if (selection.multiple) {
-      chrome.fileBrowserPrivate.selectFiles(
-          selection.urls, this.params_.shouldReturnLocalPath, callback);
-    } else {
-      var forOpening = (this.dialogType != DialogType.SELECT_SAVEAS_FILE);
-      chrome.fileBrowserPrivate.selectFile(
-          selection.urls[0], selection.filterIndex, forOpening,
-          this.params_.shouldReturnLocalPath, callback);
-    }
-  };
-
-  /**
-   * Tries to close this modal dialog with some files selected.
-   * Performs preprocessing if needed (e.g. for Drive).
-   * @param {Object} selection Contains urls, filterIndex and multiple fields.
-   * @private
-   */
-  FileManager.prototype.selectFilesAndClose_ = function(selection) {
-    if (!this.isOnDrive() ||
-        this.dialogType == DialogType.SELECT_SAVEAS_FILE) {
-      setTimeout(this.callSelectFilesApiAndClose_.bind(this, selection), 0);
-      return;
-    }
-
-    var shade = this.document_.createElement('div');
-    shade.className = 'shade';
-    var footer = this.dialogDom_.querySelector('.button-panel');
-    var progress = footer.querySelector('.progress-track');
-    progress.style.width = '0%';
-    var cancelled = false;
-
-    var progressMap = {};
-    var filesStarted = 0;
-    var filesTotal = selection.urls.length;
-    for (var index = 0; index < selection.urls.length; index++) {
-      progressMap[selection.urls[index]] = -1;
-    }
-    var lastPercent = 0;
-    var bytesTotal = 0;
-    var bytesDone = 0;
-
-    var onFileTransfersUpdated = function(statusList) {
-      for (var index = 0; index < statusList.length; index++) {
-        var status = statusList[index];
-        var escaped = encodeURI(status.fileUrl);
-        if (!(escaped in progressMap)) continue;
-        if (status.total == -1) continue;
-
-        var old = progressMap[escaped];
-        if (old == -1) {
-          // -1 means we don't know file size yet.
-          bytesTotal += status.total;
-          filesStarted++;
-          old = 0;
-        }
-        bytesDone += status.processed - old;
-        progressMap[escaped] = status.processed;
-      }
-
-      var percent = bytesTotal == 0 ? 0 : bytesDone / bytesTotal;
-      // For files we don't have information about, assume the progress is zero.
-      percent = percent * filesStarted / filesTotal * 100;
-      // Do not decrease the progress. This may happen, if first downloaded
-      // file is small, and the second one is large.
-      lastPercent = Math.max(lastPercent, percent);
-      progress.style.width = lastPercent + '%';
-    }.bind(this);
-
-    var setup = function() {
-      this.document_.querySelector('.dialog-container').appendChild(shade);
-      setTimeout(function() { shade.setAttribute('fadein', 'fadein') }, 100);
-      footer.setAttribute('progress', 'progress');
-      this.cancelButton_.removeEventListener('click', this.onCancelBound_);
-      this.cancelButton_.addEventListener('click', onCancel);
-      chrome.fileBrowserPrivate.onFileTransfersUpdated.addListener(
-          onFileTransfersUpdated);
-    }.bind(this);
-
-    var cleanup = function() {
-      shade.parentNode.removeChild(shade);
-      footer.removeAttribute('progress');
-      this.cancelButton_.removeEventListener('click', onCancel);
-      this.cancelButton_.addEventListener('click', this.onCancelBound_);
-      chrome.fileBrowserPrivate.onFileTransfersUpdated.removeListener(
-          onFileTransfersUpdated);
-    }.bind(this);
-
-    var onCancel = function() {
-      cancelled = true;
-      // According to API cancel may fail, but there is no proper UI to reflect
-      // this. So, we just silently assume that everything is cancelled.
-      chrome.fileBrowserPrivate.cancelFileTransfers(
-          selection.urls, function(response) {});
-      cleanup();
-    }.bind(this);
-
-    var onResolved = function(resolvedUrls) {
-      if (cancelled) return;
-      cleanup();
-      selection.urls = resolvedUrls;
-      // Call next method on a timeout, as it's unsafe to
-      // close a window from a callback.
-      setTimeout(this.callSelectFilesApiAndClose_.bind(this, selection), 0);
-    }.bind(this);
-
-    var onProperties = function(properties) {
-      for (var i = 0; i < properties.length; i++) {
-        if (!properties[i] || properties[i].present) {
-          // For files already in GCache, we don't get any transfer updates.
-          filesTotal--;
-        }
-      }
-      this.resolveSelectResults_(selection.urls, onResolved);
-    }.bind(this);
-
-    setup();
-
-    // TODO(mtomasz): Use Entry instead of URLs, if possible.
-    util.URLsToEntries(selection.urls, function(entries) {
-      this.metadataCache_.get(entries, 'drive', onProperties);
-    }.bind(this));
-  };
-
-  /**
-   * Handle a click of the ok button.
-   *
-   * The ok button has different UI labels depending on the type of dialog, but
-   * in code it's always referred to as 'ok'.
-   *
-   * @param {Event} event The click event.
-   * @private
-   */
-  FileManager.prototype.onOk_ = function(event) {
-    if (this.dialogType == DialogType.SELECT_SAVEAS_FILE) {
-      // Save-as doesn't require a valid selection from the list, since
-      // we're going to take the filename from the text input.
-      var filename = this.filenameInput_.value;
-      if (!filename)
-        throw new Error('Missing filename!');
-
-      var directory = this.getCurrentDirectoryEntry();
-      this.validateFileName_(directory, filename, function(isValid) {
-        if (!isValid)
-          return;
-
-        if (util.isFakeEntry(directory)) {
-          // Can't save a file into a fake directory.
-          return;
-        }
-
-        var selectFileAndClose = function() {
-          // TODO(mtomasz): Clean this up by avoiding constructing a URL
-          //                via string concatenation.
-          var currentDirUrl = directory.toURL();
-          if (currentDirUrl.charAt(currentDirUrl.length - 1) != '/')
-          currentDirUrl += '/';
-          this.selectFilesAndClose_({
-            urls: [currentDirUrl + encodeURIComponent(filename)],
-            multiple: false,
-            filterIndex: this.getSelectedFilterIndex_(filename)
-          });
-        }.bind(this);
-
-        directory.getFile(
-            filename, {create: false},
-            function(entry) {
-              // An existing file is found. Show confirmation dialog to
-              // overwrite it. If the user select "OK" on the dialog, save it.
-              this.confirm.show(strf('CONFIRM_OVERWRITE_FILE', filename),
-                                selectFileAndClose);
-            }.bind(this),
-            function(error) {
-              if (error.name == util.FileError.NOT_FOUND_ERR) {
-                // The file does not exist, so it should be ok to create a
-                // new file.
-                selectFileAndClose();
-                return;
-              }
-              if (error.name == util.FileError.TYPE_MISMATCH_ERR) {
-                // An directory is found.
-                // Do not allow to overwrite directory.
-                this.alert.show(strf('DIRECTORY_ALREADY_EXISTS', filename));
-                return;
-              }
-
-              // Unexpected error.
-              console.error('File save failed: ' + error.code);
-            }.bind(this));
-      }.bind(this));
-      return;
-    }
-
-    var files = [];
-    var selectedIndexes = this.currentList_.selectionModel.selectedIndexes;
-
-    if (DialogType.isFolderDialog(this.dialogType) &&
-        selectedIndexes.length == 0) {
-      var url = this.getCurrentDirectoryEntry().toURL();
-      var singleSelection = {
-        urls: [url],
-        multiple: false,
-        filterIndex: this.getSelectedFilterIndex_()
-      };
-      this.selectFilesAndClose_(singleSelection);
-      return;
-    }
-
-    // All other dialog types require at least one selected list item.
-    // The logic to control whether or not the ok button is enabled should
-    // prevent us from ever getting here, but we sanity check to be sure.
-    if (!selectedIndexes.length)
-      throw new Error('Nothing selected!');
-
-    var dm = this.directoryModel_.getFileList();
-    for (var i = 0; i < selectedIndexes.length; i++) {
-      var entry = dm.item(selectedIndexes[i]);
-      if (!entry) {
-        console.error('Error locating selected file at index: ' + i);
-        continue;
-      }
-
-      files.push(entry.toURL());
-    }
-
-    // Multi-file selection has no other restrictions.
-    if (this.dialogType == DialogType.SELECT_OPEN_MULTI_FILE) {
-      var multipleSelection = {
-        urls: files,
-        multiple: true
-      };
-      this.selectFilesAndClose_(multipleSelection);
-      return;
-    }
-
-    // Everything else must have exactly one.
-    if (files.length > 1)
-      throw new Error('Too many files selected!');
-
-    var selectedEntry = dm.item(selectedIndexes[0]);
-
-    if (DialogType.isFolderDialog(this.dialogType)) {
-      if (!selectedEntry.isDirectory)
-        throw new Error('Selected entry is not a folder!');
-    } else if (this.dialogType == DialogType.SELECT_OPEN_FILE) {
-      if (!selectedEntry.isFile)
-        throw new Error('Selected entry is not a file!');
-    }
-
-    var singleSelection = {
-      urls: [files[0]],
-      multiple: false,
-      filterIndex: this.getSelectedFilterIndex_()
-    };
-    this.selectFilesAndClose_(singleSelection);
+    this.ui_.listContainer.textSearchState.text = '';
   };
 
   /**
    * Verifies the user entered name for file or folder to be created or
-   * renamed to. Name restrictions must correspond to File API restrictions
-   * (see DOMFilePath::isValidPath). Curernt WebKit implementation is
-   * out of date (spec is
-   * http://dev.w3.org/2009/dap/file-system/file-dir-sys.html, 8.3) and going to
-   * be fixed. Shows message box if the name is invalid.
-   *
-   * It also verifies if the name length is in the limit of the filesystem.
+   * renamed to. See also util.validateFileName.
    *
    * @param {DirectoryEntry} parentEntry The URL of the parent directory entry.
    * @param {string} name New file or folder name.
-   * @param {function} onDone Function to invoke when user closes the
+   * @param {function(boolean)} onDone Function to invoke when user closes the
    *    warning box or immediatelly if file name is correct. If the name was
    *    valid it is passed true, and false otherwise.
    * @private
    */
   FileManager.prototype.validateFileName_ = function(
       parentEntry, name, onDone) {
-    var msg;
-    var testResult = /[\/\\\<\>\:\?\*\"\|]/.exec(name);
-    if (testResult) {
-      msg = strf('ERROR_INVALID_CHARACTER', testResult[0]);
-    } else if (/^\s*$/i.test(name)) {
-      msg = str('ERROR_WHITESPACE_NAME');
-    } else if (/^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])$/i.test(name)) {
-      msg = str('ERROR_RESERVED_NAME');
-    } else if (this.fileFilter_.isFilterHiddenOn() && name[0] == '.') {
-      msg = str('ERROR_HIDDEN_NAME');
-    }
-
-    if (msg) {
-      this.alert.show(msg, function() {
-        onDone(false);
-      });
-      return;
-    }
-
-    var self = this;
-    chrome.fileBrowserPrivate.validatePathNameLength(
-        parentEntry.toURL(), name, function(valid) {
-          if (!valid) {
-            self.alert.show(str('ERROR_LONG_NAME'),
-                            function() { onDone(false); });
-          } else {
-            onDone(true);
-          }
-        });
+    var fileNameErrorPromise = util.validateFileName(
+        parentEntry,
+        name,
+        this.fileFilter_.isFilterHiddenOn());
+    fileNameErrorPromise.then(onDone.bind(null, true), function(message) {
+      this.alert.show(message, onDone.bind(null, false));
+    }.bind(this)).catch(function(error) {
+      console.error(error.stack || error);
+    });
   };
 
   /**
@@ -3439,7 +2778,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
     // If checked, the sync is disabled.
     var nowCellularDisabled = this.syncButton.hasAttribute('checked');
     var changeInfo = {cellularDisabled: !nowCellularDisabled};
-    chrome.fileBrowserPrivate.setPreferences(changeInfo);
+    chrome.fileManagerPrivate.setPreferences(changeInfo);
   };
 
   /**
@@ -3454,221 +2793,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
     */
     var changeInfo = {};
     changeInfo['hostedFilesDisabled'] = !nowHostedFilesDisabled;
-    chrome.fileBrowserPrivate.setPreferences(changeInfo);
-  };
-
-  /**
-   * Invoked when the search box is changed.
-   *
-   * @param {Event} event The changed event.
-   * @private
-   */
-  FileManager.prototype.onSearchBoxUpdate_ = function(event) {
-    var searchString = this.searchBox_.value;
-
-    if (this.isOnDrive()) {
-      // When the search text is changed, finishes the search and showes back
-      // the last directory by passing an empty string to
-      // {@code DirectoryModel.search()}.
-      if (this.directoryModel_.isSearching() &&
-          this.lastSearchQuery_ != searchString) {
-        this.doSearch('');
-      }
-
-      // On drive, incremental search is not invoked since we have an auto-
-      // complete suggestion instead.
-      return;
-    }
-
-    this.search_(searchString);
-  };
-
-  /**
-   * Handle the search clear button click.
-   * @private
-   */
-  FileManager.prototype.onSearchClearButtonClick_ = function() {
-    this.ui_.searchBox.clear();
-    this.onSearchBoxUpdate_();
-  };
-
-  /**
-   * Search files and update the list with the search result.
-   *
-   * @param {string} searchString String to be searched with.
-   * @private
-   */
-  FileManager.prototype.search_ = function(searchString) {
-    var noResultsDiv = this.document_.getElementById('no-search-results');
-
-    var reportEmptySearchResults = function() {
-      if (this.directoryModel_.getFileList().length === 0) {
-        // The string 'SEARCH_NO_MATCHING_FILES_HTML' may contain HTML tags,
-        // hence we escapes |searchString| here.
-        var html = strf('SEARCH_NO_MATCHING_FILES_HTML',
-                        util.htmlEscape(searchString));
-        noResultsDiv.innerHTML = html;
-        noResultsDiv.setAttribute('show', 'true');
-      } else {
-        noResultsDiv.removeAttribute('show');
-      }
-    };
-
-    var hideNoResultsDiv = function() {
-      noResultsDiv.removeAttribute('show');
-    };
-
-    this.doSearch(searchString,
-                  reportEmptySearchResults.bind(this),
-                  hideNoResultsDiv.bind(this));
-  };
-
-  /**
-   * Performs search and displays results.
-   *
-   * @param {string} query Query that will be searched for.
-   * @param {function()=} opt_onSearchRescan Function that will be called when
-   *     the search directory is rescanned (i.e. search results are displayed).
-   * @param {function()=} opt_onClearSearch Function to be called when search
-   *     state gets cleared.
-   */
-  FileManager.prototype.doSearch = function(
-      searchString, opt_onSearchRescan, opt_onClearSearch) {
-    var onSearchRescan = opt_onSearchRescan || function() {};
-    var onClearSearch = opt_onClearSearch || function() {};
-
-    this.lastSearchQuery_ = searchString;
-    this.directoryModel_.search(searchString, onSearchRescan, onClearSearch);
-  };
-
-  /**
-   * Requests autocomplete suggestions for files on Drive.
-   * Once the suggestions are returned, the autocomplete popup will show up.
-   *
-   * @param {string} query The text to autocomplete from.
-   * @private
-   */
-  FileManager.prototype.requestAutocompleteSuggestions_ = function(query) {
-    query = query.trimLeft();
-
-    // Only Drive supports auto-compelete
-    if (!this.isOnDrive())
-      return;
-
-    // Remember the most recent query. If there is an other request in progress,
-    // then it's result will be discarded and it will call a new request for
-    // this query.
-    this.lastAutocompleteQuery_ = query;
-    if (this.autocompleteSuggestionsBusy_)
-      return;
-
-    // The autocomplete list should be resized and repositioned here as the
-    // search box is resized when it's focused.
-    this.autocompleteList_.syncWidthAndPositionToInput();
-
-    if (!query) {
-      this.autocompleteList_.suggestions = [];
-      return;
-    }
-
-    var headerItem = {isHeaderItem: true, searchQuery: query};
-    if (!this.autocompleteList_.dataModel ||
-        this.autocompleteList_.dataModel.length == 0)
-      this.autocompleteList_.suggestions = [headerItem];
-    else
-      // Updates only the head item to prevent a flickering on typing.
-      this.autocompleteList_.dataModel.splice(0, 1, headerItem);
-
-    this.autocompleteSuggestionsBusy_ = true;
-
-    var searchParams = {
-      'query': query,
-      'types': 'ALL',
-      'maxResults': 4
-    };
-    chrome.fileBrowserPrivate.searchDriveMetadata(
-      searchParams,
-      function(suggestions) {
-        this.autocompleteSuggestionsBusy_ = false;
-
-        // Discard results for previous requests and fire a new search
-        // for the most recent query.
-        if (query != this.lastAutocompleteQuery_) {
-          this.requestAutocompleteSuggestions_(this.lastAutocompleteQuery_);
-          return;
-        }
-
-        // Keeps the items in the suggestion list.
-        this.autocompleteList_.suggestions = [headerItem].concat(suggestions);
-      }.bind(this));
-  };
-
-  /**
-   * Opens the currently selected suggestion item.
-   * @private
-   */
-  FileManager.prototype.openAutocompleteSuggestion_ = function() {
-    var selectedItem = this.autocompleteList_.selectedItem;
-
-    // If the entry is the search item or no entry is selected, just change to
-    // the search result.
-    if (!selectedItem || selectedItem.isHeaderItem) {
-      var query = selectedItem ?
-          selectedItem.searchQuery : this.searchBox_.value;
-      this.search_(query);
-      return;
-    }
-
-    var entry = selectedItem.entry;
-    // If the entry is a directory, just change the directory.
-    if (entry.isDirectory) {
-      this.onDirectoryAction_(entry);
-      return;
-    }
-
-    var entries = [entry];
-    var self = this;
-
-    // To open a file, first get the mime type.
-    this.metadataCache_.get(entries, 'drive', function(props) {
-      var mimeType = props[0].contentMimeType || '';
-      var mimeTypes = [mimeType];
-      var openIt = function() {
-        if (self.dialogType == DialogType.FULL_PAGE) {
-          var tasks = new FileTasks(self);
-          tasks.init(entries, mimeTypes);
-          tasks.executeDefault();
-        } else {
-          self.onOk_();
-        }
-      };
-
-      // Change the current directory to the directory that contains the
-      // selected file. Note that this is necessary for an image or a video,
-      // which should be opened in the gallery mode, as the gallery mode
-      // requires the entry to be in the current directory model. For
-      // consistency, the current directory is always changed regardless of
-      // the file type.
-      entry.getParent(function(parentEntry) {
-        var onDirectoryChanged = function(event) {
-          self.directoryModel_.removeEventListener('scan-completed',
-                                                   onDirectoryChanged);
-          self.directoryModel_.selectEntry(entry);
-          openIt();
-        };
-        // changeDirectoryEntry() returns immediately. We should wait until the
-        // directory scan is complete.
-        self.directoryModel_.addEventListener('scan-completed',
-                                              onDirectoryChanged);
-        self.directoryModel_.changeDirectoryEntry(
-          parentEntry,
-          function() {
-            // Remove the listner if the change directory failed.
-            self.directoryModel_.removeEventListener('scan-completed',
-                                                     onDirectoryChanged);
-          });
-      });
-    });
+    chrome.fileManagerPrivate.setPreferences(changeInfo);
   };
 
   FileManager.prototype.decorateSplitter = function(splitterElement) {
@@ -3701,40 +2826,46 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
   };
 
   /**
-   * Updates default action menu item to match passed taskItem (icon,
-   * label and action).
+   * Updates action menu item to match passed task items.
    *
-   * @param {Object} defaultItem - taskItem to match.
-   * @param {boolean} isMultiple - if multiple tasks available.
-   */
-  FileManager.prototype.updateContextMenuActionItems = function(defaultItem,
-                                                                isMultiple) {
-    if (defaultItem) {
-      if (defaultItem.iconType) {
-        this.defaultActionMenuItem_.style.backgroundImage = '';
-        this.defaultActionMenuItem_.setAttribute('file-type-icon',
-                                                 defaultItem.iconType);
-      } else if (defaultItem.iconUrl) {
-        this.defaultActionMenuItem_.style.backgroundImage =
-            'url(' + defaultItem.iconUrl + ')';
+   * @param {Array.<Object>=} opt_items List of items.
+   */
+  FileManager.prototype.updateContextMenuActionItems = function(opt_items) {
+    var items = opt_items || [];
+
+    // When only one task is available, show it as default item.
+    if (items.length === 1) {
+      var actionItem = items[0];
+
+      if (actionItem.iconType) {
+        this.actionMenuItem_.style.backgroundImage = '';
+        this.actionMenuItem_.setAttribute('file-type-icon',
+                                          actionItem.iconType);
+      } else if (actionItem.iconUrl) {
+        this.actionMenuItem_.style.backgroundImage =
+            'url(' + actionItem.iconUrl + ')';
       } else {
-        this.defaultActionMenuItem_.style.backgroundImage = '';
+        this.actionMenuItem_.style.backgroundImage = '';
       }
 
-      this.defaultActionMenuItem_.label = defaultItem.title;
-      this.defaultActionMenuItem_.disabled = !!defaultItem.disabled;
-      this.defaultActionMenuItem_.taskId = defaultItem.taskId;
+      this.actionMenuItem_.label =
+          actionItem.taskId === FileTasks.ZIP_UNPACKER_TASK_ID ?
+          str('ACTION_OPEN') : actionItem.title;
+      this.actionMenuItem_.disabled = !!actionItem.disabled;
+      this.actionMenuItem_.taskId = actionItem.taskId;
     }
 
-    var defaultActionSeparator =
-        this.dialogDom_.querySelector('#default-action-separator');
+    this.actionMenuItem_.hidden = items.length !== 1;
 
+    // When multiple tasks are available, show them in open with.
     this.openWithCommand_.canExecuteChange();
-    this.openWithCommand_.setHidden(!(defaultItem && isMultiple));
-    this.openWithCommand_.disabled = defaultItem && !!defaultItem.disabled;
+    this.openWithCommand_.setHidden(items.length < 2);
+    this.openWithCommand_.disabled = items.length < 2;
 
-    this.defaultActionMenuItem_.hidden = !defaultItem;
-    defaultActionSeparator.hidden = !defaultItem;
+    // Hide default action separator when there does not exist available task.
+    var defaultActionSeparator =
+        this.dialogDom_.querySelector('#default-action-separator');
+    defaultActionSeparator.hidden = items.length === 0;
   };
 
   /**
@@ -3745,17 +2876,17 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
   };
 
   /**
-   * @return {ArrayDataModel} File list.
+   * @return {cr.ui.ArrayDataModel} File list.
    */
   FileManager.prototype.getFileList = function() {
     return this.directoryModel_.getFileList();
   };
 
   /**
-   * @return {cr.ui.List} Current list object.
+   * @return {!cr.ui.List} Current list object.
    */
   FileManager.prototype.getCurrentList = function() {
-    return this.currentList_;
+    return this.ui.listContainer.currentList;
   };
 
   /**
@@ -3768,14 +2899,53 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
    * @private
    */
   FileManager.prototype.getPreferences_ = function(callback, opt_update) {
-    if (!opt_update && this.preferences_ !== undefined) {
+    if (!opt_update && this.preferences_ !== null) {
       callback(this.preferences_);
       return;
     }
 
-    chrome.fileBrowserPrivate.getPreferences(function(prefs) {
+    chrome.fileManagerPrivate.getPreferences(function(prefs) {
       this.preferences_ = prefs;
       callback(prefs);
     }.bind(this));
   };
+
+  /**
+   * @param {FileEntry} entry
+   * @private
+   */
+  FileManager.prototype.doEntryAction_ = function(entry) {
+    if (this.dialogType == DialogType.FULL_PAGE) {
+      this.metadataCache_.get([entry], 'external', function(props) {
+        var tasks = new FileTasks(this);
+        tasks.init([entry], [props[0].contentMimeType || '']);
+        tasks.executeDefault_();
+      }.bind(this));
+    } else {
+      var selection = this.getSelection();
+      if (selection.entries.length === 1 &&
+          util.isSameEntry(selection.entries[0], entry)) {
+        this.ui_.dialogFooter.okButton.click();
+      }
+    }
+  };
+
+  /**
+   * Outputs the current state for debugging.
+   */
+  FileManager.prototype.debugMe = function() {
+    var out = 'Debug information.\n' +
+        '1. fileManager.initializeQueue_.pendingTasks_\n';
+    var keys = Object.keys(this.initializeQueue_.pendingTasks);
+    out += 'Length: ' + keys.length + '\n';
+    keys.forEach(function(key) {
+      out += this.initializeQueue_.pendingTasks[key].toString() + '\n';
+    }.bind(this));
+
+    out += '2. VolumeManagerWrapper\n' +
+        this.volumeManager_.toString() + '\n';
+
+    out += 'End of debug information.';
+    console.log(out);
+  };
 })();