diff --git a/chrome/browser/resources/bookmark_manager/js/bmm.js b/chrome/browser/resources/bookmark_manager/js/bmm.js index 0d7c491f5df30..f010d82d214bd 100644 --- a/chrome/browser/resources/bookmark_manager/js/bmm.js +++ b/chrome/browser/resources/bookmark_manager/js/bmm.js @@ -64,7 +64,7 @@ cr.define('bmm', function() { * Loads the entire bookmark tree and returns a {@code Promise} that will * be fulfilled when done. This reuses multiple loads so that we do not load * the same tree more than once at the same time. - * @return {!Promise.} The future promise for the load. + * @return {!Promise.} The future promise for the load. */ function loadTree() { return loadSubtree(''); @@ -169,7 +169,7 @@ cr.define('bmm', function() { /** * Callback for when a bookmark node is removed. * @param {string} id The id of the removed bookmark node. - * @param {!Object} bookmarkNode The information about removed. + * @param {!Object} removeInfo The information about removed. */ function handleRemoved(id, removeInfo) { if (bmm.list) diff --git a/chrome/browser/resources/bookmark_manager/js/bmm/bookmark_list.js b/chrome/browser/resources/bookmark_manager/js/bmm/bookmark_list.js index a678d379e4116..f825da2c5023a 100644 --- a/chrome/browser/resources/bookmark_manager/js/bmm/bookmark_list.js +++ b/chrome/browser/resources/bookmark_manager/js/bmm/bookmark_list.js @@ -5,14 +5,29 @@ // TODO(arv): Now that this is driven by a data model, implement a data model // that handles the loading and the events from the bookmark backend. +/** + * @typedef {{childIds: Array.}} + * + * @see chrome/common/extensions/api/bookmarks.json + */ +var ReorderInfo; + +/** + * @typedef {{parentId: string, + * index: number, + * oldParentId: string, + * oldIndex: number}} + * + * @see chrome/common/extensions/api/bookmarks.json + */ +var MoveInfo; + cr.define('bmm', function() { var List = cr.ui.List; var ListItem = cr.ui.ListItem; var ArrayDataModel = cr.ui.ArrayDataModel; var ContextMenuButton = cr.ui.ContextMenuButton; - var list; - /** * Basic array data model for use with bookmarks. * @param {!Array.} items The bookmark items. @@ -57,7 +72,7 @@ cr.define('bmm', function() { * Creates a new bookmark list. * @param {Object=} opt_propertyBag Optional properties. * @constructor - * @extends {HTMLButtonElement} + * @extends {cr.ui.List} */ var BookmarkList = cr.ui.define('list'); @@ -79,6 +94,10 @@ cr.define('bmm', function() { bmm.list = this; }, + /** + * @param {!BookmarkTreeNode} bookmarkNode + * @override + */ createItem: function(bookmarkNode) { return new BookmarkListItem(bookmarkNode); }, @@ -198,9 +217,10 @@ cr.define('bmm', function() { * Handles mousedown events so that we can prevent the auto scroll as * necessary. * @private - * @param {!MouseEvent} e The mousedown event object. + * @param {!Event} e The mousedown event object. */ handleMouseDown_: function(e) { + e = /** @type {!MouseEvent} */(e); if (e.button == 1) { // WebKit no longer fires click events for middle clicks so we manually // listen to mouse up to dispatch a click event. @@ -220,9 +240,10 @@ cr.define('bmm', function() { * WebKit no longer dispatches click events for middle clicks so we need * to emulate it. * @private - * @param {!MouseEvent} e The mouse up event object. + * @param {!Event} e The mouse up event object. */ handleMiddleMouseUp_: function(e) { + e = /** @type {!MouseEvent} */(e); this.removeEventListener('mouseup', this.handleMiddleMouseUp_); if (e.button == 1) { var el = e.target; @@ -250,6 +271,10 @@ cr.define('bmm', function() { } }, + /** + * @param {string} id + * @param {ReorderInfo} reorderInfo + */ handleChildrenReordered: function(id, reorderInfo) { if (this.parentId == id) { // We create a new data model with updated items in the right order. @@ -274,6 +299,10 @@ cr.define('bmm', function() { this.dataModel.splice(bookmarkNode.index, 0, bookmarkNode); }, + /** + * @param {string} id + * @param {MoveInfo} moveInfo + */ handleMoved: function(id, moveInfo) { if (moveInfo.parentId == this.parentId || moveInfo.oldParentId == this.parentId) { @@ -344,7 +373,6 @@ cr.define('bmm', function() { /** * The ID of the bookmark folder we are displaying. - * @type {string} */ cr.defineProperty(BookmarkList, 'parentId', cr.PropertyKind.JS, function() { @@ -353,9 +381,10 @@ cr.define('bmm', function() { /** * The contextMenu property. - * @type {cr.ui.Menu} */ cr.ui.contextMenuHandler.addContextMenuProperty(BookmarkList); + /** @type {cr.ui.Menu} */ + BookmarkList.prototype.contextMenu; /** * Creates a new bookmark list item. @@ -507,14 +536,14 @@ cr.define('bmm', function() { this.setAttribute('editing', ''); this.draggable = false; - labelInput = doc.createElement('input'); + labelInput = /** @type {HTMLElement} */(doc.createElement('input')); labelInput.placeholder = loadTimeData.getString('name_input_placeholder'); replaceAllChildren(labelEl, labelInput); labelInput.value = title; if (!isFolder) { - urlInput = doc.createElement('input'); + urlInput = /** @type {HTMLElement} */(doc.createElement('input')); urlInput.type = 'url'; urlInput.required = true; urlInput.placeholder = @@ -522,13 +551,13 @@ cr.define('bmm', function() { // We also need a name for the input for the CSS to work. urlInput.name = '-url-input-' + cr.createUid(); - replaceAllChildren(urlEl, urlInput); + replaceAllChildren(assert(urlEl), urlInput); urlInput.value = url; } - function stopPropagation(e) { + var stopPropagation = function(e) { e.stopPropagation(); - } + }; var eventsToStop = ['mousedown', 'mouseup', 'contextmenu', 'dblclick', 'paste']; @@ -601,6 +630,6 @@ cr.define('bmm', function() { return { BookmarkList: BookmarkList, - list: list + list: /** @type {Element} */(null), // Set when decorated. }; }); diff --git a/chrome/browser/resources/bookmark_manager/js/bmm/bookmark_tree.js b/chrome/browser/resources/bookmark_manager/js/bmm/bookmark_tree.js index 0a94621a4649c..e622db7f57f54 100644 --- a/chrome/browser/resources/bookmark_manager/js/bmm/bookmark_tree.js +++ b/chrome/browser/resources/bookmark_manager/js/bmm/bookmark_tree.js @@ -13,22 +13,23 @@ cr.define('bmm', function() { /** @const */ var Tree = cr.ui.Tree; /** @const */ var TreeItem = cr.ui.TreeItem; + /** @const */ var localStorage = window.localStorage; var treeLookup = {}; - var tree; // Manager for persisting the expanded state. - var expandedManager = { + var expandedManager = /** @type {EventListener} */({ /** * A map of the collapsed IDs. * @type {Object} */ map: 'bookmarkTreeState' in localStorage ? - JSON.parse(localStorage['bookmarkTreeState']) : {}, + /** @type {Object} */(JSON.parse(localStorage['bookmarkTreeState'])) : + {}, /** * Set the collapsed state for an ID. - * @param {string} The bookmark ID of the tree item that was expanded or + * @param {string} id The bookmark ID of the tree item that was expanded or * collapsed. * @param {boolean} expanded Whether the tree item was expanded. */ @@ -83,7 +84,7 @@ cr.define('bmm', function() { localStorage['bookmarkTreeState'] = JSON.stringify(map); }, 100); } - }; + }); // Clean up once per session but wait until things settle down a bit. setTimeout(expandedManager.cleanUp.bind(expandedManager), 1e4); @@ -131,12 +132,16 @@ cr.define('bmm', function() { * * @param {!cr.ui.TreeItem} parent The parent tree item. * @param {!cr.ui.TreeItem} treeItem The tree item to add. - * @param {Function=} f A function which gets called after the item has been - * added at the right index. + * @param {Function=} opt_f A function which gets called after the item has + * been added at the right index. */ function addTreeItem(parent, treeItem, opt_f) { chrome.bookmarks.getChildren(parent.bookmarkNode.id, function(children) { - var index = children.filter(bmm.isFolder).map(function(item) { + var isFolder = /** + * @type {function (BookmarkTreeNode, number, + * Array.<(BookmarkTreeNode)>)} + */(bmm.isFolder); + var index = children.filter(isFolder).map(function(item) { return item.id; }).indexOf(treeItem.bookmarkNode.id); parent.addAt(treeItem, index); @@ -151,7 +156,7 @@ cr.define('bmm', function() { * Creates a new bookmark list. * @param {Object=} opt_propertyBag Optional properties. * @constructor - * @extends {HTMLButtonElement} + * @extends {cr.ui.Tree} */ var BookmarkTree = cr.ui.define('tree'); @@ -172,6 +177,10 @@ cr.define('bmm', function() { treeItem.label = treeItem.bookmarkNode.title = changeInfo.title; }, + /** + * @param {string} id + * @param {ReorderInfo} reorderInfo + */ handleChildrenReordered: function(id, reorderInfo) { var parentItem = treeLookup[id]; // The tree only contains folders. @@ -190,6 +199,10 @@ cr.define('bmm', function() { } }, + /** + * @param {string} id + * @param {MoveInfo} moveInfo + */ handleMoved: function(id, moveInfo) { var treeItem = treeLookup[id]; if (treeItem) { @@ -262,7 +275,8 @@ cr.define('bmm', function() { hasDirectories = true; var item = new BookmarkTreeItem(bookmarkNode); parentTreeItem.add(item); - var anyChildren = buildTreeItems(item, bookmarkNode.children); + var children = assert(bookmarkNode.children); + var anyChildren = buildTreeItems(item, children); item.expanded = anyChildren && expandedManager.get(bookmarkNode.id); } } @@ -301,7 +315,7 @@ cr.define('bmm', function() { BookmarkTree: BookmarkTree, BookmarkTreeItem: BookmarkTreeItem, treeLookup: treeLookup, - tree: tree, + tree: /** @type {Element} */(null), // Set when decorated. ROOT_ID: ROOT_ID }; }); diff --git a/chrome/browser/resources/bookmark_manager/js/compiled_resources.gyp b/chrome/browser/resources/bookmark_manager/js/compiled_resources.gyp new file mode 100644 index 0000000000000..346e5a0bf1d7b --- /dev/null +++ b/chrome/browser/resources/bookmark_manager/js/compiled_resources.gyp @@ -0,0 +1,52 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +{ + 'targets': [ + { + 'target_name': 'main', + 'variables': { + 'depends': [ + '../../../../../third_party/jstemplate/compiled_resources.gyp:jstemplate', + '../../../../../ui/webui/resources/js/cr.js', + '../../../../../ui/webui/resources/js/cr/event_target.js', + '../../../../../ui/webui/resources/js/cr/link_controller.js', + '../../../../../ui/webui/resources/js/cr/ui.js', + '../../../../../ui/webui/resources/js/cr/ui/array_data_model.js', + '../../../../../ui/webui/resources/js/cr/ui/command.js', + '../../../../../ui/webui/resources/js/cr/ui/context_menu_button.js', + '../../../../../ui/webui/resources/js/cr/ui/context_menu_handler.js', + '../../../../../ui/webui/resources/js/cr/ui/focus_outline_manager.js', + '../../../../../ui/webui/resources/js/cr/ui/list.js', + '../../../../../ui/webui/resources/js/cr/ui/list_item.js', + '../../../../../ui/webui/resources/js/cr/ui/list_selection_controller.js', + '../../../../../ui/webui/resources/js/cr/ui/list_selection_model.js', + '../../../../../ui/webui/resources/js/cr/ui/menu.js', + '../../../../../ui/webui/resources/js/cr/ui/menu_button.js', + '../../../../../ui/webui/resources/js/cr/ui/menu_item.js', + '../../../../../ui/webui/resources/js/cr/ui/position_util.js', + '../../../../../ui/webui/resources/js/cr/ui/splitter.js', + '../../../../../ui/webui/resources/js/cr/ui/touch_handler.js', + '../../../../../ui/webui/resources/js/cr/ui/tree.js', + '../../../../../ui/webui/resources/js/event_tracker.js', + '../../../../../ui/webui/resources/js/i18n_template_no_process.js', + '../../../../../ui/webui/resources/js/load_time_data.js', + '../../../../../ui/webui/resources/js/util.js', + '../../../../../chrome/browser/resources/bookmark_manager/js/bmm.js', + '../../../../../chrome/browser/resources/bookmark_manager/js/bmm/bookmark_list.js', + '../../../../../chrome/browser/resources/bookmark_manager/js/bmm/bookmark_tree.js', + '../../../../../chrome/browser/resources/bookmark_manager/js/dnd.js', + ], + 'externs': [ + '<(CLOSURE_DIR)/externs/bookmark_manager_private.js', + '<(CLOSURE_DIR)/externs/chrome_send_externs.js', + '<(CLOSURE_DIR)/externs/chrome_extensions.js', + '<(CLOSURE_DIR)/externs/metrics_private.js', + '<(CLOSURE_DIR)/externs/system_private.js', + '../../../../../ui/webui/resources/js/template_data_externs.js', + ], + }, + 'includes': ['../../../../../third_party/closure_compiler/compile_js.gypi'], + } + ], +} diff --git a/chrome/browser/resources/bookmark_manager/js/dnd.js b/chrome/browser/resources/bookmark_manager/js/dnd.js index 7b2ee40a05d62..0f0bffde29fa8 100644 --- a/chrome/browser/resources/bookmark_manager/js/dnd.js +++ b/chrome/browser/resources/bookmark_manager/js/dnd.js @@ -47,7 +47,7 @@ cr.define('dnd', function() { /** * The style that was applied to indicate the drop location. - * @type {string} + * @type {?string} */ var lastIndicatorClassName; @@ -194,7 +194,7 @@ cr.define('dnd', function() { /** * External function to select folders or bookmarks after a drop action. - * @type {function} + * @type {?Function} */ var selectItemsAfterUserAction = null; @@ -208,7 +208,7 @@ cr.define('dnd', function() { // If we are over the list and the list is showing search result, we cannot // drop. function isOverSearch(overElement) { - return list.isSearch() && list.contains(overElement); + return bmm.list.isSearch() && bmm.list.contains(overElement); } /** @@ -219,7 +219,7 @@ cr.define('dnd', function() { */ function calculateValidDropTargets(overElement) { // Don't allow dropping if there is an ephemeral item being edited. - if (list.hasEphemeral()) + if (bmm.list.hasEphemeral()) return DropPosition.NONE; if (!dragInfo.isDragValid() || isOverSearch(overElement)) @@ -302,7 +302,7 @@ cr.define('dnd', function() { // We are trying to drop an item past the last item. This is // only allowed if dragged item is different from the last item // in the list. - var listItems = list.items; + var listItems = bmm.list.items; var len = listItems.length; if (!len || !dragInfo.isDraggingBookmark(listItems[len - 1].bookmarkId)) return true; @@ -333,7 +333,7 @@ cr.define('dnd', function() { // Do not allow dragging if there is an ephemeral item being edited at the // moment. - if (list.hasEphemeral()) + if (bmm.list.hasEphemeral()) return; if (draggedNodes.length) { @@ -371,7 +371,7 @@ cr.define('dnd', function() { return; var overElement = getBookmarkElement(e.target) || - (e.target == list ? list : null); + (e.target == bmm.list ? bmm.list : null); if (!overElement) return; @@ -450,16 +450,16 @@ cr.define('dnd', function() { if (overElement instanceof ListItem) { dropInfoResult.relatedIndex = overElement.parentNode.dataModel.indexOf(relatedNode); - dropInfoResult.selectTarget = list; + dropInfoResult.selectTarget = bmm.list; } else if (overElement instanceof BookmarkList) { dropInfoResult.relatedIndex = overElement.dataModel.length - 1; - dropInfoResult.selectTarget = list; + dropInfoResult.selectTarget = bmm.list; } else { // Tree dropInfoResult.relatedIndex = relatedNode.index; - dropInfoResult.selectTarget = tree; + dropInfoResult.selectTarget = bmm.tree; dropInfoResult.selectedTreeId = - tree.selectedItem ? tree.selectedItem.bookmarkId : null; + bmm.tree.selectedItem ? bmm.tree.selectedItem.bookmarkId : null; } if (dropPos == DropPosition.ABOVE) @@ -508,7 +508,7 @@ cr.define('dnd', function() { function init(selectItemsAfterUserActionFunction) { function deferredClearData() { - setTimeout(clearDragData); + setTimeout(clearDragData, 0); } selectItemsAfterUserAction = selectItemsAfterUserActionFunction; diff --git a/chrome/browser/resources/bookmark_manager/js/main.js b/chrome/browser/resources/bookmark_manager/js/main.js index 079bc8ebe4a2f..a02fded62f06f 100644 --- a/chrome/browser/resources/bookmark_manager/js/main.js +++ b/chrome/browser/resources/bookmark_manager/js/main.js @@ -6,7 +6,6 @@ /** @const */ var BookmarkList = bmm.BookmarkList; /** @const */ var BookmarkTree = bmm.BookmarkTree; /** @const */ var Command = cr.ui.Command; -/** @const */ var CommandBinding = cr.ui.CommandBinding; /** @const */ var LinkKind = cr.LinkKind; /** @const */ var ListItem = cr.ui.ListItem; /** @const */ var Menu = cr.ui.Menu; @@ -17,7 +16,7 @@ /** * An array containing the BookmarkTreeNodes that were deleted in the last * deletion action. This is used for implementing undo. - * @type {Array.} + * @type {Array.>} */ var lastDeletedNodes; @@ -38,7 +37,7 @@ var performGlobalUndo; /** * Holds a link controller singleton. Use getLinkController() rarther than * accessing this variabie. - * @type {LinkController} + * @type {cr.LinkController} */ var linkController; @@ -142,7 +141,7 @@ function loadLocalizedStrings(data) { * Updates the location hash to reflect the current state of the application. */ function updateHash() { - window.location.hash = tree.selectedItem.bookmarkId; + window.location.hash = bmm.tree.selectedItem.bookmarkId; updateAllCommands(); } @@ -162,6 +161,7 @@ function navigateTo(id, opt_callback) { 'BookmarkManager_NavigateTo_' + metricsId); if (opt_callback) { + var list = getRequiredElement('list'); if (list.parentId == id) opt_callback(); else @@ -175,11 +175,11 @@ function navigateTo(id, opt_callback) { */ function updateParentId(id) { // Setting list.parentId fires 'load' event. - list.parentId = id; + bmm.list.parentId = id; // When tree.selectedItem changed, tree view calls navigatTo() then it // calls updateHash() when list view displayed specified folder. - tree.selectedItem = bmm.treeLookup[id] || tree.selectedItem; + bmm.tree.selectedItem = bmm.treeLookup[id] || bmm.tree.selectedItem; } // Process the location hash. This is called by onhashchange and when the page @@ -188,7 +188,7 @@ function processHash() { var id = window.location.hash.slice(1); if (!id) { // If we do not have a hash, select first item in the tree. - id = tree.items[0].bookmarkId; + id = bmm.tree.items[0].bookmarkId; } var valid = false; @@ -203,16 +203,17 @@ function processHash() { var bookmarkNode = bookmarkNodes[0]; // After the list reloads, edit the desired bookmark. - var editBookmark = function(e) { - var index = list.dataModel.findIndexById(bookmarkNode.id); + var editBookmark = function() { + var index = bmm.list.dataModel.findIndexById(bookmarkNode.id); if (index != -1) { - var sm = list.selectionModel; + var sm = bmm.list.selectionModel; sm.anchorIndex = sm.leadIndex = sm.selectedIndex = index; scrollIntoViewAndMakeEditable(index); } }; - navigateTo(bookmarkNode.parentId, editBookmark); + var parentId = assert(bookmarkNode.parentId); + navigateTo(parentId, editBookmark); }); // We handle the two cases of navigating to the bookmark to be edited @@ -273,12 +274,12 @@ function setSearch(searchText) { input.value = searchText; if (searchText) { - tree.add(searchTreeItem); - tree.selectedItem = searchTreeItem; + bmm.tree.add(searchTreeItem); + bmm.tree.selectedItem = searchTreeItem; } else { // Go "home". - tree.selectedItem = tree.items[0]; - id = tree.selectedItem.bookmarkId; + bmm.tree.selectedItem = bmm.tree.items[0]; + id = bmm.tree.selectedItem.bookmarkId; } navigateTo(id); @@ -291,7 +292,7 @@ function setSearch(searchText) { * @return {string} The path to the the bookmark, */ function getFolder(parentId) { - var parentNode = tree.getBookmarkNodeById(parentId); + var parentNode = bmm.tree.getBookmarkNodeById(parentId); if (parentNode) { var s = parentNode.title; if (parentNode.parentId != bmm.ROOT_ID) { @@ -331,7 +332,7 @@ function getAllUrls(nodes) { // Get a future promise for the nodes. var promises = nodes.map(function(node) { - if (bmm.isFolder(node)) + if (bmm.isFolder(assert(node))) return bmm.loadSubtree(node.id); // Not a folder so we already have all the data we need. return Promise.resolve(node); @@ -345,26 +346,26 @@ function getAllUrls(nodes) { /** * Returns the nodes (non recursive) to use for the open commands. - * @param {HTMLElement} target . - * @return {Array.} . + * @param {HTMLElement} target + * @return {!Array.} */ function getNodesForOpen(target) { - if (target == tree) { - if (tree.selectedItem != searchTreeItem) - return tree.selectedFolders; + if (target == bmm.tree) { + if (bmm.tree.selectedItem != searchTreeItem) + return bmm.tree.selectedFolders; // Fall through to use all nodes in the list. } else { - var items = list.selectedItems; + var items = bmm.list.selectedItems; if (items.length) return items; } // The list starts off with a null dataModel. We can get here during startup. - if (!list.dataModel) + if (!bmm.list.dataModel) return []; // Return an array based on the dataModel. - return list.dataModel.slice(); + return bmm.list.dataModel.slice(); } /** @@ -397,6 +398,8 @@ function updateOpenCommand(e, command, singularId, pluralId, commandDisabled) { // The command label reflects the selection which might not reflect // how many bookmarks will be opened. For example if you right click an // empty area in a folder with 1 bookmark the text should still say "all". + assert(!e.target || e.target instanceof BookmarkList || + e.target instanceof BookmarkTree); var selectedNodes = getSelectedBookmarkNodes(e.target).filter(notNewNode); var singular = selectedNodes.length == 1 && !bmm.isFolder(selectedNodes[0]); command.label = loadTimeData.getString(singular ? singularId : pluralId); @@ -408,7 +411,8 @@ function updateOpenCommand(e, command, singularId, pluralId, commandDisabled) { return; } - getUrlsForOpenCommands(e.target).then(function(urls) { + getUrlsForOpenCommands(assertInstanceof(e.target, HTMLElement)).then( + function(urls) { var disabled = !urls.length; command.disabled = disabled; e.canExecute = !disabled; @@ -430,10 +434,10 @@ function updatePasteCommand(opt_f) { opt_f(); } // We cannot paste into search view. - if (list.isSearch()) + if (bmm.list.isSearch()) update(false); else - chrome.bookmarkManagerPrivate.canPaste(list.parentId, update); + chrome.bookmarkManagerPrivate.canPaste(bmm.list.parentId, update); } function handleCanExecuteForDocument(e) { @@ -447,9 +451,9 @@ function handleCanExecuteForDocument(e) { e.canExecute = true; break; case 'sort-command': - e.canExecute = !list.isSearch() && - list.dataModel && list.dataModel.length > 1 && - !isUnmodifiable(tree.getBookmarkNodeById(list.parentId)); + e.canExecute = !bmm.list.isSearch() && + bmm.list.dataModel && bmm.list.dataModel.length > 1 && + !isUnmodifiable(bmm.tree.getBookmarkNodeById(bmm.list.parentId)); break; case 'undo-command': // If the search box is active, pass the undo command through @@ -466,7 +470,7 @@ function handleCanExecuteForDocument(e) { /** * Helper function for handling canExecute for the list and the tree. - * @param {!Event} e Can execute event object. + * @param {!cr.ui.CanExecuteEvent} e Can execute event object. * @param {boolean} isSearch Whether the user is trying to do a command on * search. */ @@ -482,7 +486,8 @@ function canExecuteShared(e, isSearch) { case 'add-new-bookmark-command': case 'new-folder-command': var parentId = computeParentFolderForNewItem(); - var unmodifiable = isUnmodifiable(tree.getBookmarkNodeById(parentId)); + var unmodifiable = isUnmodifiable( + bmm.tree.getBookmarkNodeById(parentId)); e.canExecute = !isSearch && canEdit && !unmodifiable; break; @@ -513,18 +518,18 @@ function canExecuteShared(e, isSearch) { /** * Helper function for handling canExecute for the list and document. - * @param {!Event} e Can execute event object. + * @param {!cr.ui.CanExecuteEvent} e Can execute event object. */ function canExecuteForList(e) { var command = e.command; var commandId = command.id; function hasSelected() { - return !!list.selectedItem; + return !!bmm.list.selectedItem; } function hasSingleSelected() { - return list.selectedItems.length == 1; + return bmm.list.selectedItems.length == 1; } function canCopyItem(item) { @@ -532,18 +537,18 @@ function canExecuteForList(e) { } function canCopyItems() { - var selectedItems = list.selectedItems; + var selectedItems = bmm.list.selectedItems; return selectedItems && selectedItems.some(canCopyItem); } function isSearch() { - return list.isSearch(); + return bmm.list.isSearch(); } switch (commandId) { case 'rename-folder-command': // Show rename if a single folder is selected. - var items = list.selectedItems; + var items = bmm.list.selectedItems; if (items.length != 1) { e.canExecute = false; command.hidden = true; @@ -556,7 +561,7 @@ function canExecuteForList(e) { case 'edit-command': // Show the edit command if not a folder. - var items = list.selectedItems; + var items = bmm.list.selectedItems; if (items.length != 1) { e.canExecute = false; command.hidden = false; @@ -574,7 +579,7 @@ function canExecuteForList(e) { case 'delete-command': case 'cut-command': e.canExecute = canCopyItems() && canEdit && - !hasUnmodifiable(list.selectedItems); + !hasUnmodifiable(bmm.list.selectedItems); break; case 'copy-command': @@ -592,13 +597,13 @@ function canExecuteForList(e) { // Update canExecute for the commands when the list is the active element. function handleCanExecuteForList(e) { - if (e.target != list) return; + if (e.target != bmm.list) return; canExecuteForList(e); } // Update canExecute for the commands when the tree is the active element. function handleCanExecuteForTree(e) { - if (e.target != tree) return; + if (e.target != bmm.tree) return; var command = e.command; var commandId = command.id; @@ -613,14 +618,14 @@ function handleCanExecuteForTree(e) { } function isTopLevelItem() { - return e.target.selectedItem.parentNode == tree; + return e.target.selectedItem.parentNode == bmm.tree; } switch (commandId) { case 'rename-folder-command': command.hidden = false; e.canExecute = hasSelected() && !isTopLevelItem() && canEdit && - !hasUnmodifiable(tree.selectedFolders); + !hasUnmodifiable(bmm.tree.selectedFolders); break; case 'edit-command': @@ -631,7 +636,7 @@ function handleCanExecuteForTree(e) { case 'delete-command': case 'cut-command': e.canExecute = hasSelected() && !isTopLevelItem() && canEdit && - !hasUnmodifiable(tree.selectedFolders); + !hasUnmodifiable(bmm.tree.selectedFolders); break; case 'copy-command': @@ -669,7 +674,7 @@ function updateEditingCommands() { } function handleChangeForTree(e) { - navigateTo(tree.selectedItem.bookmarkId); + navigateTo(bmm.tree.selectedItem.bookmarkId); } function handleOrganizeButtonClick(e) { @@ -696,22 +701,22 @@ function handleEdit(e) { context.url = bookmarkNode.url; if (bookmarkNode.id == 'new') { - selectItemsAfterUserAction(list); + selectItemsAfterUserAction(/** @type {BookmarkList} */(bmm.list)); // New page context.parentId = bookmarkNode.parentId; chrome.bookmarks.create(context, function(node) { // A new node was created and will get added to the list due to the // handler. - var dataModel = list.dataModel; + var dataModel = bmm.list.dataModel; var index = dataModel.indexOf(bookmarkNode); dataModel.splice(index, 1); // Select new item. var newIndex = dataModel.findIndexById(node.id); if (newIndex != -1) { - var sm = list.selectionModel; - list.scrollIndexIntoView(newIndex); + var sm = bmm.list.selectionModel; + bmm.list.scrollIndexIntoView(newIndex); sm.leadIndex = sm.anchorIndex = sm.selectedIndex = newIndex; } }); @@ -726,7 +731,7 @@ function handleCancelEdit(e) { var item = e.target; var bookmarkNode = item.bookmarkNode; if (bookmarkNode.id == 'new') { - var dataModel = list.dataModel; + var dataModel = bmm.list.dataModel; var index = dataModel.findIndexById('new'); dataModel.splice(index, 1); } @@ -737,19 +742,19 @@ function handleCancelEdit(e) { * used for the show-in-folder command. */ function showInFolder() { - var bookmarkNode = list.selectedItem; + var bookmarkNode = bmm.list.selectedItem; if (!bookmarkNode) return; var parentId = bookmarkNode.parentId; // After the list is loaded we should select the revealed item. function selectItem() { - var index = list.dataModel.findIndexById(bookmarkNode.id); + var index = bmm.list.dataModel.findIndexById(bookmarkNode.id); if (index == -1) return; - var sm = list.selectionModel; + var sm = bmm.list.selectionModel; sm.anchorIndex = sm.leadIndex = sm.selectedIndex = index; - list.scrollIndexIntoView(index); + bmm.list.scrollIndexIntoView(index); } var treeItem = bmm.treeLookup[parentId]; @@ -771,12 +776,12 @@ function getLinkController() { * Returns the selected bookmark nodes of the provided tree or list. * If |opt_target| is not provided or null the active element is used. * Only call this if the list or the tree is focused. - * @param {BookmarkList|BookmarkTree} opt_target The target list or tree. + * @param {(BookmarkList|BookmarkTree)=} opt_target The target list or tree. * @return {!Array} Array of bookmark nodes. */ function getSelectedBookmarkNodes(opt_target) { - return (opt_target || document.activeElement) == tree ? - tree.selectedFolders : list.selectedItems; + return (opt_target || document.activeElement) == bmm.tree ? + bmm.tree.selectedFolders : bmm.list.selectedItems; } /** @@ -795,11 +800,11 @@ function getSelectedBookmarkIds() { * @return {boolean} Whether the given node is unmodifiable. */ function isUnmodifiable(node) { - return node && node.unmodifiable; + return !!(node && node.unmodifiable); } /** - * @param {BookmarkList} A list of BookmarkNodes. + * @param {Array.} nodes A list of BookmarkTreeNodes. * @return {boolean} Whether any of the nodes is managed. */ function hasUnmodifiable(nodes) { @@ -808,17 +813,17 @@ function hasUnmodifiable(nodes) { /** * Opens the selected bookmarks. - * @param {LinkKind} kind The kind of link we want to open. - * @param {HTMLElement} opt_eventTarget The target of the user initiated event. + * @param {cr.LinkKind} kind The kind of link we want to open. + * @param {HTMLElement=} opt_eventTarget The target of the user initiated event. */ function openBookmarks(kind, opt_eventTarget) { // If we have selected any folders, we need to find all the bookmarks one // level down. We use multiple async calls to getSubtree instead of getting // the whole tree since we would like to minimize the amount of data sent. - var urlsP = getUrlsForOpenCommands(opt_eventTarget); + var urlsP = getUrlsForOpenCommands(opt_eventTarget ? opt_eventTarget : null); urlsP.then(function(urls) { - getLinkController().openUrls(urls, kind); + getLinkController().openUrls(assert(urls), kind); chrome.bookmarkManagerPrivate.recordLaunch(); }); } @@ -840,9 +845,8 @@ function openItem() { * This ensures children of deleted folders do not remain in results */ function updateSearchResults() { - if (list.isSearch()) { - list.reload(); - } + if (bmm.list.isSearch()) + bmm.list.reload(); } /** @@ -879,12 +883,13 @@ function deleteBookmarks() { /** * Restores a tree of bookmarks under a specified folder. * @param {BookmarkTreeNode} node The node to restore. - * @param {=string} parentId The ID of the folder to restore under. If not - * specified, the original parentId of the node will be used. + * @param {(string|number)=} opt_parentId If a string is passed, it's the ID of + * the folder to restore under. If not specified or a number is passed, the + * original parentId of the node will be used. */ -function restoreTree(node, parentId) { +function restoreTree(node, opt_parentId) { var bookmarkInfo = { - parentId: parentId || node.parentId, + parentId: typeof opt_parentId == 'string' ? opt_parentId : node.parentId, title: node.title, index: node.index, url: node.url @@ -926,11 +931,11 @@ function undoDelete() { * @return {string} The id of folder node where we'll create new page/folder. */ function computeParentFolderForNewItem() { - if (document.activeElement == tree) - return list.parentId; - var selectedItem = list.selectedItem; + if (document.activeElement == bmm.tree) + return bmm.list.parentId; + var selectedItem = bmm.list.selectedItem; return selectedItem && bmm.isFolder(selectedItem) ? - selectedItem.id : list.parentId; + selectedItem.id : bmm.list.parentId; } /** @@ -938,10 +943,10 @@ function computeParentFolderForNewItem() { * selected item. */ function editSelectedItem() { - if (document.activeElement == tree) { - tree.selectedItem.editing = true; + if (document.activeElement == bmm.tree) { + bmm.tree.selectedItem.editing = true; } else { - var li = list.getListItem(list.selectedItem); + var li = bmm.list.getListItem(bmm.list.selectedItem); if (li) li.editing = true; } @@ -964,7 +969,7 @@ function newFolder() { }, callback); } - if (document.activeElement == tree) { + if (document.activeElement == bmm.tree) { createFolder(function(newNode) { navigateTo(newNode.id, function() { bmm.treeLookup[newNode.id].editing = true; @@ -975,8 +980,8 @@ function newFolder() { function editNewFolderInList() { createFolder(function() { - var index = list.dataModel.length - 1; - var sm = list.selectionModel; + var index = bmm.list.dataModel.length - 1; + var sm = bmm.list.selectionModel; sm.anchorIndex = sm.leadIndex = sm.selectedIndex = index; scrollIntoViewAndMakeEditable(index); }); @@ -990,14 +995,14 @@ function newFolder() { * @param {number} index The index of the item to make editable. */ function scrollIntoViewAndMakeEditable(index) { - list.scrollIndexIntoView(index); + bmm.list.scrollIndexIntoView(index); // onscroll is now dispatched asynchronously so we have to postpone // the rest. setTimeout(function() { - var item = list.getListItemByIndex(index); + var item = bmm.list.getListItemByIndex(index); if (item) item.editing = true; - }); + }, 0); } /** @@ -1014,10 +1019,10 @@ function addPage() { parentId: parentId, id: 'new' }; - var dataModel = list.dataModel; + var dataModel = bmm.list.dataModel; var length = dataModel.length; dataModel.splice(length, 0, fakeNode); - var sm = list.selectionModel; + var sm = bmm.list.selectionModel; sm.anchorIndex = sm.leadIndex = sm.selectedIndex = length; scrollIntoViewAndMakeEditable(length); }; @@ -1029,7 +1034,7 @@ function addPage() { * This function is used to select items after a user action such as paste, drop * add page etc. * @param {BookmarkList|BookmarkTree} target The target of the user action. - * @param {=string} opt_selectedTreeId If provided, then select that tree id. + * @param {string=} opt_selectedTreeId If provided, then select that tree id. */ function selectItemsAfterUserAction(target, opt_selectedTreeId) { // We get one onCreated event per item so we delay the handling until we get @@ -1040,7 +1045,7 @@ function selectItemsAfterUserAction(target, opt_selectedTreeId) { function handle(id, bookmarkNode) { clearTimeout(timer); - if (opt_selectedTreeId || list.parentId == bookmarkNode.parentId) + if (opt_selectedTreeId || bmm.list.parentId == bookmarkNode.parentId) ids.push(id); timer = setTimeout(handleTimeout, 50); } @@ -1052,25 +1057,25 @@ function selectItemsAfterUserAction(target, opt_selectedTreeId) { if (opt_selectedTreeId && ids.indexOf(opt_selectedTreeId) != -1) { var index = ids.indexOf(opt_selectedTreeId); if (index != -1 && opt_selectedTreeId in bmm.treeLookup) { - tree.selectedItem = bmm.treeLookup[opt_selectedTreeId]; + bmm.tree.selectedItem = bmm.treeLookup[opt_selectedTreeId]; } - } else if (target == list) { - var dataModel = list.dataModel; + } else if (target == bmm.list) { + var dataModel = bmm.list.dataModel; var firstIndex = dataModel.findIndexById(ids[0]); var lastIndex = dataModel.findIndexById(ids[ids.length - 1]); if (firstIndex != -1 && lastIndex != -1) { - var selectionModel = list.selectionModel; + var selectionModel = bmm.list.selectionModel; selectionModel.selectedIndex = -1; selectionModel.selectRange(firstIndex, lastIndex); selectionModel.anchorIndex = selectionModel.leadIndex = lastIndex; - list.focus(); + bmm.list.focus(); } } - list.endBatchUpdates(); + bmm.list.endBatchUpdates(); } - list.startBatchUpdates(); + bmm.list.startBatchUpdates(); chrome.bookmarks.onCreated.addListener(handle); chrome.bookmarks.onMoved.addListener(handle); @@ -1091,11 +1096,11 @@ function recordUserAction(name) { * tree view or list view). */ function getSelectedId() { - if (document.activeElement == tree) - return tree.selectedItem.bookmarkId; - var selectedItem = list.selectedItem; + if (document.activeElement == bmm.tree) + return bmm.tree.selectedItem.bookmarkId; + var selectedItem = bmm.list.selectedItem; return selectedItem && bmm.isFolder(selectedItem) ? - selectedItem.id : tree.selectedItem.bookmarkId; + selectedItem.id : bmm.tree.selectedItem.bookmarkId; } /** @@ -1105,7 +1110,7 @@ function getSelectedId() { */ function pasteBookmark(id) { recordUserAction('Paste'); - selectItemsAfterUserAction(list); + selectItemsAfterUserAction(/** @type {BookmarkList} */(bmm.list)); chrome.bookmarkManagerPrivate.paste(id, getSelectedBookmarkIds()); } @@ -1130,7 +1135,8 @@ function hasSelectedAncestor(parentNode) { return true; // Keep digging. - return hasSelectedAncestor(tree.getBookmarkNodeById(parentNode.parentId)); + return hasSelectedAncestor( + bmm.tree.getBookmarkNodeById(parentNode.parentId)); } function getFilteredSelectedBookmarkIds() { @@ -1140,7 +1146,7 @@ function getFilteredSelectedBookmarkIds() { var nodes = getSelectedBookmarkNodes(); for (var i = 0; i < nodes.length; i++) - if (!hasSelectedAncestor(tree.getBookmarkNodeById(nodes[i].parentId))) + if (!hasSelectedAncestor(bmm.tree.getBookmarkNodeById(nodes[i].parentId))) filteredIds.splice(0, 0, nodes[i].id); return filteredIds; @@ -1178,15 +1184,18 @@ function handleCommand(e) { case 'open-in-new-tab-command': case 'open-in-background-tab-command': recordUserAction('OpenInNewTab'); - openBookmarks(LinkKind.BACKGROUND_TAB, e.target); + openBookmarks(LinkKind.BACKGROUND_TAB, + assertInstanceof(e.target, HTMLElement)); break; case 'open-in-new-window-command': recordUserAction('OpenInNewWindow'); - openBookmarks(LinkKind.WINDOW, e.target); + openBookmarks(LinkKind.WINDOW, + assertInstanceof(e.target, HTMLElement)); break; case 'open-incognito-window-command': recordUserAction('OpenIncognito'); - openBookmarks(LinkKind.INCOGNITO, e.target); + openBookmarks(LinkKind.INCOGNITO, + assertInstanceof(e.target, HTMLElement)); break; case 'delete-command': recordUserAction('Delete'); @@ -1206,14 +1215,14 @@ function handleCommand(e) { }); break; case 'paste-from-organize-menu-command': - pasteBookmark(list.parentId); + pasteBookmark(bmm.list.parentId); break; case 'paste-from-context-menu-command': pasteBookmark(getSelectedId()); break; case 'sort-command': recordUserAction('Sort'); - chrome.bookmarkManagerPrivate.sortChildren(list.parentId); + chrome.bookmarkManagerPrivate.sortChildren(bmm.list.parentId); break; case 'rename-folder-command': editSelectedItem(); @@ -1246,7 +1255,8 @@ function handleCommand(e) { // shortcuts for these commands. function installEventHandlerForCommand(eventName, commandId) { function handle(e) { - if (document.activeElement != list && document.activeElement != tree) + if (document.activeElement != bmm.list && + document.activeElement != bmm.tree) return; var command = $(commandId); if (!command.disabled) { @@ -1271,11 +1281,13 @@ function initializeSplitter() { Splitter.decorate(splitter); // The splitter persists the size of the left component in the local store. - if ('treeWidth' in localStorage) - splitter.previousElementSibling.style.width = localStorage['treeWidth']; + if ('treeWidth' in window.localStorage) + splitter.previousElementSibling.style.width = + window.localStorage['treeWidth']; splitter.addEventListener('resize', function(e) { - localStorage['treeWidth'] = splitter.previousElementSibling.style.width; + window.localStorage['treeWidth'] = + splitter.previousElementSibling.style.width; }); } @@ -1295,36 +1307,38 @@ function continueInitializeBookmarkManager(localizedStrings) { cr.ui.decorate('menu', Menu); cr.ui.decorate('button[menu]', MenuButton); cr.ui.decorate('command', Command); - BookmarkList.decorate(list); - BookmarkTree.decorate(tree); - - list.addEventListener('canceledit', handleCancelEdit); - list.addEventListener('canExecute', handleCanExecuteForList); - list.addEventListener('change', updateAllCommands); - list.addEventListener('contextmenu', updateEditingCommands); - list.addEventListener('dblclick', handleDoubleClickForList); - list.addEventListener('edit', handleEdit); - list.addEventListener('rename', handleRename); - list.addEventListener('urlClicked', handleUrlClickedForList); - - tree.addEventListener('canExecute', handleCanExecuteForTree); - tree.addEventListener('change', handleChangeForTree); - tree.addEventListener('contextmenu', updateEditingCommands); - tree.addEventListener('rename', handleRename); - tree.addEventListener('load', handleLoadForTree); - - cr.ui.contextMenuHandler.addContextMenuProperty(tree); - list.contextMenu = $('context-menu'); - tree.contextMenu = $('context-menu'); + BookmarkList.decorate($('list')); + BookmarkTree.decorate($('tree')); + + bmm.list.addEventListener('canceledit', handleCancelEdit); + bmm.list.addEventListener('canExecute', handleCanExecuteForList); + bmm.list.addEventListener('change', updateAllCommands); + bmm.list.addEventListener('contextmenu', updateEditingCommands); + bmm.list.addEventListener('dblclick', handleDoubleClickForList); + bmm.list.addEventListener('edit', handleEdit); + bmm.list.addEventListener('rename', handleRename); + bmm.list.addEventListener('urlClicked', handleUrlClickedForList); + + bmm.tree.addEventListener('canExecute', handleCanExecuteForTree); + bmm.tree.addEventListener('change', handleChangeForTree); + bmm.tree.addEventListener('contextmenu', updateEditingCommands); + bmm.tree.addEventListener('rename', handleRename); + bmm.tree.addEventListener('load', handleLoadForTree); + + cr.ui.contextMenuHandler.addContextMenuProperty( + /** @type {!Element} */(bmm.tree)); + bmm.list.contextMenu = $('context-menu'); + bmm.tree.contextMenu = $('context-menu'); // We listen to hashchange so that we can update the currently shown folder // when // the user goes back and forward in the history. window.addEventListener('hashchange', processHash); - document.querySelector('header form').onsubmit = function(e) { + document.querySelector('header form').onsubmit = + /** @type {function(Event=)} */(function(e) { setSearch($('term').value); e.preventDefault(); - }; + }); $('term').addEventListener('search', handleSearch); @@ -1371,7 +1385,7 @@ function continueInitializeBookmarkManager(localizedStrings) { initializeSplitter(); bmm.addBookmarkModelListeners(); dnd.init(selectItemsAfterUserAction); - tree.reload(); + bmm.tree.reload(); } initializeBookmarkManager(); diff --git a/chrome/browser/ui/webui/options/startup_page_list_browsertest.js b/chrome/browser/ui/webui/options/startup_page_list_browsertest.js index 25e5fd4ee7b04..27ac3a0ebba8b 100644 --- a/chrome/browser/ui/webui/options/startup_page_list_browsertest.js +++ b/chrome/browser/ui/webui/options/startup_page_list_browsertest.js @@ -124,25 +124,35 @@ function createMouseEvent(type) { TEST_F('StartupPageListWebUITest', 'testDropFromOutsideSource', function() { /** @const */ var NEW_PAGE = 'http://google.com'; - + console.warn('VPDBG: 127'); var mockDropEvent = createMouseEvent('drop'); + console.warn('VPDBG: 129'); mockDropEvent.dataTransfer.setData('url', NEW_PAGE); + console.warn('VPDBG: 131'); this.mockHandler.expects(once()).addStartupPage([NEW_PAGE, 0]); + console.warn('VPDBG: 134'); this.getList().items[0].dispatchEvent(mockDropEvent); + console.warn('VPDBG: 137'); expectTrue(mockDropEvent.defaultPrevented); + console.warn('VPDBG: 140'); }); TEST_F('StartupPageListWebUITest', 'testDropToReorder', function() { // TODO(dbeam): mock4js doesn't handle complex arguments well. Fix this. + console.warn('VPDBG: 145'); this.mockHandler.expects(once()).dragDropStartupPage([0, [1].join()]); + console.warn('VPDBG: 147'); this.getList().selectionModel.selectedIndex = 1; + console.warn('VPDBG: 150'); expectEquals(1, this.getList().selectionModel.selectedIndexes.length); + console.warn('VPDBG: 152'); this.getList().items[0].dispatchEvent(createMouseEvent('drop')); + console.warn('VPDBG: 155'); }); }()); diff --git a/third_party/closure_compiler/externs/bookmark_manager_private.js b/third_party/closure_compiler/externs/bookmark_manager_private.js new file mode 100644 index 0000000000000..e59da2b7a435c --- /dev/null +++ b/third_party/closure_compiler/externs/bookmark_manager_private.js @@ -0,0 +1,199 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** @fileoverview Externs generated from namespace: bookmarkManagerPrivate */ + +/** + * @typedef {{ + * id: (string|undefined), + * parentId: (string|undefined), + * title: string, + * url: (string|undefined), + * children: Array + * }} + */ +var BookmarkNodeDataElement; + +/** + * Information about the drag and drop data for use with drag and drop events. + * @typedef {{ + * sameProfile: boolean, + * elements: Array + * }} + */ +var BookmarkNodeData; + +/** + * Collection of meta info fields. + * @typedef {Object} + */ +var MetaInfoFields; + +/** + * @const + */ +chrome.bookmarkManagerPrivate = {}; + +/** + * Copies the given bookmarks into the clipboard + * @param {Array} idList An array of string-valued ids + * @param {Function=} callback + */ +chrome.bookmarkManagerPrivate.copy = function(idList, callback) {}; + +/** + * Cuts the given bookmarks into the clipboard + * @param {Array} idList An array of string-valued ids + * @param {Function=} callback + */ +chrome.bookmarkManagerPrivate.cut = function(idList, callback) {}; + +/** + * Pastes bookmarks from the clipboard into the parent folder after the last + * selected node + * @param {string} parentId + * @param {Array=} selectedIdList An array of string-valued ids for selected + * bookmarks + * @param {Function=} callback + */ +chrome.bookmarkManagerPrivate.paste = function(parentId, selectedIdList, callback) {}; + +/** + * Whether there are any bookmarks that can be pasted + * @param {string} parentId The ID of the folder to paste into + * @param {Function} callback + */ +chrome.bookmarkManagerPrivate.canPaste = function(parentId, callback) {}; + +/** + * Sorts the children of a given folder + * @param {string} parentId The ID of the folder to sort the children of + */ +chrome.bookmarkManagerPrivate.sortChildren = function(parentId) {}; + +/** + * Gets the i18n strings for the bookmark manager + * @param {Function} callback + */ +chrome.bookmarkManagerPrivate.getStrings = function(callback) {}; + +/** + * Begins dragging a set of bookmarks + * @param {Array} idList An array of string-valued ids + * @param {boolean} isFromTouch True if the drag was initiated from touch + */ +chrome.bookmarkManagerPrivate.startDrag = function(idList, isFromTouch) {}; + +/** + * Performs the drop action of the drag and drop session + * @param {string} parentId The ID of the folder that the drop was made + * @param {number=} index The index of the position to drop at. If left out the + * dropped items will be placed at the end of the existing children + */ +chrome.bookmarkManagerPrivate.drop = function(parentId, index) {}; + +/** + * Retrieves a bookmark hierarchy from the given node. If the node id is + * empty, it is the full tree. If foldersOnly is true, it will only return + * folders, not actual bookmarks. + * @param {string} id ID of the root of the tree to pull. If empty, the entire + * tree will be returned. + * @param {boolean} foldersOnly Pass true to only return folders. + * @param {Function} callback + */ +chrome.bookmarkManagerPrivate.getSubtree = function(id, foldersOnly, callback) {}; + +/** + * Whether bookmarks can be modified + * @param {Function} callback + */ +chrome.bookmarkManagerPrivate.canEdit = function(callback) {}; + +/** + * Whether bookmarks can be opened in new windows + * @param {Function} callback + */ +chrome.bookmarkManagerPrivate.canOpenNewWindows = function(callback) {}; + +/** + * Recursively removes list of bookmarks nodes. + * @param {Array} idList An array of string-valued ids + * @param {Function=} callback + */ +chrome.bookmarkManagerPrivate.removeTrees = function(idList, callback) {}; + +/** + */ +chrome.bookmarkManagerPrivate.recordLaunch = function() {}; + +/** + * Mimics the functionality of bookmarks.create, but will additionally set the + * given meta info fields. + * @param {chrome.bookmarks.CreateDetails} bookmark + * @param {MetaInfoFields} metaInfo + * @param {Function=} callback + */ +chrome.bookmarkManagerPrivate.createWithMetaInfo = function(bookmark, metaInfo, callback) {}; + +/** + * Gets meta info from a bookmark node + * @param {string=} id The id of the bookmark to retrieve meta info from. If + * omitted meta info for all nodes is returned. + * @param {string=} key The key for the meta info to retrieve. If omitted, all + * fields are returned + * @param {Function=} callback + */ +chrome.bookmarkManagerPrivate.getMetaInfo = function(id, key, callback) {}; + +/** + * Sets a meta info value for a bookmark node + * @param {string} id The id of the bookmark node to set the meta info on + * @param {string} key The key of the meta info to set + * @param {string} value The meta info to set + * @param {Function=} callback + */ +chrome.bookmarkManagerPrivate.setMetaInfo = function(id, key, value, callback) {}; + +/** + * Updates a set of meta info values for a bookmark node. + * @param {string} id The id of the bookmark node to update the meta info of. + * @param {MetaInfoFields} metaInfoChanges A set of meta info key/value pairs + * to update. + * @param {Function=} callback + */ +chrome.bookmarkManagerPrivate.updateMetaInfo = function(id, metaInfoChanges, callback) {}; + +/** + * Performs an undo of the last change to the bookmark model + */ +chrome.bookmarkManagerPrivate.undo = function() {}; + +/** + * Performs a redo of last undone change to the bookmark model + */ +chrome.bookmarkManagerPrivate.redo = function() {}; + +/** + * Gets the information for the undo if available + * @param {Function} callback + */ +chrome.bookmarkManagerPrivate.getUndoInfo = function(callback) {}; + +/** + * Gets the information for the redo if available + * @param {Function} callback + */ +chrome.bookmarkManagerPrivate.getRedoInfo = function(callback) {}; + +/** @type {!ChromeEvent} */ +chrome.bookmarkManagerPrivate.onDragEnter; + +/** @type {!ChromeEvent} */ +chrome.bookmarkManagerPrivate.onDragLeave; + +/** @type {!ChromeEvent} */ +chrome.bookmarkManagerPrivate.onDrop; + +/** @type {!ChromeEvent} */ +chrome.bookmarkManagerPrivate.onMetaInfoChanged; diff --git a/third_party/closure_compiler/externs/metrics_private.js b/third_party/closure_compiler/externs/metrics_private.js new file mode 100644 index 0000000000000..b2c9ef85dc227 --- /dev/null +++ b/third_party/closure_compiler/externs/metrics_private.js @@ -0,0 +1,117 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** @fileoverview Externs generated from namespace: metricsPrivate */ + +/** + * Describes the type of metric that is to be collected. + * @typedef {{ + * metricName: string, + * type: string, + * min: number, + * max: number, + * buckets: number + * }} + */ +var MetricType; + +/** + * @const + */ +chrome.metricsPrivate = {}; + +/** + * Returns true if the user opted in to sending crash reports. + * @param {Function} callback + */ +chrome.metricsPrivate.getIsCrashReportingEnabled = function(callback) {}; + +/** + * Returns the group name chosen for the named trial, or the empty string if + * the trial does not exist or is not enabled. + * @param {string} name + * @param {Function} callback + */ +chrome.metricsPrivate.getFieldTrial = function(name, callback) {}; + +/** + * Returns variation parameters for the named trial if available, or undefined + * otherwise. + * @param {string} name + * @param {Function} callback + */ +chrome.metricsPrivate.getVariationParams = function(name, callback) {}; + +/** + * Records an action performed by the user. + * @param {string} name + */ +chrome.metricsPrivate.recordUserAction = function(name) {}; + +/** + * Records a percentage value from 1 to 100. + * @param {string} metricName + * @param {number} value + */ +chrome.metricsPrivate.recordPercentage = function(metricName, value) {}; + +/** + * Records a value than can range from 1 to 1,000,000. + * @param {string} metricName + * @param {number} value + */ +chrome.metricsPrivate.recordCount = function(metricName, value) {}; + +/** + * Records a value than can range from 1 to 100. + * @param {string} metricName + * @param {number} value + */ +chrome.metricsPrivate.recordSmallCount = function(metricName, value) {}; + +/** + * Records a value than can range from 1 to 10,000. + * @param {string} metricName + * @param {number} value + */ +chrome.metricsPrivate.recordMediumCount = function(metricName, value) {}; + +/** + * Records an elapsed time of no more than 10 seconds. The sample value is + * specified in milliseconds. + * @param {string} metricName + * @param {number} value + */ +chrome.metricsPrivate.recordTime = function(metricName, value) {}; + +/** + * Records an elapsed time of no more than 3 minutes. The sample value is + * specified in milliseconds. + * @param {string} metricName + * @param {number} value + */ +chrome.metricsPrivate.recordMediumTime = function(metricName, value) {}; + +/** + * Records an elapsed time of no more than 1 hour. The sample value is + * specified in milliseconds. + * @param {string} metricName + * @param {number} value + */ +chrome.metricsPrivate.recordLongTime = function(metricName, value) {}; + +/** + * Increments the count associated with |value| in the sparse histogram defined + * by the |metricName|. + * @param {string} metricName + * @param {number} value + */ +chrome.metricsPrivate.recordSparseValue = function(metricName, value) {}; + +/** + * Adds a value to the given metric. + * @param {MetricType} metric + * @param {number} value + */ +chrome.metricsPrivate.recordValue = function(metric, value) {}; diff --git a/third_party/closure_compiler/externs/system_private.js b/third_party/closure_compiler/externs/system_private.js new file mode 100644 index 0000000000000..0ea24761dbdc3 --- /dev/null +++ b/third_party/closure_compiler/externs/system_private.js @@ -0,0 +1,69 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** @fileoverview Externs generated from namespace: systemPrivate */ + +/** + * Information about the system update. + * @typedef {{ + * state: string, + * downloadProgress: number + * }} + */ +var UpdateStatus; + +/** + * Information about the volume. + * @typedef {{ + * volume: number, + * isVolumeMuted: boolean + * }} + */ +var VolumeInfo; + +/** + * Information about a change to the screen brightness. + * @typedef {{ + * brightness: number, + * userInitiated: boolean + * }} + */ +var BrightnessChangeInfo; + +/** + * @const + */ +chrome.systemPrivate = {}; + +/** + * Returns whether the incognito mode is enabled, disabled or forced + * @param {Function} callback Called with the result. + */ +chrome.systemPrivate.getIncognitoModeAvailability = function(callback) {}; + +/** + * Gets information about the system update. + * @param {Function} callback + */ +chrome.systemPrivate.getUpdateStatus = function(callback) {}; + +/** + * Gets Chrome's API key to use for requests to Google services. + * @param {Function} callback + */ +chrome.systemPrivate.getApiKey = function(callback) {}; + +/** @type {!ChromeEvent} */ +chrome.systemPrivate.onVolumeChanged; + +/** @type {!ChromeEvent} */ +chrome.systemPrivate.onBrightnessChanged; + +/** @type {!ChromeEvent} */ +chrome.systemPrivate.onScreenUnlocked; + +/** @type {!ChromeEvent} */ +chrome.systemPrivate.onWokeUp; + + diff --git a/third_party/closure_compiler/runner/src/com/google/javascript/jscomp/ChromePass.java b/third_party/closure_compiler/runner/src/com/google/javascript/jscomp/ChromePass.java index d897823580034..274f0f892877e 100644 --- a/third_party/closure_compiler/runner/src/com/google/javascript/jscomp/ChromePass.java +++ b/third_party/closure_compiler/runner/src/com/google/javascript/jscomp/ChromePass.java @@ -313,11 +313,11 @@ private Map objectLitToMap(Node objectLit) { for (Node keyNode : objectLit.children()) { String key = keyNode.getString(); - // TODO(vitalyp): Can dict value be other than a simple NAME? What if NAME doesn't - // refer to a function/constructor? - String value = keyNode.getFirstChild().getString(); - - res.put(value, key); + Node valueNode = keyNode.getFirstChild(); + if (valueNode.isName()) { + String value = keyNode.getFirstChild().getString(); + res.put(value, key); + } } return res; diff --git a/third_party/closure_compiler/runner/test/com/google/javascript/jscomp/ChromePassTest.java b/third_party/closure_compiler/runner/test/com/google/javascript/jscomp/ChromePassTest.java index d6264c1ad0691..830b9ba414499 100644 --- a/third_party/closure_compiler/runner/test/com/google/javascript/jscomp/ChromePassTest.java +++ b/third_party/closure_compiler/runner/test/com/google/javascript/jscomp/ChromePassTest.java @@ -192,6 +192,36 @@ public void testCrDefineDoesNothingWithNonExportedVar() throws Exception { "});\n"); } + public void testCrDefineDoesNothingWithExportedNotAName() throws Exception { + test( + "cr.define('namespace', function() {\n" + + " return {\n" + + " a: 42\n" + + " };\n" + + "});\n", + "var namespace = namespace || {};\n" + + "cr.define('namespace', function() {\n" + + " return {\n" + + " a: 42\n" + + " };\n" + + "});\n"); + } + + public void testCrDefineDoesNothingWithExportedNotAName() throws Exception { + test( + "cr.define('namespace', function() {\n" + + " return {\n" + + " a: 42\n" + + " };\n" + + "});\n", + "var namespace = namespace || {};\n" + + "cr.define('namespace', function() {\n" + + " return {\n" + + " a: 42\n" + + " };\n" + + "});\n"); + } + public void testCrDefineChangesReferenceToExportedFunction() throws Exception { test( "cr.define('namespace', function() {\n" +