Bookmarks Web UI: Reformat HTML/JS files after Polymer 3 migration
Run clang-format on the JS files in:
chrome/browser/resources/bookmarks
chrome/test/data/webui/bookmarks
and remove extra indentation from HTML files in
chrome/browser/resources/bookmarks.
Bug: 1022213
Change-Id: I574e1c768247dbf5b437d5db9cf31bfe3f4cc738
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1922593
Commit-Queue: Rebekah Potter <[email protected]>
Reviewed-by: Demetrios Papadopoulos <[email protected]>
Cr-Commit-Position: refs/heads/master@{#716893}
diff --git a/chrome/browser/resources/bookmarks/command_manager.js b/chrome/browser/resources/bookmarks/command_manager.js
index 6123d01..3528a1a 100644
--- a/chrome/browser/resources/bookmarks/command_manager.js
+++ b/chrome/browser/resources/bookmarks/command_manager.js
@@ -6,922 +6,917 @@
* @fileoverview Element which shows context menus and handles keyboard
* shortcuts.
*/
-import {Polymer, html, flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.m.js';
import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
import 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.m.js';
import 'chrome://resources/cr_elements/shared_vars_css.m.js';
-import {assert} from 'chrome://resources/js/assert.m.js';
-import {isMac} from 'chrome://resources/js/cr.m.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
-import {getInstance} from 'chrome://resources/cr_elements/cr_toast/cr_toast_manager.m.js';
-import {KeyboardShortcutList} from 'chrome://resources/js/cr/ui/keyboard_shortcut_list.m.js';
import 'chrome://resources/polymer/v3_0/iron-a11y-keys-behavior/iron-a11y-keys-behavior.js';
-import {trackUpdatedItems, highlightUpdatedItems} from './api_listener.js';
-import {BrowserProxy} from './browser_proxy.js';
-import {Command, MenuSource, IncognitoAvailability, OPEN_CONFIRMATION_LIMIT, ROOT_NODE_ID} from './constants.js';
-import {DialogFocusManager} from './dialog_focus_manager.js';
import './edit_dialog.js';
import './shared_style.js';
-import {StoreClient} from './store_client.js';
import './strings.m.js';
+
+import {getInstance} from 'chrome://resources/cr_elements/cr_toast/cr_toast_manager.m.js';
+import {assert} from 'chrome://resources/js/assert.m.js';
+import {isMac} from 'chrome://resources/js/cr.m.js';
+import {KeyboardShortcutList} from 'chrome://resources/js/cr/ui/keyboard_shortcut_list.m.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {flush, html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {deselectItems, selectAll, selectFolder} from './actions.js';
+import {highlightUpdatedItems, trackUpdatedItems} from './api_listener.js';
+import {BrowserProxy} from './browser_proxy.js';
+import {Command, IncognitoAvailability, MenuSource, OPEN_CONFIRMATION_LIMIT, ROOT_NODE_ID} from './constants.js';
+import {DialogFocusManager} from './dialog_focus_manager.js';
+import {StoreClient} from './store_client.js';
import {BookmarkNode} from './types.js';
import {canEditNode, canReorderChildren, getDisplayedList} from './util.js';
-import {deselectItems, selectAll, selectFolder} from './actions.js';
- export const CommandManager = Polymer({
- is: 'bookmarks-command-manager',
+export const CommandManager = Polymer({
+ is: 'bookmarks-command-manager',
- _template: html`{__html_template__}`,
+ _template: html`{__html_template__}`,
- behaviors: [
- StoreClient,
- ],
+ behaviors: [
+ StoreClient,
+ ],
- properties: {
- /** @private {!Array<Command>} */
- menuCommands_: {
- type: Array,
- computed: 'computeMenuCommands_(menuSource_)',
- },
-
- /** @private {Set<string>} */
- menuIds_: Object,
-
- /** @private */
- hasAnySublabel_: {
- type: Boolean,
- reflectToAttribute: true,
- computed: 'computeHasAnySublabel_(menuCommands_, menuIds_)',
- },
-
- /**
- * Indicates where the context menu was opened from. Will be NONE if
- * menu is not open, indicating that commands are from keyboard shortcuts
- * or elsewhere in the UI.
- * @private {MenuSource}
- */
- menuSource_: {
- type: Number,
- value: MenuSource.NONE,
- },
-
- /** @private */
- canPaste_: Boolean,
-
- /** @private */
- globalCanEdit_: Boolean,
+ properties: {
+ /** @private {!Array<Command>} */
+ menuCommands_: {
+ type: Array,
+ computed: 'computeMenuCommands_(menuSource_)',
},
- /** @private {?Function} */
- confirmOpenCallback_: null,
+ /** @private {Set<string>} */
+ menuIds_: Object,
- attached: function() {
- assert(CommandManager.instance_ == null);
- CommandManager.instance_ = this;
+ /** @private */
+ hasAnySublabel_: {
+ type: Boolean,
+ reflectToAttribute: true,
+ computed: 'computeHasAnySublabel_(menuCommands_, menuIds_)',
+ },
- /** @private {!BrowserProxy} */
- this.browserProxy_ = BrowserProxy.getInstance();
+ /**
+ * Indicates where the context menu was opened from. Will be NONE if
+ * menu is not open, indicating that commands are from keyboard shortcuts
+ * or elsewhere in the UI.
+ * @private {MenuSource}
+ */
+ menuSource_: {
+ type: Number,
+ value: MenuSource.NONE,
+ },
- this.watch('globalCanEdit_', state => state.prefs.canEdit);
- this.updateFromStore();
+ /** @private */
+ canPaste_: Boolean,
- /** @private {!Map<Command, KeyboardShortcutList>} */
- this.shortcuts_ = new Map();
+ /** @private */
+ globalCanEdit_: Boolean,
+ },
- this.addShortcut_(Command.EDIT, 'F2', 'Enter');
- this.addShortcut_(Command.DELETE, 'Delete', 'Delete Backspace');
+ /** @private {?Function} */
+ confirmOpenCallback_: null,
- this.addShortcut_(Command.OPEN, 'Enter', 'Meta|o');
- this.addShortcut_(Command.OPEN_NEW_TAB, 'Ctrl|Enter', 'Meta|Enter');
- this.addShortcut_(Command.OPEN_NEW_WINDOW, 'Shift|Enter');
+ attached: function() {
+ assert(CommandManager.instance_ == null);
+ CommandManager.instance_ = this;
- // Note: the undo shortcut is also defined in bookmarks_ui.cc
- // TODO(b/893033): de-duplicate shortcut by moving all shortcut
- // definitions from JS to C++.
- this.addShortcut_(Command.UNDO, 'Ctrl|z', 'Meta|z');
- this.addShortcut_(Command.REDO, 'Ctrl|y Ctrl|Shift|Z', 'Meta|Shift|Z');
+ /** @private {!BrowserProxy} */
+ this.browserProxy_ = BrowserProxy.getInstance();
- this.addShortcut_(Command.SELECT_ALL, 'Ctrl|a', 'Meta|a');
- this.addShortcut_(Command.DESELECT_ALL, 'Escape');
+ this.watch('globalCanEdit_', state => state.prefs.canEdit);
+ this.updateFromStore();
- this.addShortcut_(Command.CUT, 'Ctrl|x', 'Meta|x');
- this.addShortcut_(Command.COPY, 'Ctrl|c', 'Meta|c');
- this.addShortcut_(Command.PASTE, 'Ctrl|v', 'Meta|v');
+ /** @private {!Map<Command, KeyboardShortcutList>} */
+ this.shortcuts_ = new Map();
- /** @private {!Map<string, Function>} */
- this.boundListeners_ = new Map();
+ this.addShortcut_(Command.EDIT, 'F2', 'Enter');
+ this.addShortcut_(Command.DELETE, 'Delete', 'Delete Backspace');
- const addDocumentListener = (eventName, handler) => {
- assert(!this.boundListeners_.has(eventName));
- const boundListener = handler.bind(this);
- this.boundListeners_.set(eventName, boundListener);
- document.addEventListener(eventName, boundListener);
- };
- addDocumentListener('open-command-menu', this.onOpenCommandMenu_);
- addDocumentListener('keydown', this.onKeydown_);
+ this.addShortcut_(Command.OPEN, 'Enter', 'Meta|o');
+ this.addShortcut_(Command.OPEN_NEW_TAB, 'Ctrl|Enter', 'Meta|Enter');
+ this.addShortcut_(Command.OPEN_NEW_WINDOW, 'Shift|Enter');
- const addDocumentListenerForCommand = (eventName, command) => {
- addDocumentListener(eventName, (e) => {
- if (e.path[0].tagName == 'INPUT') {
- return;
- }
+ // Note: the undo shortcut is also defined in bookmarks_ui.cc
+ // TODO(b/893033): de-duplicate shortcut by moving all shortcut
+ // definitions from JS to C++.
+ this.addShortcut_(Command.UNDO, 'Ctrl|z', 'Meta|z');
+ this.addShortcut_(Command.REDO, 'Ctrl|y Ctrl|Shift|Z', 'Meta|Shift|Z');
- const items = this.getState().selection.items;
- if (this.canExecute(command, items)) {
- this.handle(command, items);
- }
+ this.addShortcut_(Command.SELECT_ALL, 'Ctrl|a', 'Meta|a');
+ this.addShortcut_(Command.DESELECT_ALL, 'Escape');
+
+ this.addShortcut_(Command.CUT, 'Ctrl|x', 'Meta|x');
+ this.addShortcut_(Command.COPY, 'Ctrl|c', 'Meta|c');
+ this.addShortcut_(Command.PASTE, 'Ctrl|v', 'Meta|v');
+
+ /** @private {!Map<string, Function>} */
+ this.boundListeners_ = new Map();
+
+ const addDocumentListener = (eventName, handler) => {
+ assert(!this.boundListeners_.has(eventName));
+ const boundListener = handler.bind(this);
+ this.boundListeners_.set(eventName, boundListener);
+ document.addEventListener(eventName, boundListener);
+ };
+ addDocumentListener('open-command-menu', this.onOpenCommandMenu_);
+ addDocumentListener('keydown', this.onKeydown_);
+
+ const addDocumentListenerForCommand = (eventName, command) => {
+ addDocumentListener(eventName, (e) => {
+ if (e.path[0].tagName == 'INPUT') {
+ return;
+ }
+
+ const items = this.getState().selection.items;
+ if (this.canExecute(command, items)) {
+ this.handle(command, items);
+ }
+ });
+ };
+ addDocumentListenerForCommand('command-undo', Command.UNDO);
+ addDocumentListenerForCommand('cut', Command.CUT);
+ addDocumentListenerForCommand('copy', Command.COPY);
+ addDocumentListenerForCommand('paste', Command.PASTE);
+ },
+
+ detached: function() {
+ CommandManager.instance_ = null;
+ this.boundListeners_.forEach(
+ (handler, eventName) =>
+ document.removeEventListener(eventName, handler));
+ },
+
+ /**
+ * Display the command context menu at (|x|, |y|) in window coordinates.
+ * Commands will execute on |items| if given, or on the currently selected
+ * items.
+ * @param {number} x
+ * @param {number} y
+ * @param {MenuSource} source
+ * @param {Set<string>=} items
+ */
+ openCommandMenuAtPosition: function(x, y, source, items) {
+ this.menuSource_ = source;
+ this.menuIds_ = items || this.getState().selection.items;
+
+ const dropdown =
+ /** @type {!CrActionMenuElement} */ (this.$.dropdown.get());
+ // Ensure that the menu is fully rendered before trying to position it.
+ flush();
+ DialogFocusManager.getInstance().showDialog(
+ dropdown.getDialog(), function() {
+ dropdown.showAtPosition({top: y, left: x});
});
- };
- addDocumentListenerForCommand('command-undo', Command.UNDO);
- addDocumentListenerForCommand('cut', Command.CUT);
- addDocumentListenerForCommand('copy', Command.COPY);
- addDocumentListenerForCommand('paste', Command.PASTE);
- },
+ },
- detached: function() {
- CommandManager.instance_ = null;
- this.boundListeners_.forEach(
- (handler, eventName) =>
- document.removeEventListener(eventName, handler));
- },
+ /**
+ * Display the command context menu positioned to cover the |target|
+ * element. Commands will execute on the currently selected items.
+ * @param {!Element} target
+ * @param {MenuSource} source
+ */
+ openCommandMenuAtElement: function(target, source) {
+ this.menuSource_ = source;
+ this.menuIds_ = this.getState().selection.items;
- /**
- * Display the command context menu at (|x|, |y|) in window co-ordinates.
- * Commands will execute on |items| if given, or on the currently selected
- * items.
- * @param {number} x
- * @param {number} y
- * @param {MenuSource} source
- * @param {Set<string>=} items
- */
- openCommandMenuAtPosition: function(x, y, source, items) {
- this.menuSource_ = source;
- this.menuIds_ = items || this.getState().selection.items;
+ const dropdown =
+ /** @type {!CrActionMenuElement} */ (this.$.dropdown.get());
+ // Ensure that the menu is fully rendered before trying to position it.
+ flush();
+ DialogFocusManager.getInstance().showDialog(
+ dropdown.getDialog(), function() {
+ dropdown.showAt(target);
+ });
+ },
- const dropdown =
- /** @type {!CrActionMenuElement} */ (this.$.dropdown.get());
- // Ensure that the menu is fully rendered before trying to position it.
- flush();
- DialogFocusManager.getInstance().showDialog(
- dropdown.getDialog(), function() {
- dropdown.showAtPosition({top: y, left: x});
- });
- },
+ closeCommandMenu: function() {
+ this.menuIds_ = new Set();
+ this.menuSource_ = MenuSource.NONE;
+ /** @type {!CrActionMenuElement} */ (this.$.dropdown.get()).close();
+ },
- /**
- * Display the command context menu positioned to cover the |target|
- * element. Commands will execute on the currently selected items.
- * @param {!Element} target
- * @param {MenuSource} source
- */
- openCommandMenuAtElement: function(target, source) {
- this.menuSource_ = source;
- this.menuIds_ = this.getState().selection.items;
+ ////////////////////////////////////////////////////////////////////////////
+ // Command handlers:
- const dropdown =
- /** @type {!CrActionMenuElement} */ (this.$.dropdown.get());
- // Ensure that the menu is fully rendered before trying to position it.
- flush();
- DialogFocusManager.getInstance().showDialog(
- dropdown.getDialog(), function() {
- dropdown.showAt(target);
- });
- },
+ /**
+ * Determine if the |command| can be executed with the given |itemIds|.
+ * Commands which appear in the context menu should be implemented
+ * separately using `isCommandVisible_` and `isCommandEnabled_`.
+ * @param {Command} command
+ * @param {!Set<string>} itemIds
+ * @return {boolean}
+ */
+ canExecute: function(command, itemIds) {
+ const state = this.getState();
+ switch (command) {
+ case Command.OPEN:
+ return itemIds.size > 0;
+ case Command.UNDO:
+ case Command.REDO:
+ return this.globalCanEdit_;
+ case Command.SELECT_ALL:
+ case Command.DESELECT_ALL:
+ return true;
+ case Command.COPY:
+ return itemIds.size > 0;
+ case Command.CUT:
+ return itemIds.size > 0 &&
+ !this.containsMatchingNode_(itemIds, function(node) {
+ return !canEditNode(state, node.id);
+ });
+ case Command.PASTE:
+ return state.search.term == '' &&
+ canReorderChildren(state, state.selectedFolder);
+ default:
+ return this.isCommandVisible_(command, itemIds) &&
+ this.isCommandEnabled_(command, itemIds);
+ }
+ },
- closeCommandMenu: function() {
- this.menuIds_ = new Set();
- this.menuSource_ = MenuSource.NONE;
- /** @type {!CrActionMenuElement} */ (this.$.dropdown.get()).close();
- },
+ /**
+ * @param {Command} command
+ * @param {!Set<string>} itemIds
+ * @return {boolean} True if the command should be visible in the context
+ * menu.
+ */
+ isCommandVisible_: function(command, itemIds) {
+ switch (command) {
+ case Command.EDIT:
+ return itemIds.size == 1 && this.globalCanEdit_;
+ case Command.PASTE:
+ return this.globalCanEdit_;
+ case Command.CUT:
+ case Command.COPY:
+ return itemIds.size >= 1 && this.globalCanEdit_;
+ case Command.COPY_URL:
+ return this.isSingleBookmark_(itemIds);
+ case Command.DELETE:
+ return itemIds.size > 0 && this.globalCanEdit_;
+ case Command.SHOW_IN_FOLDER:
+ return this.menuSource_ == MenuSource.ITEM && itemIds.size == 1 &&
+ this.getState().search.term != '' &&
+ !this.containsMatchingNode_(itemIds, function(node) {
+ return !node.parentId || node.parentId == ROOT_NODE_ID;
+ });
+ case Command.OPEN_NEW_TAB:
+ case Command.OPEN_NEW_WINDOW:
+ case Command.OPEN_INCOGNITO:
+ return itemIds.size > 0;
+ case Command.ADD_BOOKMARK:
+ case Command.ADD_FOLDER:
+ case Command.SORT:
+ case Command.EXPORT:
+ case Command.IMPORT:
+ case Command.HELP_CENTER:
+ return true;
+ }
+ return assert(false);
+ },
- ////////////////////////////////////////////////////////////////////////////
- // Command handlers:
+ /**
+ * @param {Command} command
+ * @param {!Set<string>} itemIds
+ * @return {boolean} True if the command should be clickable in the context
+ * menu.
+ */
+ isCommandEnabled_: function(command, itemIds) {
+ const state = this.getState();
+ switch (command) {
+ case Command.EDIT:
+ case Command.DELETE:
+ return !this.containsMatchingNode_(itemIds, function(node) {
+ return !canEditNode(state, node.id);
+ });
+ case Command.OPEN_NEW_TAB:
+ case Command.OPEN_NEW_WINDOW:
+ return this.expandUrls_(itemIds).length > 0;
+ case Command.OPEN_INCOGNITO:
+ return this.expandUrls_(itemIds).length > 0 &&
+ state.prefs.incognitoAvailability != IncognitoAvailability.DISABLED;
+ case Command.SORT:
+ return this.canChangeList_() &&
+ state.nodes[state.selectedFolder].children.length > 1;
+ case Command.ADD_BOOKMARK:
+ case Command.ADD_FOLDER:
+ return this.canChangeList_();
+ case Command.IMPORT:
+ return this.globalCanEdit_;
+ case Command.PASTE:
+ return this.canPaste_;
+ default:
+ return true;
+ }
+ },
- /**
- * Determine if the |command| can be executed with the given |itemIds|.
- * Commands which appear in the context menu should be implemented
- * separately using `isCommandVisible_` and `isCommandEnabled_`.
- * @param {Command} command
- * @param {!Set<string>} itemIds
- * @return {boolean}
- */
- canExecute: function(command, itemIds) {
- const state = this.getState();
- switch (command) {
- case Command.OPEN:
- return itemIds.size > 0;
- case Command.UNDO:
- case Command.REDO:
- return this.globalCanEdit_;
- case Command.SELECT_ALL:
- case Command.DESELECT_ALL:
- return true;
- case Command.COPY:
- return itemIds.size > 0;
- case Command.CUT:
- return itemIds.size > 0 &&
- !this.containsMatchingNode_(itemIds, function(node) {
- return !canEditNode(state, node.id);
- });
- case Command.PASTE:
- return state.search.term == '' &&
- canReorderChildren(state, state.selectedFolder);
- default:
- return this.isCommandVisible_(command, itemIds) &&
- this.isCommandEnabled_(command, itemIds);
+ /**
+ * Returns whether the currently displayed bookmarks list can be changed.
+ * @private
+ * @return {boolean}
+ */
+ canChangeList_: function() {
+ const state = this.getState();
+ return state.search.term == '' &&
+ canReorderChildren(state, state.selectedFolder);
+ },
+
+ /**
+ * @param {Command} command
+ * @param {!Set<string>} itemIds
+ */
+ handle: function(command, itemIds) {
+ const state = this.getState();
+ switch (command) {
+ case Command.EDIT: {
+ const id = Array.from(itemIds)[0];
+ /** @type {!BookmarksEditDialogElement} */ (this.$.editDialog.get())
+ .showEditDialog(state.nodes[id]);
+ break;
}
- },
-
- /**
- * @param {Command} command
- * @param {!Set<string>} itemIds
- * @return {boolean} True if the command should be visible in the context
- * menu.
- */
- isCommandVisible_: function(command, itemIds) {
- switch (command) {
- case Command.EDIT:
- return itemIds.size == 1 && this.globalCanEdit_;
- case Command.PASTE:
- return this.globalCanEdit_;
- case Command.CUT:
- case Command.COPY:
- return itemIds.size >= 1 && this.globalCanEdit_;
- case Command.COPY_URL:
- return this.isSingleBookmark_(itemIds);
- case Command.DELETE:
- return itemIds.size > 0 && this.globalCanEdit_;
- case Command.SHOW_IN_FOLDER:
- return this.menuSource_ == MenuSource.ITEM && itemIds.size == 1 &&
- this.getState().search.term != '' &&
- !this.containsMatchingNode_(itemIds, function(node) {
- return !node.parentId || node.parentId == ROOT_NODE_ID;
- });
- case Command.OPEN_NEW_TAB:
- case Command.OPEN_NEW_WINDOW:
- case Command.OPEN_INCOGNITO:
- return itemIds.size > 0;
- case Command.ADD_BOOKMARK:
- case Command.ADD_FOLDER:
- case Command.SORT:
- case Command.EXPORT:
- case Command.IMPORT:
- case Command.HELP_CENTER:
- return true;
- }
- return assert(false);
- },
-
- /**
- * @param {Command} command
- * @param {!Set<string>} itemIds
- * @return {boolean} True if the command should be clickable in the context
- * menu.
- */
- isCommandEnabled_: function(command, itemIds) {
- const state = this.getState();
- switch (command) {
- case Command.EDIT:
- case Command.DELETE:
- return !this.containsMatchingNode_(itemIds, function(node) {
- return !canEditNode(state, node.id);
- });
- case Command.OPEN_NEW_TAB:
- case Command.OPEN_NEW_WINDOW:
- return this.expandUrls_(itemIds).length > 0;
- case Command.OPEN_INCOGNITO:
- return this.expandUrls_(itemIds).length > 0 &&
- state.prefs.incognitoAvailability !=
- IncognitoAvailability.DISABLED;
- case Command.SORT:
- return this.canChangeList_() &&
- state.nodes[state.selectedFolder].children.length > 1;
- case Command.ADD_BOOKMARK:
- case Command.ADD_FOLDER:
- return this.canChangeList_();
- case Command.IMPORT:
- return this.globalCanEdit_;
- case Command.PASTE:
- return this.canPaste_;
- default:
- return true;
- }
- },
-
- /**
- * Returns whether the currently displayed bookmarks list can be changed.
- * @private
- * @return {boolean}
- */
- canChangeList_: function() {
- const state = this.getState();
- return state.search.term == '' &&
- canReorderChildren(state, state.selectedFolder);
- },
-
- /**
- * @param {Command} command
- * @param {!Set<string>} itemIds
- */
- handle: function(command, itemIds) {
- const state = this.getState();
- switch (command) {
- case Command.EDIT: {
- const id = Array.from(itemIds)[0];
- /** @type {!BookmarksEditDialogElement} */ (this.$.editDialog.get())
- .showEditDialog(state.nodes[id]);
- break;
- }
- case Command.COPY_URL:
- case Command.COPY: {
- const idList = Array.from(itemIds);
- chrome.bookmarkManagerPrivate.copy(idList, () => {
- let labelPromise;
- if (command == Command.COPY_URL) {
- labelPromise =
- Promise.resolve(loadTimeData.getString('toastUrlCopied'));
- } else if (idList.length == 1) {
- labelPromise =
- Promise.resolve(loadTimeData.getString('toastItemCopied'));
- } else {
- labelPromise = this.browserProxy_.getPluralString(
- 'toastItemsCopied', idList.length);
- }
-
- this.showTitleToast_(
- labelPromise, state.nodes[idList[0]].title, false);
- });
- break;
- }
- case Command.SHOW_IN_FOLDER: {
- const id = Array.from(itemIds)[0];
- this.dispatch(selectFolder(
- assert(state.nodes[id].parentId), state.nodes));
- DialogFocusManager.getInstance().clearFocus();
- this.fire('highlight-items', [id]);
- break;
- }
- case Command.DELETE: {
- const idList = Array.from(this.minimizeDeletionSet_(itemIds));
- const title = state.nodes[idList[0]].title;
+ case Command.COPY_URL:
+ case Command.COPY: {
+ const idList = Array.from(itemIds);
+ chrome.bookmarkManagerPrivate.copy(idList, () => {
let labelPromise;
-
- if (idList.length == 1) {
+ if (command == Command.COPY_URL) {
labelPromise =
- Promise.resolve(loadTimeData.getString('toastItemDeleted'));
+ Promise.resolve(loadTimeData.getString('toastUrlCopied'));
+ } else if (idList.length == 1) {
+ labelPromise =
+ Promise.resolve(loadTimeData.getString('toastItemCopied'));
} else {
labelPromise = this.browserProxy_.getPluralString(
- 'toastItemsDeleted', idList.length);
+ 'toastItemsCopied', idList.length);
}
- chrome.bookmarkManagerPrivate.removeTrees(idList, () => {
- this.showTitleToast_(labelPromise, title, true);
- });
- break;
- }
- case Command.UNDO:
- chrome.bookmarkManagerPrivate.undo();
- getInstance().hide();
- break;
- case Command.REDO:
- chrome.bookmarkManagerPrivate.redo();
- break;
- case Command.OPEN_NEW_TAB:
- case Command.OPEN_NEW_WINDOW:
- case Command.OPEN_INCOGNITO:
- this.openUrls_(this.expandUrls_(itemIds), command);
- break;
- case Command.OPEN:
- if (this.isFolder_(itemIds)) {
- const folderId = Array.from(itemIds)[0];
- this.dispatch(
- selectFolder(folderId, state.nodes));
- } else {
- this.openUrls_(this.expandUrls_(itemIds), command);
- }
- break;
- case Command.SELECT_ALL:
- const displayedIds = getDisplayedList(state);
- this.dispatch(selectAll(displayedIds, state));
- break;
- case Command.DESELECT_ALL:
- this.dispatch(deselectItems());
- break;
- case Command.CUT:
- chrome.bookmarkManagerPrivate.cut(Array.from(itemIds));
- break;
- case Command.PASTE:
- const selectedFolder = state.selectedFolder;
- const selectedItems = state.selection.items;
- trackUpdatedItems();
- chrome.bookmarkManagerPrivate.paste(
- selectedFolder, Array.from(selectedItems),
- highlightUpdatedItems);
- break;
- case Command.SORT:
- chrome.bookmarkManagerPrivate.sortChildren(
- assert(state.selectedFolder));
- getInstance().show(
- loadTimeData.getString('toastFolderSorted'), true);
- break;
- case Command.ADD_BOOKMARK:
- /** @type {!BookmarksEditDialogElement} */ (this.$.editDialog.get())
- .showAddDialog(false, assert(state.selectedFolder));
- break;
- case Command.ADD_FOLDER:
- /** @type {!BookmarksEditDialogElement} */ (this.$.editDialog.get())
- .showAddDialog(true, assert(state.selectedFolder));
- break;
- case Command.IMPORT:
- chrome.bookmarks.import();
- break;
- case Command.EXPORT:
- chrome.bookmarks.export();
- break;
- case Command.HELP_CENTER:
- window.open('https://support.google.com/chrome/?p=bookmarks');
- break;
- default:
- assert(false);
- }
- this.recordCommandHistogram_(
- itemIds, 'BookmarkManager.CommandExecuted', command);
- },
-
- /**
- * @param {!Event} e
- * @param {!Set<string>} itemIds
- * @return {boolean} True if the event was handled, triggering a keyboard
- * shortcut.
- */
- handleKeyEvent: function(e, itemIds) {
- for (const commandTuple of this.shortcuts_) {
- const command = /** @type {Command} */ (commandTuple[0]);
- const shortcut =
- /** @type {KeyboardShortcutList} */ (commandTuple[1]);
- if (shortcut.matchesEvent(e) && this.canExecute(command, itemIds)) {
- this.handle(command, itemIds);
-
- this.recordCommandHistogram_(
- itemIds, 'BookmarkManager.CommandExecutedFromKeyboard', command);
- e.stopPropagation();
- e.preventDefault();
- return true;
- }
- }
-
- return false;
- },
-
- ////////////////////////////////////////////////////////////////////////////
- // Private functions:
-
- /**
- * Register a keyboard shortcut for a command.
- * @param {Command} command Command that the shortcut will trigger.
- * @param {string} shortcut Keyboard shortcut, using the syntax of
- * cr/ui/command.js.
- * @param {string=} macShortcut If set, enables a replacement shortcut for
- * Mac.
- */
- addShortcut_: function(command, shortcut, macShortcut) {
- shortcut = (isMac && macShortcut) ? macShortcut : shortcut;
- this.shortcuts_.set(command, new KeyboardShortcutList(shortcut));
- },
-
- /**
- * Minimize the set of |itemIds| by removing any node which has an ancestor
- * node already in the set. This ensures that instead of trying to delete
- * both a node and its descendant, we will only try to delete the topmost
- * node, preventing an error in the bookmarkManagerPrivate.removeTrees API
- * call.
- * @param {!Set<string>} itemIds
- * @return {!Set<string>}
- */
- minimizeDeletionSet_: function(itemIds) {
- const minimizedSet = new Set();
- const nodes = this.getState().nodes;
- itemIds.forEach(function(itemId) {
- let currentId = itemId;
- while (currentId != ROOT_NODE_ID) {
- currentId = assert(nodes[currentId].parentId);
- if (itemIds.has(currentId)) {
- return;
- }
- }
- minimizedSet.add(itemId);
- });
- return minimizedSet;
- },
-
- /**
- * Open the given |urls| in response to a |command|. May show a confirmation
- * dialog before opening large numbers of URLs.
- * @param {!Array<string>} urls
- * @param {Command} command
- * @private
- */
- openUrls_: function(urls, command) {
- assert(
- command == Command.OPEN || command == Command.OPEN_NEW_TAB ||
- command == Command.OPEN_NEW_WINDOW ||
- command == Command.OPEN_INCOGNITO);
-
- if (urls.length == 0) {
- return;
- }
-
- const openUrlsCallback = function() {
- const incognito = command == Command.OPEN_INCOGNITO;
- if (command == Command.OPEN_NEW_WINDOW || incognito) {
- chrome.windows.create({url: urls, incognito: incognito});
- } else {
- if (command == Command.OPEN) {
- chrome.tabs.create({url: urls.shift(), active: true});
- }
- urls.forEach(function(url) {
- chrome.tabs.create({url: url, active: false});
- });
- }
- };
-
- if (urls.length <= OPEN_CONFIRMATION_LIMIT) {
- openUrlsCallback();
- return;
- }
-
- this.confirmOpenCallback_ = openUrlsCallback;
- const dialog = this.$.openDialog.get();
- dialog.querySelector('[slot=body]').textContent =
- loadTimeData.getStringF('openDialogBody', urls.length);
-
- DialogFocusManager.getInstance().showDialog(
- this.$.openDialog.get());
- },
-
- /**
- * Returns all URLs in the given set of nodes and their immediate children.
- * Note that these will be ordered by insertion order into the |itemIds|
- * set, and that it is possible to duplicate a URL by passing in both the
- * parent ID and child ID.
- * @param {!Set<string>} itemIds
- * @return {!Array<string>}
- * @private
- */
- expandUrls_: function(itemIds) {
- const urls = [];
- const nodes = this.getState().nodes;
-
- itemIds.forEach(function(id) {
- const node = nodes[id];
- if (node.url) {
- urls.push(node.url);
- } else {
- node.children.forEach(function(childId) {
- const childNode = nodes[childId];
- if (childNode.url) {
- urls.push(childNode.url);
- }
- });
- }
- });
-
- return urls;
- },
-
- /**
- * @param {!Set<string>} itemIds
- * @param {function(BookmarkNode):boolean} predicate
- * @return {boolean} True if any node in |itemIds| returns true for
- * |predicate|.
- */
- containsMatchingNode_: function(itemIds, predicate) {
- const nodes = this.getState().nodes;
-
- return Array.from(itemIds).some(function(id) {
- return predicate(nodes[id]);
- });
- },
-
- /**
- * @param {!Set<string>} itemIds
- * @return {boolean} True if |itemIds| is a single bookmark (non-folder)
- * node.
- * @private
- */
- isSingleBookmark_: function(itemIds) {
- return itemIds.size == 1 &&
- this.containsMatchingNode_(itemIds, function(node) {
- return !!node.url;
- });
- },
-
- /**
- * @param {!Set<string>} itemIds
- * @return {boolean}
- * @private
- */
- isFolder_: function(itemIds) {
- return itemIds.size == 1 &&
- this.containsMatchingNode_(itemIds, node => !node.url);
- },
-
- /**
- * @param {Command} command
- * @return {string}
- * @private
- */
- getCommandLabel_: function(command) {
- const multipleNodes = this.menuIds_.size > 1 ||
- this.containsMatchingNode_(this.menuIds_, function(node) {
- return !node.url;
- });
- let label;
- switch (command) {
- case Command.EDIT:
- if (this.menuIds_.size != 1) {
- return '';
- }
-
- const id = Array.from(this.menuIds_)[0];
- const itemUrl = this.getState().nodes[id].url;
- label = itemUrl ? 'menuEdit' : 'menuRename';
- break;
- case Command.CUT:
- label = 'menuCut';
- break;
- case Command.COPY:
- label = 'menuCopy';
- break;
- case Command.COPY_URL:
- label = 'menuCopyURL';
- break;
- case Command.PASTE:
- label = 'menuPaste';
- break;
- case Command.DELETE:
- label = 'menuDelete';
- break;
- case Command.SHOW_IN_FOLDER:
- label = 'menuShowInFolder';
- break;
- case Command.OPEN_NEW_TAB:
- label = multipleNodes ? 'menuOpenAllNewTab' : 'menuOpenNewTab';
- break;
- case Command.OPEN_NEW_WINDOW:
- label = multipleNodes ? 'menuOpenAllNewWindow' : 'menuOpenNewWindow';
- break;
- case Command.OPEN_INCOGNITO:
- label = multipleNodes ? 'menuOpenAllIncognito' : 'menuOpenIncognito';
- break;
- case Command.SORT:
- label = 'menuSort';
- break;
- case Command.ADD_BOOKMARK:
- label = 'menuAddBookmark';
- break;
- case Command.ADD_FOLDER:
- label = 'menuAddFolder';
- break;
- case Command.IMPORT:
- label = 'menuImport';
- break;
- case Command.EXPORT:
- label = 'menuExport';
- break;
- case Command.HELP_CENTER:
- label = 'menuHelpCenter';
- break;
- }
- assert(label);
-
- return loadTimeData.getString(assert(label));
- },
-
- /**
- * @param {Command} command
- * @return {string}
- * @private
- */
- getCommandSublabel_: function(command) {
- const multipleNodes = this.menuIds_.size > 1 ||
- this.containsMatchingNode_(this.menuIds_, function(node) {
- return !node.url;
- });
- switch (command) {
- case Command.OPEN_NEW_TAB:
- const urls = this.expandUrls_(this.menuIds_);
- return multipleNodes && urls.length > 0 ? String(urls.length) : '';
- default:
- return '';
- }
- },
-
- /** @private */
- computeMenuCommands_: function() {
- switch (this.menuSource_) {
- case MenuSource.ITEM:
- case MenuSource.TREE:
- return [
- Command.EDIT,
- Command.SHOW_IN_FOLDER,
- Command.DELETE,
- // <hr>
- Command.CUT,
- Command.COPY,
- Command.COPY_URL,
- Command.PASTE,
- // <hr>
- Command.OPEN_NEW_TAB,
- Command.OPEN_NEW_WINDOW,
- Command.OPEN_INCOGNITO,
- ];
- case MenuSource.TOOLBAR:
- return [
- Command.SORT,
- // <hr>
- Command.ADD_BOOKMARK,
- Command.ADD_FOLDER,
- // <hr>
- Command.IMPORT,
- Command.EXPORT,
- // <hr>
- Command.HELP_CENTER,
- ];
- case MenuSource.LIST:
- return [
- Command.ADD_BOOKMARK,
- Command.ADD_FOLDER,
- ];
- case MenuSource.NONE:
- return [];
- }
- assert(false);
- },
-
- /**
- * @return {boolean}
- * @private
- */
- computeHasAnySublabel_: function() {
- if (this.menuIds_ == undefined || this.menuCommands_ == undefined) {
- return false;
- }
-
- return this.menuCommands_.some(
- (command) => this.getCommandSublabel_(command) != '');
- },
-
- /**
- * @param {Command} command
- * @param {!Set<string>} itemIds
- * @return {boolean}
- * @private
- */
- showDividerAfter_: function(command, itemIds) {
- switch (command) {
- case Command.SORT:
- case Command.ADD_FOLDER:
- case Command.EXPORT:
- return this.menuSource_ == MenuSource.TOOLBAR;
- case Command.DELETE:
- return this.globalCanEdit_;
- case Command.PASTE:
- return this.globalCanEdit_ || this.isSingleBookmark_(itemIds);
- }
- return false;
- },
-
- /**
- * @param {!Set<string>} itemIds
- * @param {string} histogram
- * @param {number} command
- * @private
- */
- recordCommandHistogram_: function(itemIds, histogram, command) {
- if (command == Command.OPEN) {
- command = this.isFolder_(itemIds) ? Command.OPEN_FOLDER :
- Command.OPEN_BOOKMARK;
- }
-
- this.browserProxy_.recordInHistogram(
- histogram, command, Command.MAX_VALUE);
- },
-
- /**
- * Show a toast with a bookmark |title| inserted into a label, with the
- * title ellipsised if necessary.
- * @param {!Promise<string>} labelPromise Promise which resolves with the
- * label for the toast.
- * @param {string} title Bookmark title to insert.
- * @param {boolean} canUndo If true, shows an undo button in the toast.
- * @private
- */
- showTitleToast_: async function(labelPromise, title, canUndo) {
- const label = await labelPromise;
- const pieces = loadTimeData.getSubstitutedStringPieces(label, title)
- .map(function(p) {
- // Make the bookmark name collapsible.
- p.collapsible = !!p.arg;
- return p;
- });
-
- getInstance().showForStringPieces(pieces, canUndo);
- },
-
- /**
- * @param {number} targetId
- * @private
- */
- updateCanPaste_: function(targetId) {
- return new Promise(resolve => {
- chrome.bookmarkManagerPrivate.canPaste(`${targetId}`, result => {
- this.canPaste_ = result;
- resolve();
+ this.showTitleToast_(
+ labelPromise, state.nodes[idList[0]].title, false);
});
- });
- },
+ break;
+ }
+ case Command.SHOW_IN_FOLDER: {
+ const id = Array.from(itemIds)[0];
+ this.dispatch(
+ selectFolder(assert(state.nodes[id].parentId), state.nodes));
+ DialogFocusManager.getInstance().clearFocus();
+ this.fire('highlight-items', [id]);
+ break;
+ }
+ case Command.DELETE: {
+ const idList = Array.from(this.minimizeDeletionSet_(itemIds));
+ const title = state.nodes[idList[0]].title;
+ let labelPromise;
- ////////////////////////////////////////////////////////////////////////////
- // Event handlers:
+ if (idList.length == 1) {
+ labelPromise =
+ Promise.resolve(loadTimeData.getString('toastItemDeleted'));
+ } else {
+ labelPromise = this.browserProxy_.getPluralString(
+ 'toastItemsDeleted', idList.length);
+ }
- /**
- * @param {Event} e
- * @private
- */
- onOpenCommandMenu_: async function(e) {
- await this.updateCanPaste_(e.detail.source);
- if (e.detail.targetElement) {
- this.openCommandMenuAtElement(e.detail.targetElement, e.detail.source);
+ chrome.bookmarkManagerPrivate.removeTrees(idList, () => {
+ this.showTitleToast_(labelPromise, title, true);
+ });
+ break;
+ }
+ case Command.UNDO:
+ chrome.bookmarkManagerPrivate.undo();
+ getInstance().hide();
+ break;
+ case Command.REDO:
+ chrome.bookmarkManagerPrivate.redo();
+ break;
+ case Command.OPEN_NEW_TAB:
+ case Command.OPEN_NEW_WINDOW:
+ case Command.OPEN_INCOGNITO:
+ this.openUrls_(this.expandUrls_(itemIds), command);
+ break;
+ case Command.OPEN:
+ if (this.isFolder_(itemIds)) {
+ const folderId = Array.from(itemIds)[0];
+ this.dispatch(selectFolder(folderId, state.nodes));
+ } else {
+ this.openUrls_(this.expandUrls_(itemIds), command);
+ }
+ break;
+ case Command.SELECT_ALL:
+ const displayedIds = getDisplayedList(state);
+ this.dispatch(selectAll(displayedIds, state));
+ break;
+ case Command.DESELECT_ALL:
+ this.dispatch(deselectItems());
+ break;
+ case Command.CUT:
+ chrome.bookmarkManagerPrivate.cut(Array.from(itemIds));
+ break;
+ case Command.PASTE:
+ const selectedFolder = state.selectedFolder;
+ const selectedItems = state.selection.items;
+ trackUpdatedItems();
+ chrome.bookmarkManagerPrivate.paste(
+ selectedFolder, Array.from(selectedItems), highlightUpdatedItems);
+ break;
+ case Command.SORT:
+ chrome.bookmarkManagerPrivate.sortChildren(
+ assert(state.selectedFolder));
+ getInstance().show(loadTimeData.getString('toastFolderSorted'), true);
+ break;
+ case Command.ADD_BOOKMARK:
+ /** @type {!BookmarksEditDialogElement} */ (this.$.editDialog.get())
+ .showAddDialog(false, assert(state.selectedFolder));
+ break;
+ case Command.ADD_FOLDER:
+ /** @type {!BookmarksEditDialogElement} */ (this.$.editDialog.get())
+ .showAddDialog(true, assert(state.selectedFolder));
+ break;
+ case Command.IMPORT:
+ chrome.bookmarks.import();
+ break;
+ case Command.EXPORT:
+ chrome.bookmarks.export();
+ break;
+ case Command.HELP_CENTER:
+ window.open('https://support.google.com/chrome/?p=bookmarks');
+ break;
+ default:
+ assert(false);
+ }
+ this.recordCommandHistogram_(
+ itemIds, 'BookmarkManager.CommandExecuted', command);
+ },
+
+ /**
+ * @param {!Event} e
+ * @param {!Set<string>} itemIds
+ * @return {boolean} True if the event was handled, triggering a keyboard
+ * shortcut.
+ */
+ handleKeyEvent: function(e, itemIds) {
+ for (const commandTuple of this.shortcuts_) {
+ const command = /** @type {Command} */ (commandTuple[0]);
+ const shortcut =
+ /** @type {KeyboardShortcutList} */ (commandTuple[1]);
+ if (shortcut.matchesEvent(e) && this.canExecute(command, itemIds)) {
+ this.handle(command, itemIds);
+
+ this.recordCommandHistogram_(
+ itemIds, 'BookmarkManager.CommandExecutedFromKeyboard', command);
+ e.stopPropagation();
+ e.preventDefault();
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Private functions:
+
+ /**
+ * Register a keyboard shortcut for a command.
+ * @param {Command} command Command that the shortcut will trigger.
+ * @param {string} shortcut Keyboard shortcut, using the syntax of
+ * cr/ui/command.js.
+ * @param {string=} macShortcut If set, enables a replacement shortcut for
+ * Mac.
+ */
+ addShortcut_: function(command, shortcut, macShortcut) {
+ shortcut = (isMac && macShortcut) ? macShortcut : shortcut;
+ this.shortcuts_.set(command, new KeyboardShortcutList(shortcut));
+ },
+
+ /**
+ * Minimize the set of |itemIds| by removing any node which has an ancestor
+ * node already in the set. This ensures that instead of trying to delete
+ * both a node and its descendant, we will only try to delete the topmost
+ * node, preventing an error in the bookmarkManagerPrivate.removeTrees API
+ * call.
+ * @param {!Set<string>} itemIds
+ * @return {!Set<string>}
+ */
+ minimizeDeletionSet_: function(itemIds) {
+ const minimizedSet = new Set();
+ const nodes = this.getState().nodes;
+ itemIds.forEach(function(itemId) {
+ let currentId = itemId;
+ while (currentId != ROOT_NODE_ID) {
+ currentId = assert(nodes[currentId].parentId);
+ if (itemIds.has(currentId)) {
+ return;
+ }
+ }
+ minimizedSet.add(itemId);
+ });
+ return minimizedSet;
+ },
+
+ /**
+ * Open the given |urls| in response to a |command|. May show a confirmation
+ * dialog before opening large numbers of URLs.
+ * @param {!Array<string>} urls
+ * @param {Command} command
+ * @private
+ */
+ openUrls_: function(urls, command) {
+ assert(
+ command == Command.OPEN || command == Command.OPEN_NEW_TAB ||
+ command == Command.OPEN_NEW_WINDOW ||
+ command == Command.OPEN_INCOGNITO);
+
+ if (urls.length == 0) {
+ return;
+ }
+
+ const openUrlsCallback = function() {
+ const incognito = command == Command.OPEN_INCOGNITO;
+ if (command == Command.OPEN_NEW_WINDOW || incognito) {
+ chrome.windows.create({url: urls, incognito: incognito});
} else {
- this.openCommandMenuAtPosition(e.detail.x, e.detail.y, e.detail.source);
+ if (command == Command.OPEN) {
+ chrome.tabs.create({url: urls.shift(), active: true});
+ }
+ urls.forEach(function(url) {
+ chrome.tabs.create({url: url, active: false});
+ });
}
- this.browserProxy_.recordInHistogram(
- 'BookmarkManager.CommandMenuOpened', e.detail.source,
- MenuSource.NUM_VALUES);
- },
+ };
- /**
- * @param {Event} e
- * @private
- */
- onCommandClick_: function(e) {
- this.handle(
- /** @type {Command} */ (
- Number(e.currentTarget.getAttribute('command'))),
- assert(this.menuIds_));
- this.closeCommandMenu();
- },
+ if (urls.length <= OPEN_CONFIRMATION_LIMIT) {
+ openUrlsCallback();
+ return;
+ }
- /**
- * @param {!Event} e
- * @private
- */
- onKeydown_: function(e) {
- const path = e.composedPath();
- if (path[0].tagName == 'INPUT') {
- return;
+ this.confirmOpenCallback_ = openUrlsCallback;
+ const dialog = this.$.openDialog.get();
+ dialog.querySelector('[slot=body]').textContent =
+ loadTimeData.getStringF('openDialogBody', urls.length);
+
+ DialogFocusManager.getInstance().showDialog(this.$.openDialog.get());
+ },
+
+ /**
+ * Returns all URLs in the given set of nodes and their immediate children.
+ * Note that these will be ordered by insertion order into the |itemIds|
+ * set, and that it is possible to duplicate a URL by passing in both the
+ * parent ID and child ID.
+ * @param {!Set<string>} itemIds
+ * @return {!Array<string>}
+ * @private
+ */
+ expandUrls_: function(itemIds) {
+ const urls = [];
+ const nodes = this.getState().nodes;
+
+ itemIds.forEach(function(id) {
+ const node = nodes[id];
+ if (node.url) {
+ urls.push(node.url);
+ } else {
+ node.children.forEach(function(childId) {
+ const childNode = nodes[childId];
+ if (childNode.url) {
+ urls.push(childNode.url);
+ }
+ });
}
- if ((e.target == document.body ||
- path.some(el => el.tagName == 'BOOKMARKS-TOOLBAR')) &&
- !DialogFocusManager.getInstance().hasOpenDialog()) {
- this.handleKeyEvent(e, this.getState().selection.items);
- }
- },
+ });
- /**
- * Close the menu on mousedown so clicks can propagate to the underlying UI.
- * This allows the user to right click the list while a context menu is
- * showing and get another context menu.
- * @param {Event} e
- * @private
- */
- onMenuMousedown_: function(e) {
- if (e.path[0].tagName != 'DIALOG') {
- return;
- }
+ return urls;
+ },
- this.closeCommandMenu();
- },
+ /**
+ * @param {!Set<string>} itemIds
+ * @param {function(BookmarkNode):boolean} predicate
+ * @return {boolean} True if any node in |itemIds| returns true for
+ * |predicate|.
+ */
+ containsMatchingNode_: function(itemIds, predicate) {
+ const nodes = this.getState().nodes;
- /** @private */
- onOpenCancelTap_: function() {
- this.$.openDialog.get().cancel();
- },
+ return Array.from(itemIds).some(function(id) {
+ return predicate(nodes[id]);
+ });
+ },
- /** @private */
- onOpenConfirmTap_: function() {
- this.confirmOpenCallback_();
- this.$.openDialog.get().close();
- },
- });
+ /**
+ * @param {!Set<string>} itemIds
+ * @return {boolean} True if |itemIds| is a single bookmark (non-folder)
+ * node.
+ * @private
+ */
+ isSingleBookmark_: function(itemIds) {
+ return itemIds.size == 1 &&
+ this.containsMatchingNode_(itemIds, function(node) {
+ return !!node.url;
+ });
+ },
- /** @private {CommandManager} */
- CommandManager.instance_ = null;
+ /**
+ * @param {!Set<string>} itemIds
+ * @return {boolean}
+ * @private
+ */
+ isFolder_: function(itemIds) {
+ return itemIds.size == 1 &&
+ this.containsMatchingNode_(itemIds, node => !node.url);
+ },
- /** @return {!CommandManager} */
- CommandManager.getInstance = function() {
- return assert(CommandManager.instance_);
- };
+ /**
+ * @param {Command} command
+ * @return {string}
+ * @private
+ */
+ getCommandLabel_: function(command) {
+ const multipleNodes = this.menuIds_.size > 1 ||
+ this.containsMatchingNode_(this.menuIds_, function(node) {
+ return !node.url;
+ });
+ let label;
+ switch (command) {
+ case Command.EDIT:
+ if (this.menuIds_.size != 1) {
+ return '';
+ }
+ const id = Array.from(this.menuIds_)[0];
+ const itemUrl = this.getState().nodes[id].url;
+ label = itemUrl ? 'menuEdit' : 'menuRename';
+ break;
+ case Command.CUT:
+ label = 'menuCut';
+ break;
+ case Command.COPY:
+ label = 'menuCopy';
+ break;
+ case Command.COPY_URL:
+ label = 'menuCopyURL';
+ break;
+ case Command.PASTE:
+ label = 'menuPaste';
+ break;
+ case Command.DELETE:
+ label = 'menuDelete';
+ break;
+ case Command.SHOW_IN_FOLDER:
+ label = 'menuShowInFolder';
+ break;
+ case Command.OPEN_NEW_TAB:
+ label = multipleNodes ? 'menuOpenAllNewTab' : 'menuOpenNewTab';
+ break;
+ case Command.OPEN_NEW_WINDOW:
+ label = multipleNodes ? 'menuOpenAllNewWindow' : 'menuOpenNewWindow';
+ break;
+ case Command.OPEN_INCOGNITO:
+ label = multipleNodes ? 'menuOpenAllIncognito' : 'menuOpenIncognito';
+ break;
+ case Command.SORT:
+ label = 'menuSort';
+ break;
+ case Command.ADD_BOOKMARK:
+ label = 'menuAddBookmark';
+ break;
+ case Command.ADD_FOLDER:
+ label = 'menuAddFolder';
+ break;
+ case Command.IMPORT:
+ label = 'menuImport';
+ break;
+ case Command.EXPORT:
+ label = 'menuExport';
+ break;
+ case Command.HELP_CENTER:
+ label = 'menuHelpCenter';
+ break;
+ }
+ assert(label);
+
+ return loadTimeData.getString(assert(label));
+ },
+
+ /**
+ * @param {Command} command
+ * @return {string}
+ * @private
+ */
+ getCommandSublabel_: function(command) {
+ const multipleNodes = this.menuIds_.size > 1 ||
+ this.containsMatchingNode_(this.menuIds_, function(node) {
+ return !node.url;
+ });
+ switch (command) {
+ case Command.OPEN_NEW_TAB:
+ const urls = this.expandUrls_(this.menuIds_);
+ return multipleNodes && urls.length > 0 ? String(urls.length) : '';
+ default:
+ return '';
+ }
+ },
+
+ /** @private */
+ computeMenuCommands_: function() {
+ switch (this.menuSource_) {
+ case MenuSource.ITEM:
+ case MenuSource.TREE:
+ return [
+ Command.EDIT,
+ Command.SHOW_IN_FOLDER,
+ Command.DELETE,
+ // <hr>
+ Command.CUT,
+ Command.COPY,
+ Command.COPY_URL,
+ Command.PASTE,
+ // <hr>
+ Command.OPEN_NEW_TAB,
+ Command.OPEN_NEW_WINDOW,
+ Command.OPEN_INCOGNITO,
+ ];
+ case MenuSource.TOOLBAR:
+ return [
+ Command.SORT,
+ // <hr>
+ Command.ADD_BOOKMARK,
+ Command.ADD_FOLDER,
+ // <hr>
+ Command.IMPORT,
+ Command.EXPORT,
+ // <hr>
+ Command.HELP_CENTER,
+ ];
+ case MenuSource.LIST:
+ return [
+ Command.ADD_BOOKMARK,
+ Command.ADD_FOLDER,
+ ];
+ case MenuSource.NONE:
+ return [];
+ }
+ assert(false);
+ },
+
+ /**
+ * @return {boolean}
+ * @private
+ */
+ computeHasAnySublabel_: function() {
+ if (this.menuIds_ == undefined || this.menuCommands_ == undefined) {
+ return false;
+ }
+
+ return this.menuCommands_.some(
+ (command) => this.getCommandSublabel_(command) != '');
+ },
+
+ /**
+ * @param {Command} command
+ * @param {!Set<string>} itemIds
+ * @return {boolean}
+ * @private
+ */
+ showDividerAfter_: function(command, itemIds) {
+ switch (command) {
+ case Command.SORT:
+ case Command.ADD_FOLDER:
+ case Command.EXPORT:
+ return this.menuSource_ == MenuSource.TOOLBAR;
+ case Command.DELETE:
+ return this.globalCanEdit_;
+ case Command.PASTE:
+ return this.globalCanEdit_ || this.isSingleBookmark_(itemIds);
+ }
+ return false;
+ },
+
+ /**
+ * @param {!Set<string>} itemIds
+ * @param {string} histogram
+ * @param {number} command
+ * @private
+ */
+ recordCommandHistogram_: function(itemIds, histogram, command) {
+ if (command == Command.OPEN) {
+ command =
+ this.isFolder_(itemIds) ? Command.OPEN_FOLDER : Command.OPEN_BOOKMARK;
+ }
+
+ this.browserProxy_.recordInHistogram(histogram, command, Command.MAX_VALUE);
+ },
+
+ /**
+ * Show a toast with a bookmark |title| inserted into a label, with the
+ * title ellipsised if necessary.
+ * @param {!Promise<string>} labelPromise Promise which resolves with the
+ * label for the toast.
+ * @param {string} title Bookmark title to insert.
+ * @param {boolean} canUndo If true, shows an undo button in the toast.
+ * @private
+ */
+ showTitleToast_: async function(labelPromise, title, canUndo) {
+ const label = await labelPromise;
+ const pieces =
+ loadTimeData.getSubstitutedStringPieces(label, title).map(function(p) {
+ // Make the bookmark name collapsible.
+ p.collapsible = !!p.arg;
+ return p;
+ });
+
+ getInstance().showForStringPieces(pieces, canUndo);
+ },
+
+ /**
+ * @param {number} targetId
+ * @private
+ */
+ updateCanPaste_: function(targetId) {
+ return new Promise(resolve => {
+ chrome.bookmarkManagerPrivate.canPaste(`${targetId}`, result => {
+ this.canPaste_ = result;
+ resolve();
+ });
+ });
+ },
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Event handlers:
+
+ /**
+ * @param {Event} e
+ * @private
+ */
+ onOpenCommandMenu_: async function(e) {
+ await this.updateCanPaste_(e.detail.source);
+ if (e.detail.targetElement) {
+ this.openCommandMenuAtElement(e.detail.targetElement, e.detail.source);
+ } else {
+ this.openCommandMenuAtPosition(e.detail.x, e.detail.y, e.detail.source);
+ }
+ this.browserProxy_.recordInHistogram(
+ 'BookmarkManager.CommandMenuOpened', e.detail.source,
+ MenuSource.NUM_VALUES);
+ },
+
+ /**
+ * @param {Event} e
+ * @private
+ */
+ onCommandClick_: function(e) {
+ this.handle(
+ /** @type {Command} */ (
+ Number(e.currentTarget.getAttribute('command'))),
+ assert(this.menuIds_));
+ this.closeCommandMenu();
+ },
+
+ /**
+ * @param {!Event} e
+ * @private
+ */
+ onKeydown_: function(e) {
+ const path = e.composedPath();
+ if (path[0].tagName == 'INPUT') {
+ return;
+ }
+ if ((e.target == document.body ||
+ path.some(el => el.tagName == 'BOOKMARKS-TOOLBAR')) &&
+ !DialogFocusManager.getInstance().hasOpenDialog()) {
+ this.handleKeyEvent(e, this.getState().selection.items);
+ }
+ },
+
+ /**
+ * Close the menu on mousedown so clicks can propagate to the underlying UI.
+ * This allows the user to right click the list while a context menu is
+ * showing and get another context menu.
+ * @param {Event} e
+ * @private
+ */
+ onMenuMousedown_: function(e) {
+ if (e.path[0].tagName != 'DIALOG') {
+ return;
+ }
+
+ this.closeCommandMenu();
+ },
+
+ /** @private */
+ onOpenCancelTap_: function() {
+ this.$.openDialog.get().cancel();
+ },
+
+ /** @private */
+ onOpenConfirmTap_: function() {
+ this.confirmOpenCallback_();
+ this.$.openDialog.get().close();
+ },
+});
+
+/** @private {CommandManager} */
+CommandManager.instance_ = null;
+
+/** @return {!CommandManager} */
+CommandManager.getInstance = function() {
+ return assert(CommandManager.instance_);
+};