| Index: chrome/browser/renderer_context_menu/arc_app_menu_observer_chromeos.cc
|
| diff --git a/chrome/browser/renderer_context_menu/arc_app_menu_observer_chromeos.cc b/chrome/browser/renderer_context_menu/arc_app_menu_observer_chromeos.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..1fd91010eb4323f3ae402af7653e75ea4d36b25a
|
| --- /dev/null
|
| +++ b/chrome/browser/renderer_context_menu/arc_app_menu_observer_chromeos.cc
|
| @@ -0,0 +1,290 @@
|
| +// 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/arc_app_menu_observer_chromeos.h"
|
| +
|
| +#include <algorithm>
|
| +
|
| +#include "base/bind.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/render_view_context_menu.h"
|
| +#include "chrome/grit/generated_resources.h"
|
| +#include "components/arc/arc_bridge_service.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 {
|
| +
|
| +const size_t kMaxArcAppsInMainMenu =
|
| + IDC_CONTENT_CONTEXT_ARC_APP_LAST - IDC_CONTENT_CONTEXT_ARC_APP1 + 1;
|
| +const size_t kMaxArcAppsInSubMenu =
|
| + IDC_CONTENT_CONTEXT_ARC_SUB_APP_LAST - IDC_CONTENT_CONTEXT_ARC_SUB_APP1 + 1;
|
| +
|
| +const int kMinInstanceVersion = 2; // see intent_helper.mojom
|
| +
|
| +std::set<int> GetAllCommandIds() {
|
| + std::set<int> ids;
|
| +
|
| + for (size_t i = 0; i < kMaxArcAppsInMainMenu; ++i) {
|
| + ids.insert(IDC_CONTENT_CONTEXT_ARC_APP1 + i);
|
| + }
|
| + ids.insert(IDC_CONTENT_CONTEXT_ARC_SUB_MENU);
|
| + for (size_t i = 0; i < kMaxArcAppsInSubMenu; ++i) {
|
| + ids.insert(IDC_CONTENT_CONTEXT_ARC_SUB_APP1 + i);
|
| + }
|
| +
|
| + return ids;
|
| +}
|
| +
|
| +// Returns a set of command IDs to enable when the total number of ARC apps to
|
| +// show in the main and sub menus is |num_apps|.
|
| +std::set<int> GetCommandIdsToEnable(size_t num_apps) {
|
| + std::set<int> ids;
|
| +
|
| + if (num_apps <= kMaxArcAppsInMainMenu) {
|
| + // When |num_apps| fits in the main menu, do not enable
|
| + // IDC_CONTENT_CONTEXT_ARC_SUB_MENU and IDC_CONTENT_CONTEXT_ARC_SUB_APP*.
|
| + for (size_t i = 0; i < num_apps; ++i) {
|
| + ids.insert(IDC_CONTENT_CONTEXT_ARC_APP1 + i);
|
| + }
|
| + } else {
|
| + // When |num_apps| does not fit in the main menu, enable
|
| + // IDC_CONTENT_CONTEXT_ARC_SUB_MENU and some of
|
| + // IDC_CONTENT_CONTEXT_ARC_SUB_APP* IDs. In this case, do NOT enable
|
| + // IDC_CONTENT_CONTEXT_ARC_APP_LAST, hence the '-1'.
|
| + for (size_t i = 0; i < kMaxArcAppsInMainMenu - 1; ++i) {
|
| + ids.insert(IDC_CONTENT_CONTEXT_ARC_APP1 + i);
|
| + }
|
| +
|
| + ids.insert(IDC_CONTENT_CONTEXT_ARC_SUB_MENU);
|
| + size_t sub_items = std::min(num_apps - (kMaxArcAppsInMainMenu - 1),
|
| + kMaxArcAppsInSubMenu);
|
| + for (size_t i = 0; i < sub_items; ++i) {
|
| + ids.insert(IDC_CONTENT_CONTEXT_ARC_SUB_APP1 + i);
|
| + }
|
| + }
|
| +
|
| + return ids;
|
| +}
|
| +
|
| +// The same as GetCommandIdsToEnable except that the function returns a set of
|
| +// IDs to disable.
|
| +std::set<int> GetCommandIdsToDisable(size_t num_apps,
|
| + const std::set<int>& all_ids) {
|
| + std::set<int> ids;
|
| + std::set<int> enabled(GetCommandIdsToEnable(num_apps));
|
| + std::set_difference(all_ids.begin(), all_ids.end(), enabled.begin(),
|
| + enabled.end(), std::inserter(ids, ids.begin()));
|
| + return ids;
|
| +}
|
| +
|
| +int GetIndexForCommandId(int command_id) {
|
| + int index = -1;
|
| + if (command_id >= IDC_CONTENT_CONTEXT_ARC_APP1 &&
|
| + command_id <= IDC_CONTENT_CONTEXT_ARC_APP_LAST) {
|
| + index = command_id - IDC_CONTENT_CONTEXT_ARC_APP1;
|
| + } else if (command_id >= IDC_CONTENT_CONTEXT_ARC_SUB_APP1 &&
|
| + command_id <= IDC_CONTENT_CONTEXT_ARC_SUB_APP_LAST) {
|
| + const int offset = kMaxArcAppsInMainMenu - 1;
|
| + index = command_id - IDC_CONTENT_CONTEXT_ARC_SUB_APP1 + offset;
|
| + }
|
| + return index;
|
| +}
|
| +
|
| +class SubMenuDelegate : public ui::SimpleMenuModel::Delegate {
|
| + public:
|
| + explicit SubMenuDelegate(ArcAppMenuObserver* parent_menu)
|
| + : parent_menu_(parent_menu) {}
|
| + ~SubMenuDelegate() override {}
|
| +
|
| + // ui::SimpleMenuModel::Delegate overrides:
|
| + 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_menu_->ExecuteCommand(command_id);
|
| + }
|
| +
|
| + private:
|
| + ArcAppMenuObserver* parent_menu_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(SubMenuDelegate);
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +ArcAppMenuObserver::ArcAppMenuObserver(RenderViewContextMenuProxy* proxy)
|
| + : proxy_(proxy),
|
| + all_command_ids_(GetAllCommandIds()),
|
| + sub_menu_(new SubMenuDelegate(this)),
|
| + loading_frame_(0),
|
| + weak_ptr_factory_(this) {}
|
| +
|
| +ArcAppMenuObserver::~ArcAppMenuObserver() {}
|
| +
|
| +arc::IntentHelperInstance* ArcAppMenuObserver::GetIntentHelper() {
|
| + arc::ArcBridgeService* bridge = arc::ArcBridgeService::Get();
|
| + if (!bridge) {
|
| + DLOG(WARNING) << "ARC bridge is not ready.";
|
| + return nullptr;
|
| + }
|
| + arc::IntentHelperInstance* intent_helper_instance =
|
| + bridge->intent_helper_instance();
|
| + if (!intent_helper_instance) {
|
| + DLOG(WARNING) << "ARC intent helper instance is not ready.";
|
| + return nullptr;
|
| + }
|
| + if (bridge->intent_helper_version() < kMinInstanceVersion) {
|
| + DLOG(WARNING) << "ARC intent helper instance is too old.";
|
| + return nullptr;
|
| + }
|
| + return intent_helper_instance;
|
| +}
|
| +
|
| +void ArcAppMenuObserver::InitMenu(const content::ContextMenuParams& params) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| +
|
| + arc::IntentHelperInstance* intent_helper_instance = GetIntentHelper();
|
| + if (!intent_helper_instance) {
|
| + return;
|
| + }
|
| +
|
| + if (params.link_url.is_empty()) {
|
| + return;
|
| + }
|
| + link_url_ = params.link_url;
|
| +
|
| + loading_message_ =
|
| + l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_LOADING_ARC_APP_NAMES);
|
| +
|
| + // Add placeholder items.
|
| + for (size_t i = 0; i < kMaxArcAppsInMainMenu; ++i) {
|
| + proxy_->AddMenuItem(IDC_CONTENT_CONTEXT_ARC_APP1 + i,
|
| + loading_message_);
|
| + }
|
| + // Add a placeholder sub menu.
|
| + proxy_->AddSubMenu(IDC_CONTENT_CONTEXT_ARC_SUB_MENU, loading_message_,
|
| + &sub_menu_);
|
| + // Add placeholder sub items.
|
| + for (size_t i = 0; i < kMaxArcAppsInSubMenu; ++i) {
|
| + sub_menu_.AddItem(IDC_CONTENT_CONTEXT_ARC_SUB_APP1 + i,
|
| + loading_message_);
|
| + }
|
| +
|
| + // Check if ARC apps can handle the |link_url_|. Since the information is
|
| + // held in a different (ARC) process, issue a mojo IPC request. Usually, the
|
| + // callback function, OnUrlHandlerList, is called within a few milliseconds
|
| + // even on a slowest Chromebook we support.
|
| + intent_helper_instance->RequestUrlHandlerList(
|
| + link_url_.spec(), base::Bind(&ArcAppMenuObserver::OnUrlHandlerList,
|
| + weak_ptr_factory_.GetWeakPtr()));
|
| +
|
| + // Start the animation just in case. Since the IPC above almost always returns
|
| + // within a few milliseconds, the user will unlikely see the anination.
|
| + animation_timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(1),
|
| + this, &ArcAppMenuObserver::OnAnimationTimerExpired);
|
| +}
|
| +
|
| +bool ArcAppMenuObserver::IsCommandIdSupported(int command_id) {
|
| + return all_command_ids_.count(command_id);
|
| +}
|
| +
|
| +bool ArcAppMenuObserver::IsCommandIdChecked(int command_id) {
|
| + return false;
|
| +}
|
| +
|
| +bool ArcAppMenuObserver::IsCommandIdEnabled(int command_id) {
|
| + // Items will be enabled later in OnUrlHandlerList() as needed.
|
| + return false;
|
| +}
|
| +
|
| +void ArcAppMenuObserver::ExecuteCommand(int command_id) {
|
| + arc::IntentHelperInstance* intent_helper_instance = GetIntentHelper();
|
| + if (!intent_helper_instance) {
|
| + return;
|
| + }
|
| +
|
| + // |index| will be UINT_MAX when command_id is invalid/unknown.
|
| + const size_t index = GetIndexForCommandId(command_id);
|
| + if (index < handlers_.size()) {
|
| + intent_helper_instance->HandleUrl(link_url_.spec(),
|
| + handlers_[index]->package_name);
|
| + }
|
| +}
|
| +
|
| +void ArcAppMenuObserver::OnMenuCancel() {}
|
| +
|
| +std::set<int> ArcAppMenuObserver::GetCommandIdsToEnableForTesting(
|
| + size_t num_apps) {
|
| + return GetCommandIdsToEnable(num_apps);
|
| +}
|
| +
|
| +std::set<int> ArcAppMenuObserver::GetCommandIdsToDisableForTesting(
|
| + size_t num_apps) {
|
| + return GetCommandIdsToDisable(num_apps, GetAllCommandIds());
|
| +}
|
| +
|
| +int ArcAppMenuObserver::GetIndexForCommandIdForTesting(int command_id) {
|
| + return GetIndexForCommandId(command_id);
|
| +}
|
| +
|
| +base::string16 ArcAppMenuObserver::GetLabelForCommandId(int command_id) const {
|
| + if (command_id == IDC_CONTENT_CONTEXT_ARC_SUB_MENU)
|
| + return l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_MORE_ARC_APPS);
|
| +
|
| + // |index| will be UINT_MAX when command_id is invalid/unknown.
|
| + const size_t index = GetIndexForCommandId(command_id);
|
| + if (index < handlers_.size()) {
|
| + return l10n_util::GetStringFUTF16(
|
| + IDS_CONTENT_CONTEXT_OPEN_WITH_ARC_APP,
|
| + base::UTF8ToUTF16(handlers_[index]->name.get()));
|
| + }
|
| + return base::EmptyString16();
|
| +}
|
| +
|
| +void ArcAppMenuObserver::OnUrlHandlerList(
|
| + mojo::Array<arc::UrlHandlerInfoPtr> handlers) {
|
| + animation_timer_.Stop();
|
| + handlers_ = std::move(handlers);
|
| +
|
| + const size_t num_apps = handlers_.size();
|
| + std::set<int> disabled(GetCommandIdsToDisable(num_apps, all_command_ids_));
|
| + for (const auto& command_id : disabled) {
|
| + proxy_->UpdateMenuItem(command_id,
|
| + false, // enabled
|
| + true, // hidden
|
| + base::EmptyString16());
|
| + }
|
| +
|
| + std::set<int> enabled(GetCommandIdsToEnable(num_apps));
|
| + for (const auto& command_id : enabled) {
|
| + // TODO(yusukes): Show icon of the app.
|
| + proxy_->UpdateMenuItem(command_id,
|
| + true, // enabled
|
| + false, // hidden
|
| + GetLabelForCommandId(command_id));
|
| + }
|
| +}
|
| +
|
| +void ArcAppMenuObserver::OnAnimationTimerExpired() {
|
| + // Append '.' characters to the end of "Checking".
|
| + loading_frame_ = (loading_frame_ + 1) & 3;
|
| + base::string16 loading_message =
|
| + loading_message_ + base::string16(loading_frame_, '.');
|
| +
|
| + // Update the menu item with the text. We disable this item to prevent users
|
| + // from selecting it.
|
| + for (const auto& command_id : all_command_ids_) {
|
| + proxy_->UpdateMenuItem(command_id,
|
| + false, // enabled
|
| + false, // hidden
|
| + loading_message);
|
| + }
|
| +}
|
|
|