// Copyright 2018 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 "ash/app_menu/app_menu_model_adapter.h" #include "ash/app_menu/notification_menu_controller.h" #include "ash/constants/ash_features.h" #include "ash/public/cpp/shelf_model.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "ui/accessibility/ax_enums.mojom.h" #include "ui/base/models/simple_menu_model.h" #include "ui/views/controls/menu/menu_item_view.h" #include "ui/views/controls/menu/menu_model_adapter.h" #include "ui/views/controls/menu/menu_runner.h" #include "ui/views/controls/menu/submenu_view.h" #include "ui/views/widget/widget.h" namespace ash { namespace { // The UMA histogram that logs the commands which are executed on non-app // context menus. constexpr char kNonAppContextMenuExecuteCommand[] = "Apps.ContextMenuExecuteCommand.NotFromApp"; constexpr char kNonAppContextMenuExecuteCommandInTablet[] = "Apps.ContextMenuExecuteCommand.NotFromApp.TabletMode"; constexpr char kNonAppContextMenuExecuteCommandInClamshell[] = "Apps.ContextMenuExecuteCommand.NotFromApp.ClamshellMode"; // The UMA histogram that logs the commands which are executed on app context // menus. constexpr char kAppContextMenuExecuteCommand[] = "Apps.ContextMenuExecuteCommand.FromApp"; constexpr char kAppContextMenuExecuteCommandInTablet[] = "Apps.ContextMenuExecuteCommand.FromApp.TabletMode"; constexpr char kAppContextMenuExecuteCommandInClamshell[] = "Apps.ContextMenuExecuteCommand.FromApp.ClamshellMode"; } // namespace AppMenuModelAdapter::AppMenuModelAdapter( const std::string& app_id, std::unique_ptr model, views::Widget* widget_owner, ui::MenuSourceType source_type, base::OnceClosure on_menu_closed_callback, bool is_tablet_mode) : views::MenuModelAdapter(model.get()), app_id_(app_id), model_(std::move(model)), widget_owner_(widget_owner), source_type_(source_type), on_menu_closed_callback_(std::move(on_menu_closed_callback)), is_tablet_mode_(is_tablet_mode) {} AppMenuModelAdapter::~AppMenuModelAdapter() = default; void AppMenuModelAdapter::Run(const gfx::Rect& menu_anchor_rect, views::MenuAnchorPosition menu_anchor_position, int run_types) { DCHECK(!root_); DCHECK(model_); menu_open_time_ = base::TimeTicks::Now(); root_ = CreateMenu(); if (ash::features::IsNotificationsInContextMenuEnabled()) { notification_menu_controller_ = std::make_unique(app_id_, root_, this); } menu_runner_ = std::make_unique(root_, run_types); menu_runner_->RunMenuAt(widget_owner_, nullptr /* MenuButtonController */, menu_anchor_rect, menu_anchor_position, source_type_); } bool AppMenuModelAdapter::IsShowingMenu() const { return menu_runner_ && menu_runner_->IsRunning(); } void AppMenuModelAdapter::Cancel() { if (!IsShowingMenu()) return; menu_runner_->Cancel(); } int AppMenuModelAdapter::GetCommandIdForHistograms(int command_id) { return command_id; } base::TimeTicks AppMenuModelAdapter::GetClosingEventTime() { DCHECK(menu_runner_); return menu_runner_->closing_event_time(); } views::Widget* AppMenuModelAdapter::GetSubmenuWidget() { if (root_ && root_->GetSubmenu()) return root_->GetSubmenu()->GetWidget(); return nullptr; } void AppMenuModelAdapter::ExecuteCommand(int id, int mouse_event_flags) { // Note that the command execution may cause this to get deleted - for // example, for search result menus, the command could open an app window // causing the app list search to get cleared, destroying non-zero state // search results. RecordExecuteCommandHistogram(GetCommandIdForHistograms(id)); views::MenuModelAdapter::ExecuteCommand(id, mouse_event_flags); } void AppMenuModelAdapter::OnMenuClosed(views::MenuItemView* menu) { DCHECK_NE(base::TimeTicks(), menu_open_time_); RecordHistogramOnMenuClosed(); // No |widget_owner_| in tests. if (widget_owner_ && widget_owner_->GetRootView()) { widget_owner_->GetRootView()->NotifyAccessibilityEvent( ax::mojom::Event::kMenuEnd, /*send_native_event=*/true); } if (on_menu_closed_callback_) std::move(on_menu_closed_callback_).Run(); } void AppMenuModelAdapter::RecordExecuteCommandHistogram(int command_id) { const bool is_not_from_app = app_id().empty(); base::UmaHistogramSparse(is_not_from_app ? kNonAppContextMenuExecuteCommand : kAppContextMenuExecuteCommand, command_id); if (is_tablet_mode_) { base::UmaHistogramSparse(is_not_from_app ? kNonAppContextMenuExecuteCommandInTablet : kAppContextMenuExecuteCommandInTablet, command_id); } else { base::UmaHistogramSparse(is_not_from_app ? kNonAppContextMenuExecuteCommandInClamshell : kAppContextMenuExecuteCommandInClamshell, command_id); } } } // namespace ash