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

Unified Diff: chrome/browser/renderer_context_menu/arc_app_menu_observer_chromeos.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, 9 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/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..ebfa63bef0afaa15a342953322b5467a28bf38ea
--- /dev/null
+++ b/chrome/browser/renderer_context_menu/arc_app_menu_observer_chromeos.cc
@@ -0,0 +1,288 @@
+// 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_|. Sicne the information is
hidehiko 2016/03/07 14:40:07 nit: s/Sicne/Since/
Yusuke Sato 2016/03/07 22:25:16 Done.
+ // 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;
+ }
+
+ 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);
+
+ const size_t index = GetIndexForCommandId(command_id);
hidehiko 2016/03/07 14:40:07 nit: GetIndexForCommandId can return a negative va
Yusuke Sato 2016/03/07 22:25:16 Done.
+ 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);
hidehiko 2016/03/07 14:40:07 nit: I'm ok with the current code, but how about h
Yusuke Sato 2016/03/07 22:25:16 That allows us to remove GetIndexForCommandId() as
hidehiko 2016/03/14 12:07:05 FYI: what I imagined was; unordered_map<int /* co
Yusuke Sato 2016/03/15 00:00:13 Ah okay, I guess I misunderstood your comment. Tha
+
+ 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);
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698