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/arc_app_menu_observer_chromeos.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 #include "base/strings/string_util.h" | |
| 11 #include "base/strings/utf_string_conversions.h" | |
| 12 #include "chrome/app/chrome_command_ids.h" | |
| 13 #include "chrome/browser/renderer_context_menu/render_view_context_menu.h" | |
| 14 #include "chrome/grit/generated_resources.h" | |
| 15 #include "components/arc/arc_bridge_service.h" | |
| 16 #include "content/public/common/context_menu_params.h" | |
| 17 #include "ui/base/l10n/l10n_util.h" | |
| 18 #include "ui/base/models/simple_menu_model.h" | |
| 19 | |
| 20 namespace { | |
| 21 | |
| 22 const int kMaxArcAppsInMainMenu = | |
| 23 IDC_CONTENT_CONTEXT_ARC_APP_LAST - IDC_CONTENT_CONTEXT_ARC_APP1 + 1; | |
| 24 const int kMaxArcAppsInSubMenu = | |
| 25 IDC_CONTENT_CONTEXT_ARC_SUB_APP_LAST - IDC_CONTENT_CONTEXT_ARC_SUB_APP1 + 1; | |
| 26 | |
| 27 const int kMinInstanceVersion = 2; // see intent_helper.mojom | |
| 28 | |
| 29 std::set<int> GetAllCommandIds() { | |
| 30 std::set<int> ids; | |
| 31 | |
| 32 for (int i = 0; i < kMaxArcAppsInMainMenu; ++i) { | |
| 33 ids.insert(IDC_CONTENT_CONTEXT_ARC_APP1 + i); | |
| 34 } | |
| 35 ids.insert(IDC_CONTENT_CONTEXT_ARC_SUB_MENU); | |
| 36 for (int i = 0; i < kMaxArcAppsInSubMenu; ++i) { | |
| 37 ids.insert(IDC_CONTENT_CONTEXT_ARC_SUB_APP1 + i); | |
| 38 } | |
| 39 | |
| 40 return ids; | |
| 41 } | |
| 42 | |
| 43 // Returns a set of command IDs to enable when the total number of ARC apps to | |
| 44 // show in the main and sub menus is |num_apps|. | |
| 45 std::set<int> GetCommandIdsToEnable(size_t num_apps) { | |
| 46 std::set<int> ids; | |
| 47 | |
| 48 if (num_apps <= kMaxArcAppsInMainMenu) { | |
| 49 // When |num_apps| fits in the main menu, do not enable | |
| 50 // IDC_CONTENT_CONTEXT_ARC_SUB_MENU and IDC_CONTENT_CONTEXT_ARC_SUB_APP*. | |
| 51 for (size_t i = 0; i < num_apps; ++i) { | |
| 52 ids.insert(IDC_CONTENT_CONTEXT_ARC_APP1 + i); | |
| 53 } | |
| 54 } else { | |
| 55 // When |num_apps| does not fit in the main menu, enable | |
| 56 // IDC_CONTENT_CONTEXT_ARC_SUB_MENU and some of | |
| 57 // IDC_CONTENT_CONTEXT_ARC_SUB_APP* IDs. In this case, do NOT enable | |
| 58 // IDC_CONTENT_CONTEXT_ARC_APP_LAST, hence the '-1'. | |
| 59 for (size_t i = 0; i < kMaxArcAppsInMainMenu - 1; ++i) { | |
| 60 ids.insert(IDC_CONTENT_CONTEXT_ARC_APP1 + i); | |
| 61 } | |
| 62 | |
| 63 ids.insert(IDC_CONTENT_CONTEXT_ARC_SUB_MENU); | |
| 64 size_t sub_items = std::min<size_t>(num_apps - (kMaxArcAppsInMainMenu - 1), | |
|
dcheng
2016/03/05 05:06:17
Just make these constants size_t so you don't have
Yusuke Sato
2016/03/05 20:49:51
Done.
| |
| 65 kMaxArcAppsInSubMenu); | |
| 66 for (size_t i = 0; i < sub_items; ++i) { | |
| 67 ids.insert(IDC_CONTENT_CONTEXT_ARC_SUB_APP1 + i); | |
| 68 } | |
| 69 } | |
| 70 | |
| 71 return ids; | |
| 72 } | |
| 73 | |
| 74 // The same as GetCommandIdsToEnable except that the function returns a set of | |
| 75 // IDs to disable. | |
| 76 std::set<int> GetCommandIdsToDisable(size_t num_apps, | |
| 77 const std::set<int>& all_ids) { | |
| 78 std::set<int> ids; | |
| 79 std::set<int> enabled(GetCommandIdsToEnable(num_apps)); | |
| 80 std::set_difference(all_ids.begin(), all_ids.end(), enabled.begin(), | |
| 81 enabled.end(), std::inserter(ids, ids.begin())); | |
| 82 return ids; | |
| 83 } | |
| 84 | |
| 85 int GetIndexForCommandId(int command_id) { | |
| 86 int index = -1; | |
| 87 if (command_id >= IDC_CONTENT_CONTEXT_ARC_APP1 && | |
| 88 command_id <= IDC_CONTENT_CONTEXT_ARC_APP_LAST) { | |
| 89 index = command_id - IDC_CONTENT_CONTEXT_ARC_APP1; | |
| 90 } else if (command_id >= IDC_CONTENT_CONTEXT_ARC_SUB_APP1 && | |
| 91 command_id <= IDC_CONTENT_CONTEXT_ARC_SUB_APP_LAST) { | |
| 92 const int offset = kMaxArcAppsInMainMenu - 1; | |
| 93 index = command_id - IDC_CONTENT_CONTEXT_ARC_SUB_APP1 + offset; | |
| 94 } | |
| 95 return index; | |
| 96 } | |
| 97 | |
| 98 class SubMenuDelegate : public ui::SimpleMenuModel::Delegate { | |
| 99 public: | |
| 100 explicit SubMenuDelegate(ArcAppMenuObserver* parent_menu) | |
| 101 : parent_menu_(parent_menu) {} | |
| 102 ~SubMenuDelegate() override {} | |
| 103 | |
| 104 // ui::SimpleMenuModel::Delegate overrides: | |
| 105 bool IsCommandIdChecked(int command_id) const override { return false; } | |
| 106 bool IsCommandIdEnabled(int command_id) const override { return true; } | |
| 107 bool GetAcceleratorForCommandId(int command_id, | |
| 108 ui::Accelerator* accelerator) override { | |
| 109 return false; | |
| 110 } | |
| 111 void ExecuteCommand(int command_id, int event_flags) override { | |
| 112 parent_menu_->ExecuteCommand(command_id); | |
| 113 } | |
| 114 | |
| 115 private: | |
| 116 ArcAppMenuObserver* parent_menu_; | |
| 117 | |
| 118 DISALLOW_COPY_AND_ASSIGN(SubMenuDelegate); | |
| 119 }; | |
| 120 | |
| 121 } // namespace | |
| 122 | |
| 123 ArcAppMenuObserver::ArcAppMenuObserver(RenderViewContextMenuProxy* proxy) | |
| 124 : proxy_(proxy), | |
| 125 all_command_ids_(GetAllCommandIds()), | |
| 126 sub_menu_(new SubMenuDelegate(this)), | |
| 127 loading_frame_(0), | |
| 128 weak_ptr_factory_(this) {} | |
| 129 | |
| 130 ArcAppMenuObserver::~ArcAppMenuObserver() {} | |
| 131 | |
| 132 arc::IntentHelperInstance* ArcAppMenuObserver::GetIntentHelper() { | |
| 133 arc::ArcBridgeService* bridge = arc::ArcBridgeService::Get(); | |
| 134 if (!bridge) { | |
| 135 DLOG(WARNING) << "ARC bridge is not ready."; | |
| 136 return nullptr; | |
| 137 } | |
| 138 arc::IntentHelperInstance* intent_helper_instance = | |
| 139 bridge->intent_helper_instance(); | |
| 140 if (!intent_helper_instance) { | |
| 141 DLOG(WARNING) << "ARC intent helper instance is not ready."; | |
| 142 return nullptr; | |
| 143 } | |
| 144 if (bridge->intent_helper_version() < kMinInstanceVersion) { | |
| 145 DLOG(WARNING) << "ARC intent helper instance is too old."; | |
| 146 return nullptr; | |
| 147 } | |
| 148 return intent_helper_instance; | |
| 149 } | |
| 150 | |
| 151 void ArcAppMenuObserver::InitMenu(const content::ContextMenuParams& params) { | |
| 152 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 153 | |
| 154 arc::IntentHelperInstance* intent_helper_instance = GetIntentHelper(); | |
| 155 if (!intent_helper_instance) { | |
| 156 return; | |
| 157 } | |
| 158 | |
| 159 if (params.link_url.is_empty()) { | |
| 160 return; | |
| 161 } | |
| 162 link_url_ = params.link_url; | |
| 163 | |
| 164 loading_message_ = | |
| 165 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_LOADING_ARC_APP_NAMES); | |
| 166 | |
| 167 // Add placeholder items. | |
| 168 for (int i = 0; i < kMaxArcAppsInMainMenu; ++i) { | |
| 169 proxy_->AddMenuItem(IDC_CONTENT_CONTEXT_ARC_APP1 + i, | |
| 170 loading_message_); | |
| 171 } | |
| 172 // Add a placeholder sub menu. | |
| 173 proxy_->AddSubMenu(IDC_CONTENT_CONTEXT_ARC_SUB_MENU, loading_message_, | |
| 174 &sub_menu_); | |
| 175 // Add placeholder sub items. | |
| 176 for (int i = 0; i < kMaxArcAppsInSubMenu; ++i) { | |
| 177 sub_menu_.AddItem(IDC_CONTENT_CONTEXT_ARC_SUB_APP1 + i, | |
| 178 loading_message_); | |
| 179 } | |
| 180 | |
| 181 // Check if ARC apps can handle the |link_url_|. Sicne the information is | |
| 182 // held in a different (ARC) process, issue a mojo IPC request. Usually, the | |
| 183 // callback function, OnUrlHandlerList, is called within a few milliseconds | |
| 184 // even on a slowest Chromebook we support. | |
| 185 intent_helper_instance->RequestUrlHandlerList( | |
| 186 link_url_.spec(), base::Bind(&ArcAppMenuObserver::OnUrlHandlerList, | |
| 187 weak_ptr_factory_.GetWeakPtr())); | |
| 188 | |
| 189 // Start the animation just in case. Since the IPC above almost always returns | |
| 190 // within a few milliseconds, the user will unlikely see the anination. | |
| 191 animation_timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(1), | |
| 192 this, &ArcAppMenuObserver::OnAnimationTimerExpired); | |
| 193 } | |
| 194 | |
| 195 bool ArcAppMenuObserver::IsCommandIdSupported(int command_id) { | |
| 196 return all_command_ids_.count(command_id); | |
| 197 } | |
| 198 | |
| 199 bool ArcAppMenuObserver::IsCommandIdChecked(int command_id) { | |
| 200 return false; | |
| 201 } | |
| 202 | |
| 203 bool ArcAppMenuObserver::IsCommandIdEnabled(int command_id) { | |
| 204 // Items will be enabled later in OnUrlHandlerList() as needed. | |
| 205 return false; | |
| 206 } | |
| 207 | |
| 208 void ArcAppMenuObserver::ExecuteCommand(int command_id) { | |
| 209 arc::IntentHelperInstance* intent_helper_instance = GetIntentHelper(); | |
| 210 if (!intent_helper_instance) { | |
| 211 return; | |
| 212 } | |
| 213 | |
| 214 const size_t index = GetIndexForCommandId(command_id); | |
| 215 if (index < handlers_.size()) { | |
| 216 intent_helper_instance->HandleUrl(link_url_.spec(), | |
| 217 handlers_[index]->package_name); | |
| 218 } | |
| 219 } | |
| 220 | |
| 221 void ArcAppMenuObserver::OnMenuCancel() {} | |
| 222 | |
| 223 std::set<int> ArcAppMenuObserver::GetCommandIdsToEnableForTesting( | |
| 224 size_t num_apps) { | |
| 225 return GetCommandIdsToEnable(num_apps); | |
| 226 } | |
| 227 | |
| 228 std::set<int> ArcAppMenuObserver::GetCommandIdsToDisableForTesting( | |
| 229 size_t num_apps) { | |
| 230 return GetCommandIdsToDisable(num_apps, GetAllCommandIds()); | |
| 231 } | |
| 232 | |
| 233 int ArcAppMenuObserver::GetIndexForCommandIdForTesting(int command_id) { | |
| 234 return GetIndexForCommandId(command_id); | |
| 235 } | |
| 236 | |
| 237 base::string16 ArcAppMenuObserver::GetLabelForCommandId(int command_id) const { | |
| 238 if (command_id == IDC_CONTENT_CONTEXT_ARC_SUB_MENU) | |
| 239 return l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_MORE_ARC_APPS); | |
| 240 | |
| 241 const size_t index = GetIndexForCommandId(command_id); | |
| 242 if (index < handlers_.size()) { | |
| 243 return l10n_util::GetStringFUTF16( | |
| 244 IDS_CONTENT_CONTEXT_OPEN_WITH_ARC_APP, | |
| 245 base::UTF8ToUTF16(handlers_[index]->name.get())); | |
| 246 } | |
| 247 return base::EmptyString16(); | |
| 248 } | |
| 249 | |
| 250 void ArcAppMenuObserver::OnUrlHandlerList( | |
| 251 mojo::Array<arc::UrlHandlerInfoPtr> handlers) { | |
| 252 animation_timer_.Stop(); | |
| 253 handlers_ = std::move(handlers); | |
| 254 | |
| 255 const size_t num_apps = handlers_.size(); | |
| 256 std::set<int> disabled(GetCommandIdsToDisable(num_apps, all_command_ids_)); | |
| 257 for (const auto& command_id : disabled) { | |
| 258 proxy_->UpdateMenuItem(command_id, | |
| 259 false, // enabled | |
| 260 true, // hidden | |
| 261 base::EmptyString16()); | |
| 262 } | |
| 263 | |
| 264 std::set<int> enabled(GetCommandIdsToEnable(num_apps)); | |
| 265 for (const auto& command_id : enabled) { | |
| 266 // TODO(yusukes): Show icon of the app. | |
| 267 proxy_->UpdateMenuItem(command_id, | |
| 268 true, // enabled | |
| 269 false, // hidden | |
| 270 GetLabelForCommandId(command_id)); | |
| 271 } | |
| 272 } | |
| 273 | |
| 274 void ArcAppMenuObserver::OnAnimationTimerExpired() { | |
| 275 // Append '.' characters to the end of "Checking". | |
| 276 loading_frame_ = (loading_frame_ + 1) & 3; | |
| 277 base::string16 loading_message = | |
| 278 loading_message_ + base::string16(loading_frame_, '.'); | |
| 279 | |
| 280 // Update the menu item with the text. We disable this item to prevent users | |
| 281 // from selecting it. | |
| 282 for (const auto& command_id : all_command_ids_) { | |
| 283 proxy_->UpdateMenuItem(command_id, | |
| 284 false, // enabled | |
| 285 false, // hidden | |
| 286 loading_message); | |
| 287 } | |
| 288 } | |
| OLD | NEW |