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

Unified 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 side-by-side diff with in-line comments
Download patch
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..d55e7231c1c839efd5eab5ca5dc6754352fb41e3
--- /dev/null
+++ b/chrome/browser/renderer_context_menu/open_with_menu_factory_ash.cc
@@ -0,0 +1,220 @@
+// 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/renderer_context_menu/link_handler_model.h"
+#include "ash/renderer_context_menu/open_with_menu_controller.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) {}
+
+ ~OpenWithMenuObserver() override {}
+
+ // RenderViewContextMenuObserver overrides:
+ void InitMenu(const content::ContextMenuParams& params) override {
+ if (!ash::Shell::HasInstance())
+ return;
+ ash::OpenWithMenuController* controller =
+ ash::Shell::GetInstance()->open_with_menu_controller();
+ if (!controller)
+ return;
+
+ link_url_ = params.link_url;
+ menu_model_ = controller->CreateModel(link_url_);
+ if (!menu_model_)
+ return;
+
+ // Add placeholder items.
+ 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.
+ 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_.reset(submenu);
+
+ for (int i = 0; i < kNumMainMenuCommands; ++i) {
+ const int command_id = IDC_CONTENT_CONTEXT_OPEN_WITH1 + i;
+ 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.
+ base::string16 label =
+ l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_MORE_APPS);
+ proxy_->AddSubMenu(command_id, label, submenu_.get());
+ } else {
+ proxy_->AddMenuItem(command_id, base::EmptyString16());
+ }
+ }
+
+ 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;
hidehiko 2016/04/20 04:40:18 indent?
Yusuke Sato 2016/04/22 05:27:48 This is by git cl format actually..
+ }
+
+ 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(
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.
+ 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) {
+ // Keep the submenu parent.
+ } else if (it == handlers_.end()) {
+ // Hide the menu.
+ 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_;
+ 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);
+}

Powered by Google App Engine
This is Rietveld 408576698