Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(870)

Side by Side Diff: chrome/browser/renderer_context_menu/open_with_menu_factory_ash.cc

Issue 1760773004: Add "Open with <ARC-app-name>" items to the context menu (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: address comments Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698