Index: chrome/browser/renderer_context_menu/open_with_menu_factory_ash.cc |
diff --git a/chrome/browser/renderer_context_menu/open_with_menu_factory_ash.cc b/chrome/browser/renderer_context_menu/open_with_menu_factory_ash.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..1e2886cb117c6f01440f536b4667e4c0c664f64b |
--- /dev/null |
+++ b/chrome/browser/renderer_context_menu/open_with_menu_factory_ash.cc |
@@ -0,0 +1,222 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "chrome/browser/renderer_context_menu/open_with_menu_factory.h" |
+ |
+#include <memory> |
+#include <unordered_map> |
+#include <utility> |
+#include <vector> |
+ |
+#include "ash/link_handler_model.h" |
+#include "ash/link_handler_model_factory.h" |
+#include "ash/shell.h" |
+#include "base/strings/string_util.h" |
+#include "base/strings/utf_string_conversions.h" |
+#include "chrome/app/chrome_command_ids.h" |
+#include "chrome/grit/generated_resources.h" |
+#include "components/renderer_context_menu/render_view_context_menu_observer.h" |
+#include "components/renderer_context_menu/render_view_context_menu_proxy.h" |
+#include "content/public/common/context_menu_params.h" |
+#include "ui/base/l10n/l10n_util.h" |
+#include "ui/base/models/simple_menu_model.h" |
+ |
+namespace { |
+ |
+using HandlerMap = std::unordered_map<int, ash::LinkHandlerInfo>; |
+ |
+// An observer class which populates the "Open with <app>" menu items either |
+// synchronously or asynchronously. |
+class OpenWithMenuObserver : public RenderViewContextMenuObserver, |
+ public ash::LinkHandlerModel::Observer { |
+ public: |
+ class SubMenuDelegate : public ui::SimpleMenuModel::Delegate { |
+ public: |
+ explicit SubMenuDelegate(OpenWithMenuObserver* parent) : parent_(parent) {} |
+ ~SubMenuDelegate() override {} |
+ |
+ bool IsCommandIdChecked(int command_id) const override { return false; } |
+ bool IsCommandIdEnabled(int command_id) const override { return true; } |
+ |
+ bool GetAcceleratorForCommandId(int command_id, |
+ ui::Accelerator* accelerator) override { |
+ return false; |
+ } |
+ |
+ void ExecuteCommand(int command_id, int event_flags) override { |
+ parent_->ExecuteCommand(command_id); |
+ } |
+ |
+ private: |
+ OpenWithMenuObserver* const parent_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(SubMenuDelegate); |
+ }; |
+ |
+ explicit OpenWithMenuObserver(RenderViewContextMenuProxy* proxy) |
+ : proxy_(proxy), |
+ submenu_delegate_(this), |
+ more_apps_label_( |
+ l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_MORE_APPS)) {} |
+ |
+ ~OpenWithMenuObserver() override {} |
+ |
+ // RenderViewContextMenuObserver overrides: |
+ void InitMenu(const content::ContextMenuParams& params) override { |
+ if (!ash::Shell::HasInstance()) |
+ return; |
+ ash::LinkHandlerModelFactory* factory = |
+ ash::Shell::GetInstance()->link_handler_model_factory(); |
+ if (!factory) |
+ return; |
+ |
+ link_url_ = params.link_url; |
+ menu_model_ = factory->CreateModel(link_url_); |
+ if (!menu_model_) |
+ return; |
+ |
+ // Add placeholder items. |
+ std::unique_ptr<ui::SimpleMenuModel> submenu( |
+ new ui::SimpleMenuModel(&submenu_delegate_)); |
+ for (int i = 0; i < kNumSubMenuCommands; ++i) { |
+ const int command_id = |
+ IDC_CONTENT_CONTEXT_OPEN_WITH1 + kNumMainMenuCommands + i; |
+ submenu->AddItem(command_id, base::EmptyString16()); |
+ } |
+ submenu_ = std::move(submenu); |
+ |
+ int command_id; |
+ for (int i = 0; i < kNumMainMenuCommands - 1; ++i) { |
+ command_id = IDC_CONTENT_CONTEXT_OPEN_WITH1 + i; |
+ proxy_->AddMenuItem(command_id, base::EmptyString16()); |
+ } |
+ proxy_->AddSubMenu(++command_id, more_apps_label_, submenu_.get()); |
+ |
+ menu_model_->AddObserver(this); |
+ } |
+ |
+ bool IsCommandIdSupported(int command_id) override { |
+ return command_id >= IDC_CONTENT_CONTEXT_OPEN_WITH1 && |
+ command_id <= IDC_CONTENT_CONTEXT_OPEN_WITH_LAST; |
+ } |
+ |
+ bool IsCommandIdChecked(int command_id) override { return false; } |
+ bool IsCommandIdEnabled(int command_id) override { return true; } |
+ |
+ void ExecuteCommand(int command_id) override { |
+ // Note: SubmenuDelegate also calls this method with a command_id for the |
+ // submenu. |
+ const auto it = handlers_.find(command_id); |
+ if (it == handlers_.end()) |
+ return; |
+ menu_model_->OpenLinkWithHandler(link_url_, it->second.id); |
+ } |
+ |
+ void OnMenuCancel() override {} |
+ |
+ // ash::OpenWithItems::Delegate overrides: |
+ void ModelChanged( |
+ const std::vector<ash::LinkHandlerInfo>& handlers) override { |
+ auto result = BuildHandlersMap(handlers); |
+ handlers_ = std::move(result.first); |
+ const int submenu_parent_id = result.second; |
+ for (int command_id = IDC_CONTENT_CONTEXT_OPEN_WITH1; |
+ command_id <= IDC_CONTENT_CONTEXT_OPEN_WITH_LAST; ++command_id) { |
+ const auto it = handlers_.find(command_id); |
+ if (command_id == submenu_parent_id) { |
+ // Show the submenu parent. |
+ proxy_->UpdateMenuItem(command_id, true, false, more_apps_label_); |
+ } else if (it == handlers_.end()) { |
+ // Hide the menu or submenu parent. |
+ proxy_->UpdateMenuItem(command_id, false, true, base::EmptyString16()); |
+ } else { |
+ // Update the menu with the new model. |
+ const base::string16 label = |
+ l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_OPEN_WITH_APP, |
+ base::UTF8ToUTF16(it->second.name)); |
+ proxy_->UpdateMenuItem(command_id, true, false, label); |
+ } |
+ } |
+ } |
+ |
+ static std::pair<HandlerMap, int> BuildHandlersMapForTesting( |
+ const std::vector<ash::LinkHandlerInfo>& handlers) { |
+ return BuildHandlersMap(handlers); |
+ } |
+ |
+ private: |
+ // Converts |handlers| into HandlerMap which is a map from a command ID to a |
+ // LinkHandlerInfo and returns the map. Also returns a command id for the |
+ // parent of the submenu. When the submenu is not needed, the function |
+ // returns |kInvalidCommandId|. |
+ static std::pair<HandlerMap, int> BuildHandlersMap( |
+ const std::vector<ash::LinkHandlerInfo>& handlers) { |
+ const int kInvalidCommandId = -1; |
+ const int submenu_id_start = |
+ IDC_CONTENT_CONTEXT_OPEN_WITH1 + kNumMainMenuCommands; |
+ |
+ HandlerMap handler_map; |
+ int submenu_parent_command_id = kInvalidCommandId; |
+ |
+ const int num_apps = handlers.size(); |
+ size_t handlers_index = 0; |
+ // We use the last item in the main menu (IDC_CONTENT_CONTEXT_OPEN_WITH1 + |
+ // kNumMainMenuCommands- 1) as a parent of a submenu, and others as regular |
+ // menu items. |
+ if (num_apps < kNumMainMenuCommands) { |
+ // All apps can be shown with the regular main menu items. |
+ for (int i = 0; i < num_apps; ++i) { |
+ handler_map[IDC_CONTENT_CONTEXT_OPEN_WITH1 + i] = |
+ handlers[handlers_index++]; |
+ } |
+ } else { |
+ // Otherwise, use the submenu too. In this case, disable the last item of |
+ // the regular main menu (hence '-2'). |
+ for (int i = 0; i < kNumMainMenuCommands - 2; ++i) { |
+ handler_map[IDC_CONTENT_CONTEXT_OPEN_WITH1 + i] = |
+ handlers[handlers_index++]; |
+ } |
+ submenu_parent_command_id = |
+ IDC_CONTENT_CONTEXT_OPEN_WITH1 + kNumMainMenuCommands - 1; |
+ const int sub_items = |
+ std::min(num_apps - (kNumMainMenuCommands - 2), kNumSubMenuCommands); |
+ for (int i = 0; i < sub_items; ++i) { |
+ handler_map[submenu_id_start + i] = handlers[handlers_index++]; |
+ } |
+ } |
+ |
+ return std::make_pair(std::move(handler_map), submenu_parent_command_id); |
+ } |
+ |
+ static const int kNumMainMenuCommands; |
+ static const int kNumSubMenuCommands; |
+ |
+ RenderViewContextMenuProxy* const proxy_; |
+ SubMenuDelegate submenu_delegate_; |
+ const base::string16 more_apps_label_; |
+ GURL link_url_; |
+ |
+ // A menu model received from Ash side. |
+ std::unique_ptr<ash::LinkHandlerModel> menu_model_; |
+ HandlerMap handlers_; |
+ // A submenu passed to Chrome side. |
+ std::unique_ptr<ui::MenuModel> submenu_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(OpenWithMenuObserver); |
+}; |
+ |
+const int OpenWithMenuObserver::kNumMainMenuCommands = 4; |
+const int OpenWithMenuObserver::kNumSubMenuCommands = 10; |
+ |
+} // namespace |
+ |
+std::pair<HandlerMap, int> BuildHandlersMapForTesting( |
+ const std::vector<ash::LinkHandlerInfo>& handlers) { |
+ return OpenWithMenuObserver::BuildHandlersMapForTesting(handlers); |
+} |
+ |
+RenderViewContextMenuObserver* OpenWithMenuFactory::CreateMenu( |
+ RenderViewContextMenuProxy* proxy) { |
+ return new OpenWithMenuObserver(proxy); |
+} |