 Chromium Code Reviews
 Chromium Code Reviews Issue 2864993002:
  MD Bookmarks: Add commands for 'Open in <x>' menu items  (Closed)
    
  
    Issue 2864993002:
  MD Bookmarks: Add commands for 'Open in <x>' menu items  (Closed) 
  | Index: chrome/browser/resources/md_bookmarks/command_manager.js | 
| diff --git a/chrome/browser/resources/md_bookmarks/command_manager.js b/chrome/browser/resources/md_bookmarks/command_manager.js | 
| index a21c9c471868ec0e2bdcb6484d27bb132b9670f8..6e9565b8ae30c7e6017619bdd0a23d14df25d646 100644 | 
| --- a/chrome/browser/resources/md_bookmarks/command_manager.js | 
| +++ b/chrome/browser/resources/md_bookmarks/command_manager.js | 
| @@ -10,6 +10,22 @@ Polymer({ | 
| ], | 
| properties: { | 
| + /** @private {!Array<Command>} */ | 
| + menuCommands_: { | 
| + type: Array, | 
| + value: function() { | 
| + return [ | 
| + Command.EDIT, | 
| + Command.COPY, | 
| + Command.DELETE, | 
| + // <hr> | 
| + Command.OPEN_NEW_TAB, | 
| + Command.OPEN_NEW_WINDOW, | 
| + Command.OPEN_INCOGNITO, | 
| + ]; | 
| + }, | 
| + }, | 
| + | 
| /** @type {Set<string>} */ | 
| menuIds_: Object, | 
| }, | 
| @@ -28,6 +44,9 @@ Polymer({ | 
| this.shortcuts_[Command.EDIT] = cr.isMac ? 'enter' : 'f2'; | 
| this.shortcuts_[Command.COPY] = cr.isMac ? 'meta+c' : 'ctrl+c'; | 
| this.shortcuts_[Command.DELETE] = cr.isMac ? 'delete backspace' : 'delete'; | 
| + this.shortcuts_[Command.OPEN_NEW_TAB] = | 
| + cr.isMac ? 'meta+enter' : 'ctrl+enter'; | 
| + this.shortcuts_[Command.OPEN_NEW_WINDOW] = 'shift+enter'; | 
| }, | 
| detached: function() { | 
| @@ -65,11 +84,25 @@ Polymer({ | 
| // Command handlers: | 
| /** | 
| + * 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) { | 
| + return this.isCommandVisible_(command, itemIds) && | 
| + this.isCommandEnabled_(command, itemIds); | 
| + }, | 
| + | 
| + /** | 
| + * @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; | 
| @@ -79,12 +112,32 @@ Polymer({ | 
| return !!node.url; | 
| }); | 
| case Command.DELETE: | 
| + case Command.OPEN_NEW_TAB: | 
| + case Command.OPEN_NEW_WINDOW: | 
| + case Command.OPEN_INCOGNITO: | 
| return itemIds.size > 0; | 
| default: | 
| return 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) { | 
| + switch (command) { | 
| + case Command.OPEN_NEW_TAB: | 
| + case Command.OPEN_NEW_WINDOW: | 
| + case Command.OPEN_INCOGNITO: | 
| + return this.expandUrls_(itemIds).length > 0; | 
| + default: | 
| + return true; | 
| + } | 
| + }, | 
| + | 
| /** | 
| * @param {Command} command | 
| * @param {!Set<string>} itemIds | 
| @@ -108,6 +161,11 @@ Polymer({ | 
| // TODO(jiaxi): Add toast later. | 
| }); | 
| break; | 
| + case Command.OPEN_NEW_TAB: | 
| + case Command.OPEN_NEW_WINDOW: | 
| + case Command.OPEN_INCOGNITO: | 
| + this.openUrls_(this.expandUrls_(itemIds), command); | 
| + break; | 
| } | 
| }, | 
| @@ -137,6 +195,56 @@ Polymer({ | 
| return minimizedSet; | 
| }, | 
| + /** | 
| + * @param {!Array<string>} urls | 
| + * @param {Command} command | 
| + * @private | 
| + */ | 
| + openUrls_: function(urls, command) { | 
| + assert( | 
| + command == Command.OPEN_NEW_TAB || command == Command.OPEN_NEW_WINDOW || | 
| + command == Command.OPEN_INCOGNITO); | 
| + | 
| + if (urls.length == 0) | 
| + return; | 
| + | 
| + var incognito = command == Command.OPEN_INCOGNITO; | 
| + if (command == Command.OPEN_NEW_WINDOW || incognito) { | 
| + chrome.windows.create({url: urls, incognito: incognito}); | 
| + } else { | 
| + urls.forEach(function(url) { | 
| + chrome.tabs.create({url: url, active: false}); | 
| + }); | 
| + } | 
| + }, | 
| + | 
| + /** | 
| + * 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. | 
| 
calamity
2017/05/12 03:22:52
Mention that this assumes itemIds are all at the s
 
tsergeant
2017/05/12 05:15:27
As discussed, it doesn't assume that, but I've exp
 | 
| + * @param {!Set<string>} itemIds | 
| + * @return {!Array<string>} | 
| + * @private | 
| + */ | 
| + expandUrls_: function(itemIds) { | 
| + var urls = []; | 
| + var nodes = this.getState().nodes; | 
| + | 
| + itemIds.forEach(function(id) { | 
| + var node = nodes[id]; | 
| + if (node.url) { | 
| + urls.push(node.url); | 
| + } else { | 
| + node.children.forEach(function(childId) { | 
| + var childNode = nodes[childId]; | 
| + if (childNode.url) | 
| + urls.push(childNode.url); | 
| + }); | 
| + } | 
| + }); | 
| + | 
| + return urls; | 
| + }, | 
| + | 
| /** | 
| * @param {!Set<string>} itemIds | 
| * @param {function(BookmarkNode):boolean} predicate | 
| @@ -207,14 +315,52 @@ Polymer({ | 
| this.$.dropdown.close(); | 
| }, | 
| - /** @private */ | 
| - getEditActionLabel_: function() { | 
| - if (this.menuIds_.size > 1) | 
| - return; | 
| + /** | 
| + * @param {Command} command | 
| + * @return {string} | 
| + * @private | 
| + */ | 
| + getCommandLabel_: function(command) { | 
| + var multipleNodes = this.menuIds_.size > 1 || | 
| + this.containsMatchingNode_(this.menuIds_, function(node) { | 
| + return !node.url; | 
| + }); | 
| + var label; | 
| + switch (command) { | 
| + case Command.EDIT: | 
| + if (this.menuIds_.size > 1) | 
| + return ''; | 
| + | 
| + var id = Array.from(this.menuIds_)[0]; | 
| + var itemUrl = this.getState().nodes[id].url; | 
| + label = itemUrl ? 'menuEdit' : 'menuRename'; | 
| + break; | 
| + case Command.COPY: | 
| + label = 'menuCopyURL'; | 
| + break; | 
| + case Command.DELETE: | 
| + label = 'menuDelete'; | 
| + 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; | 
| + } | 
| + | 
| + return loadTimeData.getString(assert(label)); | 
| + }, | 
| - var id = Array.from(this.menuIds_)[0]; | 
| - var itemUrl = this.getState().nodes[id].url; | 
| - var label = itemUrl ? 'menuEdit' : 'menuRename'; | 
| - return loadTimeData.getString(label); | 
| + /** | 
| + * @param {Command} command | 
| + * @return {boolean} | 
| + * @private | 
| + */ | 
| + showDividerAfter_: function(command) { | 
| + return command == Command.DELETE; | 
| }, | 
| }); |