blob: 6b815372f7489a47b10dd8a2cbed18a02c144ee6 [file] [log] [blame]
// 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_ash.h"
#include <algorithm>
#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/browser/renderer_context_menu/open_with_menu_factory.h"
#include "chrome/grit/generated_resources.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"
const int OpenWithMenuObserver::kNumMainMenuCommands = 4;
const int OpenWithMenuObserver::kNumSubMenuCommands = 10;
bool OpenWithMenuObserver::SubMenuDelegate::IsCommandIdChecked(
int command_id) const {
return false;
}
bool OpenWithMenuObserver::SubMenuDelegate::IsCommandIdEnabled(
int command_id) const {
return true;
}
void OpenWithMenuObserver::SubMenuDelegate::ExecuteCommand(int command_id,
int event_flags) {
parent_->ExecuteCommand(command_id);
}
OpenWithMenuObserver::OpenWithMenuObserver(RenderViewContextMenuProxy* proxy)
: proxy_(proxy),
submenu_delegate_(this),
more_apps_label_(
l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_MORE_APPS)) {}
OpenWithMenuObserver::~OpenWithMenuObserver() {}
void OpenWithMenuObserver::InitMenu(const content::ContextMenuParams& params) {
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_));
AddPlaceholderItems(proxy_, submenu.get());
submenu_ = std::move(submenu);
menu_model_->AddObserver(this);
}
bool OpenWithMenuObserver::IsCommandIdSupported(int command_id) {
return command_id >= IDC_CONTENT_CONTEXT_OPEN_WITH1 &&
command_id <= IDC_CONTENT_CONTEXT_OPEN_WITH_LAST;
}
bool OpenWithMenuObserver::IsCommandIdChecked(int command_id) {
return false;
}
bool OpenWithMenuObserver::IsCommandIdEnabled(int command_id) {
return true;
}
void OpenWithMenuObserver::ExecuteCommand(int command_id) {
// 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 OpenWithMenuObserver::ModelChanged(
const std::vector<ash::LinkHandlerInfo>& handlers) {
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);
if (!it->second.icon.IsEmpty())
proxy_->UpdateMenuIcon(command_id, it->second.icon);
}
}
}
void OpenWithMenuObserver::AddPlaceholderItemsForTesting(
RenderViewContextMenuProxy* proxy,
ui::SimpleMenuModel* submenu) {
return AddPlaceholderItems(proxy, submenu);
}
std::pair<OpenWithMenuObserver::HandlerMap, int>
OpenWithMenuObserver::BuildHandlersMapForTesting(
const std::vector<ash::LinkHandlerInfo>& handlers) {
return BuildHandlersMap(handlers);
}
void OpenWithMenuObserver::AddPlaceholderItems(
RenderViewContextMenuProxy* proxy,
ui::SimpleMenuModel* submenu) {
for (int i = 0; i < kNumSubMenuCommands; ++i) {
const int command_id =
IDC_CONTENT_CONTEXT_OPEN_WITH1 + kNumMainMenuCommands + i;
submenu->AddItem(command_id, base::EmptyString16());
}
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, base::EmptyString16(), submenu);
}
std::pair<OpenWithMenuObserver::HandlerMap, int>
OpenWithMenuObserver::BuildHandlersMap(
const std::vector<ash::LinkHandlerInfo>& handlers) {
const int kInvalidCommandId = -1;
const int submenu_id_start =
IDC_CONTENT_CONTEXT_OPEN_WITH1 + kNumMainMenuCommands;
OpenWithMenuObserver::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);
}
RenderViewContextMenuObserver* OpenWithMenuFactory::CreateMenu(
RenderViewContextMenuProxy* proxy) {
return new OpenWithMenuObserver(proxy);
}