Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/renderer_context_menu/open_with_menu_factory.h" | |
| 6 | |
| 7 #include <memory> | |
| 8 #include <unordered_map> | |
| 9 #include <utility> | |
| 10 #include <vector> | |
| 11 | |
| 12 #include "ash/renderer_context_menu/link_handler_model.h" | |
| 13 #include "ash/renderer_context_menu/open_with_menu_controller.h" | |
| 14 #include "ash/shell.h" | |
| 15 #include "base/strings/string_util.h" | |
| 16 #include "base/strings/utf_string_conversions.h" | |
| 17 #include "chrome/app/chrome_command_ids.h" | |
| 18 #include "chrome/grit/generated_resources.h" | |
| 19 #include "components/renderer_context_menu/render_view_context_menu_observer.h" | |
| 20 #include "components/renderer_context_menu/render_view_context_menu_proxy.h" | |
| 21 #include "content/public/common/context_menu_params.h" | |
| 22 #include "ui/base/l10n/l10n_util.h" | |
| 23 #include "ui/base/models/simple_menu_model.h" | |
| 24 | |
| 25 namespace { | |
| 26 | |
| 27 using HandlerMap = std::unordered_map<int, ash::LinkHandlerInfo>; | |
| 28 | |
| 29 // An observer class which populates the "Open with <app>" menu items either | |
| 30 // synchronously or asynchronously. | |
| 31 class OpenWithMenuObserver : public RenderViewContextMenuObserver, | |
| 32 public ash::LinkHandlerModel::Observer { | |
| 33 public: | |
| 34 class SubMenuDelegate : public ui::SimpleMenuModel::Delegate { | |
| 35 public: | |
| 36 explicit SubMenuDelegate(OpenWithMenuObserver* parent) : parent_(parent) {} | |
| 37 ~SubMenuDelegate() override {} | |
| 38 | |
| 39 bool IsCommandIdChecked(int command_id) const override { return false; } | |
| 40 bool IsCommandIdEnabled(int command_id) const override { return true; } | |
| 41 | |
| 42 bool GetAcceleratorForCommandId(int command_id, | |
| 43 ui::Accelerator* accelerator) override { | |
| 44 return false; | |
| 45 } | |
| 46 | |
| 47 void ExecuteCommand(int command_id, int event_flags) override { | |
| 48 parent_->ExecuteCommand(command_id); | |
| 49 } | |
| 50 | |
| 51 private: | |
| 52 OpenWithMenuObserver* const parent_; | |
| 53 | |
| 54 DISALLOW_COPY_AND_ASSIGN(SubMenuDelegate); | |
| 55 }; | |
| 56 | |
| 57 explicit OpenWithMenuObserver(RenderViewContextMenuProxy* proxy) | |
| 58 : proxy_(proxy), submenu_delegate_(this) {} | |
| 59 | |
| 60 ~OpenWithMenuObserver() override {} | |
| 61 | |
| 62 // RenderViewContextMenuObserver overrides: | |
| 63 void InitMenu(const content::ContextMenuParams& params) override { | |
| 64 if (!ash::Shell::HasInstance()) | |
| 65 return; | |
| 66 ash::OpenWithMenuController* controller = | |
| 67 ash::Shell::GetInstance()->open_with_menu_controller(); | |
| 68 if (!controller) | |
| 69 return; | |
| 70 | |
| 71 link_url_ = params.link_url; | |
| 72 menu_model_ = controller->CreateModel(link_url_); | |
| 73 if (!menu_model_) | |
| 74 return; | |
| 75 | |
| 76 // Add placeholder items. | |
| 77 ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(&submenu_delegate_); | |
|
hidehiko
2016/04/20 04:40:18
I'd prefer
std::unique_ptr here, too. Instead, std
Yusuke Sato
2016/04/22 05:27:48
Done.
| |
| 78 for (int i = 0; i < kNumSubMenuCommands; ++i) { | |
| 79 const int command_id = | |
| 80 IDC_CONTENT_CONTEXT_OPEN_WITH1 + kNumMainMenuCommands + i; | |
| 81 submenu->AddItem(command_id, base::EmptyString16()); | |
| 82 } | |
| 83 submenu_.reset(submenu); | |
| 84 | |
| 85 for (int i = 0; i < kNumMainMenuCommands; ++i) { | |
| 86 const int command_id = IDC_CONTENT_CONTEXT_OPEN_WITH1 + i; | |
| 87 if (i == kNumMainMenuCommands - 1) { | |
|
hidehiko
2016/04/20 04:40:18
How about iterating between [i, kNumMainMenuComman
Yusuke Sato
2016/04/22 05:27:48
Done.
| |
| 88 base::string16 label = | |
| 89 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_MORE_APPS); | |
| 90 proxy_->AddSubMenu(command_id, label, submenu_.get()); | |
| 91 } else { | |
| 92 proxy_->AddMenuItem(command_id, base::EmptyString16()); | |
| 93 } | |
| 94 } | |
| 95 | |
| 96 menu_model_->AddObserver(this); | |
| 97 } | |
| 98 | |
| 99 bool IsCommandIdSupported(int command_id) override { | |
| 100 return command_id >= IDC_CONTENT_CONTEXT_OPEN_WITH1 && | |
| 101 command_id <= IDC_CONTENT_CONTEXT_OPEN_WITH_LAST; | |
|
hidehiko
2016/04/20 04:40:18
indent?
Yusuke Sato
2016/04/22 05:27:48
This is by git cl format actually..
| |
| 102 } | |
| 103 | |
| 104 bool IsCommandIdChecked(int command_id) override { return false; } | |
| 105 bool IsCommandIdEnabled(int command_id) override { return true; } | |
| 106 | |
| 107 void ExecuteCommand(int command_id) override { | |
| 108 // Note: SubmenuDelegate also calls this method with a command_id for the | |
| 109 // submenu. | |
| 110 const auto it = handlers_.find(command_id); | |
| 111 if (it == handlers_.end()) | |
| 112 return; | |
| 113 menu_model_->OpenLinkWithHandler(link_url_, it->second.id); | |
| 114 } | |
| 115 | |
| 116 void OnMenuCancel() override {} | |
| 117 | |
| 118 // ash::OpenWithItems::Delegate overrides: | |
| 119 void ModelChanged( | |
|
hidehiko
2016/04/20 18:10:18
As we discussed offline, if this is called twice o
Yusuke Sato
2016/04/22 05:27:48
Done.
| |
| 120 const std::vector<ash::LinkHandlerInfo>& handlers) override { | |
| 121 auto result = BuildHandlersMap(handlers); | |
| 122 handlers_ = std::move(result.first); | |
| 123 const int submenu_parent_id = result.second; | |
| 124 for (int command_id = IDC_CONTENT_CONTEXT_OPEN_WITH1; | |
| 125 command_id <= IDC_CONTENT_CONTEXT_OPEN_WITH_LAST; ++command_id) { | |
| 126 const auto it = handlers_.find(command_id); | |
| 127 if (command_id == submenu_parent_id) { | |
| 128 // Keep the submenu parent. | |
| 129 } else if (it == handlers_.end()) { | |
| 130 // Hide the menu. | |
| 131 proxy_->UpdateMenuItem(command_id, false, true, base::EmptyString16()); | |
| 132 } else { | |
| 133 // Update the menu with the new model. | |
| 134 const base::string16 label = | |
| 135 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_OPEN_WITH_APP, | |
| 136 base::UTF8ToUTF16(it->second.name)); | |
| 137 proxy_->UpdateMenuItem(command_id, true, false, label); | |
| 138 } | |
| 139 } | |
| 140 } | |
| 141 | |
| 142 static std::pair<HandlerMap, int> BuildHandlersMapForTesting( | |
| 143 const std::vector<ash::LinkHandlerInfo>& handlers) { | |
| 144 return BuildHandlersMap(handlers); | |
| 145 } | |
| 146 | |
| 147 private: | |
| 148 // Converts |handlers| into HandlerMap which is a map from a command ID to a | |
| 149 // LinkHandlerInfo and returns the map. Also returns a command id for the | |
| 150 // parent of the submenu. When the submenu is not needed, the function | |
| 151 // returns |kInvalidCommandId|. | |
| 152 static std::pair<HandlerMap, int> BuildHandlersMap( | |
| 153 const std::vector<ash::LinkHandlerInfo>& handlers) { | |
| 154 const int kInvalidCommandId = -1; | |
| 155 const int submenu_id_start = | |
| 156 IDC_CONTENT_CONTEXT_OPEN_WITH1 + kNumMainMenuCommands; | |
| 157 | |
| 158 HandlerMap handler_map; | |
| 159 int submenu_parent_command_id = kInvalidCommandId; | |
| 160 | |
| 161 const int num_apps = handlers.size(); | |
| 162 size_t handlers_index = 0; | |
| 163 // We use the last item in the main menu (IDC_CONTENT_CONTEXT_OPEN_WITH1 + | |
| 164 // kNumMainMenuCommands- 1) as a parent of a submenu, and others as regular | |
| 165 // menu items. | |
| 166 if (num_apps < kNumMainMenuCommands) { | |
| 167 // All apps can be shown with the regular main menu items. | |
| 168 for (int i = 0; i < num_apps; ++i) { | |
| 169 handler_map[IDC_CONTENT_CONTEXT_OPEN_WITH1 + i] = | |
| 170 handlers[handlers_index++]; | |
| 171 } | |
| 172 } else { | |
| 173 // Otherwise, use the submenu too. In this case, disable the last item of | |
| 174 // the regular main menu (hence '-2'). | |
| 175 for (int i = 0; i < kNumMainMenuCommands - 2; ++i) { | |
| 176 handler_map[IDC_CONTENT_CONTEXT_OPEN_WITH1 + i] = | |
| 177 handlers[handlers_index++]; | |
| 178 } | |
| 179 submenu_parent_command_id = | |
| 180 IDC_CONTENT_CONTEXT_OPEN_WITH1 + kNumMainMenuCommands - 1; | |
| 181 const int sub_items = | |
| 182 std::min(num_apps - (kNumMainMenuCommands - 2), kNumSubMenuCommands); | |
| 183 for (int i = 0; i < sub_items; ++i) { | |
| 184 handler_map[submenu_id_start + i] = handlers[handlers_index++]; | |
| 185 } | |
| 186 } | |
| 187 | |
| 188 return std::make_pair(std::move(handler_map), submenu_parent_command_id); | |
| 189 } | |
| 190 | |
| 191 static const int kNumMainMenuCommands; | |
| 192 static const int kNumSubMenuCommands; | |
| 193 | |
| 194 RenderViewContextMenuProxy* const proxy_; | |
| 195 SubMenuDelegate submenu_delegate_; | |
| 196 GURL link_url_; | |
| 197 | |
| 198 // A menu model received from Ash side. | |
| 199 std::unique_ptr<ash::LinkHandlerModel> menu_model_; | |
| 200 HandlerMap handlers_; | |
| 201 // A submenu passed to Chrome side. | |
| 202 std::unique_ptr<ui::MenuModel> submenu_; | |
| 203 | |
| 204 DISALLOW_COPY_AND_ASSIGN(OpenWithMenuObserver); | |
| 205 }; | |
| 206 | |
| 207 const int OpenWithMenuObserver::kNumMainMenuCommands = 4; | |
| 208 const int OpenWithMenuObserver::kNumSubMenuCommands = 10; | |
| 209 | |
| 210 } // namespace | |
| 211 | |
| 212 std::pair<HandlerMap, int> BuildHandlersMapForTesting( | |
| 213 const std::vector<ash::LinkHandlerInfo>& handlers) { | |
| 214 return OpenWithMenuObserver::BuildHandlersMapForTesting(handlers); | |
| 215 } | |
| 216 | |
| 217 RenderViewContextMenuObserver* OpenWithMenuFactory::CreateMenu( | |
| 218 RenderViewContextMenuProxy* proxy) { | |
| 219 return new OpenWithMenuObserver(proxy); | |
| 220 } | |
| OLD | NEW |