Commit a81e67c4 authored by fukino's avatar fukino Committed by Commit bot

Ensure existence of queried elements.

Added util.querySelector(), which throws error if the query doesn't find an element.
Using it and assert(), ensure the existence of elements which are queried in initialization process of FileManager.
Some trivial fixes for type-check errors are also included.

BUG=406995
TEST=run browser_tests

Review URL: https://codereview.chromium.org/667933003

Cr-Commit-Position: refs/heads/master@{#300646}
parent 2c0c6c9e
...@@ -274,7 +274,8 @@ DirectoryModel.prototype.setSelectedEntries_ = function(value) { ...@@ -274,7 +274,8 @@ DirectoryModel.prototype.setSelectedEntries_ = function(value) {
*/ */
DirectoryModel.prototype.getLeadEntry_ = function() { DirectoryModel.prototype.getLeadEntry_ = function() {
var index = this.fileListSelection_.leadIndex; var index = this.fileListSelection_.leadIndex;
return index >= 0 && this.getFileList().item(index); return index >= 0 ?
/** @type {Entry} */ (this.getFileList().item(index)) : null;
}; };
/** /**
......
...@@ -231,30 +231,9 @@ function FileManager() { ...@@ -231,30 +231,9 @@ function FileManager() {
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// Menus. // Menus.
/**
* Context menu for files.
* @type {HTMLMenuElement}
* @private
*/
this.fileContextMenu_ = null;
/**
* Context menu for volumes or shortcuts displayed on left pane.
* @type {HTMLMenuElement}
* @private
*/
this.rootsContextMenu_ = null;
/**
* Context menu for directory tree items.
* @type {HTMLMenuElement}
* @private
*/
this.directoryTreeContextMenu_ = null;
/** /**
* Context menu for texts. * Context menu for texts.
* @type {HTMLMenuElement} * @type {cr.ui.Menu}
* @private * @private
*/ */
this.textContextMenu_ = null; this.textContextMenu_ = null;
...@@ -304,7 +283,7 @@ function FileManager() { ...@@ -304,7 +283,7 @@ function FileManager() {
/** /**
* The button to open gear menu. * The button to open gear menu.
* @type {HTMLButtonElement} * @type {cr.ui.MenuButton}
* @private * @private
*/ */
this.gearButton_ = null; this.gearButton_ = null;
...@@ -997,37 +976,53 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52; ...@@ -997,37 +976,53 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
* @private * @private
*/ */
FileManager.prototype.initContextMenus_ = function() { FileManager.prototype.initContextMenus_ = function() {
this.fileContextMenu_ = this.dialogDom_.querySelector('#file-context-menu'); assert(this.grid_);
cr.ui.Menu.decorate(this.fileContextMenu_); assert(this.table_);
assert(this.document_);
cr.ui.contextMenuHandler.setContextMenu(this.grid_, this.fileContextMenu_); assert(this.dialogDom_);
cr.ui.contextMenuHandler.setContextMenu(this.table_.querySelector('.list'),
this.fileContextMenu_); // 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.grid_, fileContextMenu);
cr.ui.contextMenuHandler.setContextMenu( cr.ui.contextMenuHandler.setContextMenu(
this.document_.querySelector('.drive-welcome.page'), queryRequiredElement(this.table_, '.list'), fileContextMenu);
this.fileContextMenu_); cr.ui.contextMenuHandler.setContextMenu(
queryRequiredElement(this.document_, '.drive-welcome.page'),
this.rootsContextMenu_ = fileContextMenu);
this.dialogDom_.querySelector('#roots-context-menu');
cr.ui.Menu.decorate(this.rootsContextMenu_); // Set up the context menu for the volume/shortcut items in directory tree.
this.directoryTree_.contextMenuForRootItems = this.rootsContextMenu_; var rootsContextMenu = queryRequiredElement(
this.dialogDom_, '#roots-context-menu');
this.directoryTreeContextMenu_ = cr.ui.Menu.decorate(rootsContextMenu);
this.dialogDom_.querySelector('#directory-tree-context-menu'); rootsContextMenu = /** @type {!cr.ui.Menu} */ (rootsContextMenu);
cr.ui.Menu.decorate(this.directoryTreeContextMenu_);
this.directoryTree_.contextMenuForSubitems = this.directoryTreeContextMenu_; this.directoryTree_.contextMenuForRootItems = rootsContextMenu;
this.textContextMenu_ = // Set up the context menu for the folder items in directory tree.
this.dialogDom_.querySelector('#text-context-menu'); var directoryTreeContextMenu = queryRequiredElement(
cr.ui.Menu.decorate(this.textContextMenu_); this.dialogDom_, '#directory-tree-context-menu');
cr.ui.Menu.decorate(directoryTreeContextMenu);
this.gearButton_ = this.dialogDom_.querySelector('#gear-button'); directoryTreeContextMenu =
this.gearButton_.addEventListener('menushow', /** @type {!cr.ui.Menu} */ (directoryTreeContextMenu);
this.onShowGearMenu_.bind(this));
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 = this.dialogDom_.querySelector('#gear-menu').menuItemSelector =
'menuitem, hr'; 'menuitem, hr';
cr.ui.decorate(this.gearButton_, cr.ui.MenuButton); cr.ui.decorate(gearButton, cr.ui.MenuButton);
this.gearButton_ = /** @type {!cr.ui.MenuButton} */ (gearButton);
this.syncButton.checkable = true; this.syncButton.checkable = true;
this.hostedButton.checkable = true; this.hostedButton.checkable = true;
...@@ -1068,6 +1063,9 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52; ...@@ -1068,6 +1063,9 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
* @private * @private
*/ */
FileManager.prototype.initCommands_ = function() { FileManager.prototype.initCommands_ = function() {
assert(this.textContextMenu_);
assert(this.renameInput_);
this.commandHandler = new CommandHandler(this); this.commandHandler = new CommandHandler(this);
// TODO(hirono): Move the following block to the UI part. // TODO(hirono): Move the following block to the UI part.
...@@ -1303,22 +1301,28 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52; ...@@ -1303,22 +1301,28 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
// Cache nodes we'll be manipulating. // Cache nodes we'll be manipulating.
var dom = this.dialogDom_; var dom = this.dialogDom_;
this.filenameInput_ = dom.querySelector('#filename-input-box input'); var filenameInput = queryRequiredElement(dom, '#filename-input-box input');
this.taskItems_ = dom.querySelector('#tasks'); this.filenameInput_ = /** @type {HTMLInputElement} */ (filenameInput);
var taskItems = queryRequiredElement(dom, '#tasks');
this.taskItems_ = /** @type {HTMLButtonElement} */ (taskItems);
this.table_ = dom.querySelector('.detail-table'); var spinner = queryRequiredElement(dom, '#list-container > .spinner-layer');
this.grid_ = dom.querySelector('.thumbnail-grid'); this.spinner_ = /** @type {HTMLDivElement} */ (spinner);
this.spinner_ = dom.querySelector('#list-container > .spinner-layer');
this.showSpinner_(true); this.showSpinner_(true);
var fullPage = this.dialogType == DialogType.FULL_PAGE; var fullPage = this.dialogType == DialogType.FULL_PAGE;
var table = queryRequiredElement(dom, '.detail-table');
var grid = queryRequiredElement(dom, '.thumbnail-grid');
FileTable.decorate( FileTable.decorate(
this.table_, this.metadataCache_, this.volumeManager_, fullPage); table, this.metadataCache_, this.volumeManager_, fullPage);
FileGrid.decorate(this.grid_, this.metadataCache_, this.volumeManager_); FileGrid.decorate(grid, this.metadataCache_, this.volumeManager_);
this.table_ = /** @type {!FileTable} */ (table);
this.grid_ = /** @type {!FileGrid} */ (grid);
this.ui_.locationLine = new LocationLine( this.ui_.locationLine = new LocationLine(
dom.querySelector('#location-breadcrumbs'), queryRequiredElement(dom, '#location-breadcrumbs'),
dom.querySelector('#location-volume-icon'), queryRequiredElement(dom, '#location-volume-icon'),
this.metadataCache_, this.metadataCache_,
this.volumeManager_); this.volumeManager_);
this.ui_.locationLine.addEventListener( this.ui_.locationLine.addEventListener(
...@@ -1338,7 +1342,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52; ...@@ -1338,7 +1342,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
// Initialize progress center panel. // Initialize progress center panel.
this.progressCenterPanel_ = new ProgressCenterPanel( this.progressCenterPanel_ = new ProgressCenterPanel(
dom.querySelector('#progress-center')); queryRequiredElement(dom, '#progress-center'));
this.backgroundPage_.background.progressCenter.addPanel( this.backgroundPage_.background.progressCenter.addPanel(
this.progressCenterPanel_); this.progressCenterPanel_);
......
...@@ -60,7 +60,8 @@ CommandButton.prototype.setCommand = function(command) { ...@@ -60,7 +60,8 @@ CommandButton.prototype.setCommand = function(command) {
/** @type {EventListener} */ (this)); /** @type {EventListener} */ (this));
} }
if (typeof command == 'string' && command[0] == '#') { if (typeof command == 'string') {
assert(command[0] == '#');
command = /** @type {!cr.ui.Command} */ command = /** @type {!cr.ui.Command} */
(this.ownerDocument.getElementById(command.slice(1))); (this.ownerDocument.getElementById(command.slice(1)));
cr.ui.decorate(command, cr.ui.Command); cr.ui.decorate(command, cr.ui.Command);
......
...@@ -30,7 +30,7 @@ DirectoryItemTreeBaseMethods.updateSubElementsFromList = function(recursive) { ...@@ -30,7 +30,7 @@ DirectoryItemTreeBaseMethods.updateSubElementsFromList = function(recursive) {
while (this.entries_[index]) { while (this.entries_[index]) {
var currentEntry = this.entries_[index]; var currentEntry = this.entries_[index];
var currentElement = this.items[index]; var currentElement = this.items[index];
var label = util.getEntryLabel(tree.volumeManager_, currentEntry); var label = util.getEntryLabel(tree.volumeManager_, currentEntry) || '';
if (index >= this.items.length) { if (index >= this.items.length) {
var item = new DirectoryItem(label, currentEntry, this, tree); var item = new DirectoryItem(label, currentEntry, this, tree);
...@@ -532,7 +532,7 @@ VolumeItem.prototype.updateSubDirectories = function(recursive) { ...@@ -532,7 +532,7 @@ VolumeItem.prototype.updateSubDirectories = function(recursive) {
for (var i = 0; i < entries.length; i++) { for (var i = 0; i < entries.length; i++) {
var item = new DirectoryItem( var item = new DirectoryItem(
util.getEntryLabel(this.parentTree_.volumeManager_, entries[i]), util.getEntryLabel(this.parentTree_.volumeManager_, entries[i]) || '',
entries[i], this, this.parentTree_); entries[i], this, this.parentTree_);
this.add(item); this.add(item);
item.updateSubDirectories(false); item.updateSubDirectories(false);
......
...@@ -32,7 +32,7 @@ FileGrid.prototype.__proto__ = cr.ui.Grid.prototype; ...@@ -32,7 +32,7 @@ FileGrid.prototype.__proto__ = cr.ui.Grid.prototype;
/** /**
* Decorates an HTML element to be a FileGrid. * Decorates an HTML element to be a FileGrid.
* @param {HTMLElement} self The grid to decorate. * @param {!Element} self The grid to decorate.
* @param {MetadataCache} metadataCache Metadata cache to find entries * @param {MetadataCache} metadataCache Metadata cache to find entries
* metadata. * metadata.
* @param {VolumeManagerWrapper} volumeManager Volume manager instance. * @param {VolumeManagerWrapper} volumeManager Volume manager instance.
...@@ -227,6 +227,7 @@ Object.defineProperty(FileGrid.Item.prototype, 'label', { ...@@ -227,6 +227,7 @@ Object.defineProperty(FileGrid.Item.prototype, 'label', {
*/ */
FileGrid.Item.decorate = function(li, entry, grid) { FileGrid.Item.decorate = function(li, entry, grid) {
li.__proto__ = FileGrid.Item.prototype; li.__proto__ = FileGrid.Item.prototype;
li = /** @type {!FileGrid.Item} */ (li);
// TODO(mtomasz): Pass the metadata cache and the volume manager directly // TODO(mtomasz): Pass the metadata cache and the volume manager directly
// instead of accessing private members of grid. // instead of accessing private members of grid.
FileGrid.decorateThumbnail( FileGrid.decorateThumbnail(
......
...@@ -225,7 +225,7 @@ FileTable.prototype.__proto__ = cr.ui.Table.prototype; ...@@ -225,7 +225,7 @@ FileTable.prototype.__proto__ = cr.ui.Table.prototype;
/** /**
* Decorates the element. * Decorates the element.
* @param {HTMLElement} self Table to decorate. * @param {!Element} self Table to decorate.
* @param {MetadataCache} metadataCache To retrieve metadata. * @param {MetadataCache} metadataCache To retrieve metadata.
* @param {VolumeManagerWrapper} volumeManager To retrieve volume info. * @param {VolumeManagerWrapper} volumeManager To retrieve volume info.
* @param {boolean} fullPage True if it's full page File Manager, * @param {boolean} fullPage True if it's full page File Manager,
...@@ -590,7 +590,8 @@ FileTable.prototype.renderType_ = function(entry, columnId, table) { ...@@ -590,7 +590,8 @@ FileTable.prototype.renderType_ = function(entry, columnId, table) {
* @private * @private
*/ */
FileTable.prototype.renderDate_ = function(entry, columnId, table) { FileTable.prototype.renderDate_ = function(entry, columnId, table) {
var div = this.ownerDocument.createElement('div'); var div = /** @type {!HTMLDivElement} */
(this.ownerDocument.createElement('div'));
div.className = 'date'; div.className = 'date';
this.updateDate_(div, entry); this.updateDate_(div, entry);
......
...@@ -8,8 +8,8 @@ ...@@ -8,8 +8,8 @@
* TODO(hirono): Remove metadataCache and volumeManager dependencies from the UI * TODO(hirono): Remove metadataCache and volumeManager dependencies from the UI
* class. * class.
* @extends {cr.EventTarget} * @extends {cr.EventTarget}
* @param {HTMLElement} breadcrumbs Container element for breadcrumbs. * @param {!Element} breadcrumbs Container element for breadcrumbs.
* @param {HTMLElement} volumeIcon Volume icon. * @param {!Element} volumeIcon Volume icon.
* @param {MetadataCache} metadataCache To retrieve metadata. * @param {MetadataCache} metadataCache To retrieve metadata.
* @param {VolumeManagerWrapper} volumeManager Volume manager. * @param {VolumeManagerWrapper} volumeManager Volume manager.
* @constructor * @constructor
......
...@@ -49,14 +49,14 @@ function ProgressCenterItemElement(document) { ...@@ -49,14 +49,14 @@ function ProgressCenterItemElement(document) {
* @private * @private
*/ */
ProgressCenterItemElement.safelySetAnimation_ = function(callback) { ProgressCenterItemElement.safelySetAnimation_ = function(callback) {
var requestId = requestAnimationFrame(function() { var requestId = window.requestAnimationFrame(function() {
// The transition start properties currently set are rendered at this frame. // The transition start properties currently set are rendered at this frame.
// And the transition end properties set by the callback is rendered at the // And the transition end properties set by the callback is rendered at the
// next frame. // next frame.
requestId = requestAnimationFrame(callback); requestId = window.requestAnimationFrame(callback);
}); });
return function() { return function() {
cancelAnimationFrame(requestId); window.cancelAnimationFrame(requestId);
}; };
}; };
...@@ -168,13 +168,13 @@ ProgressCenterItemElement.prototype.onTransitionEnd_ = function(event) { ...@@ -168,13 +168,13 @@ ProgressCenterItemElement.prototype.onTransitionEnd_ = function(event) {
/** /**
* Progress center panel. * Progress center panel.
* *
* @param {HTMLElement} element DOM Element of the process center panel. * @param {!Element} element DOM Element of the process center panel.
* @constructor * @constructor
*/ */
function ProgressCenterPanel(element) { function ProgressCenterPanel(element) {
/** /**
* Root element of the progress center. * Root element of the progress center.
* @type {Element} * @type {!Element}
* @private * @private
*/ */
this.element_ = element; this.element_ = element;
......
...@@ -261,6 +261,21 @@ function getRequiredElement(id) { ...@@ -261,6 +261,21 @@ function getRequiredElement(id) {
'Missing required element: ' + id); 'Missing required element: ' + id);
} }
/**
* Query an element that's known to exist by a selector. We use this instead of
* just calling querySelector and not checking the result because this lets us
* satisfy the JSCompiler type system.
* @param {(!Document|!DocumentFragment|!Element)} context The context object
* for querySelector.
* @param {string} selectors CSS selectors to query the element.
* @return {!HTMLElement} the Element.
*/
function queryRequiredElement(context, selectors) {
var element = context.querySelector(selectors);
return assertInstanceof(element, HTMLElement,
'Missing required element: ' + selectors);
}
// Handle click on a link. If the link points to a chrome: or file: url, then // Handle click on a link. If the link points to a chrome: or file: url, then
// call into the browser to do the navigation. // call into the browser to do the navigation.
document.addEventListener('click', function(e) { document.addEventListener('click', function(e) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment