// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/web_applications/web_app_menu_model.h"

#include "base/feature_list.h"
#include "base/notimplemented.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/media/router/media_router_feature.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/extensions/extensions_container.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/toolbar/app_menu_model.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/web_applications/app_browser_controller.h"
#include "chrome/browser/ui/web_applications/web_app_browser_controller.h"
#include "chrome/common/chrome_features.h"
#include "chrome/grit/branded_strings.h"
#include "chrome/grit/generated_resources.h"
#include "components/omnibox/browser/location_bar_model.h"
#include "components/strings/grit/components_strings.h"
#include "components/vector_icons/vector_icons.h"
#include "components/webapps/browser/installable/installable_metrics.h"
#include "content/public/common/content_features.h"
#include "third_party/blink/public/common/features.h"
#include "ui/base/accelerators/menu_label_accelerator_util.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/models/image_model.h"
#include "ui/base/ui_base_features.h"
#include "url/gurl.h"

#if BUILDFLAG(IS_CHROMEOS)
#include "chromeos/strings/grit/chromeos_strings.h"
#include "chromeos/ui/frame/desks/move_to_desks_menu_delegate.h"
#include "chromeos/ui/frame/desks/move_to_desks_menu_model.h"
#include "ui/views/widget/widget.h"
#endif

namespace {

bool ShouldAllowOpenInChrome(BrowserWindowInterface* browser) {
  // Isolated Web Apps shouldn't be opened in Chrome.
  const bool is_isolated_web_app =
      web_app::AppBrowserController::IsIsolatedWebApp(browser);
  // Web Apps with enabled prevent close shouldn't be opened in Chrome.
  auto* const app_controller = web_app::AppBrowserController::From(browser);
  const bool prevent_close_enabled =
      app_controller && app_controller->IsPreventCloseEnabled();
  return !is_isolated_web_app && !prevent_close_enabled;
}

void AddItemWithStringIdAndVectorIcon(ui::SimpleMenuModel* model,
                                      int command_id,
                                      int string_id,
                                      const gfx::VectorIcon& vector_icon) {
  return model->AddItemWithStringIdAndIcon(
      command_id, string_id,
      ui::ImageModel::FromVectorIcon(vector_icon, ui::kColorMenuIcon,
                                     ui::SimpleMenuModel::kDefaultIconSize));
}

}  // namespace

constexpr int WebAppMenuModel::kUninstallAppCommandId;
constexpr int WebAppMenuModel::kExtensionsMenuCommandId;

WebAppMenuModel::WebAppMenuModel(ui::AcceleratorProvider* provider,
                                 BrowserWindowInterface* browser)
    : AppMenuModel(provider, browser) {}

WebAppMenuModel::~WebAppMenuModel() = default;

bool WebAppMenuModel::IsCommandIdEnabled(int command_id) const {
  switch (command_id) {
    case kUninstallAppCommandId:
      return web_app::AppBrowserController::From(browser())->CanUserUninstall();
    case kExtensionsMenuCommandId:
    case IDC_OPEN_IN_CHROME:
    case IDC_WEB_APP_UPGRADE_DIALOG:
#if BUILDFLAG(IS_CHROMEOS)
    case chromeos::MoveToDesksMenuModel::kMenuCommandId:
#endif
      // These commands only exist in the menu model if they should be visible
      // and enabled.
      return true;
    default:
      return AppMenuModel::IsCommandIdEnabled(command_id);
  }
}

void WebAppMenuModel::ExecuteCommand(int command_id, int event_flags) {
  switch (command_id) {
    case kUninstallAppCommandId:
      LogMenuAction(MENU_ACTION_UNINSTALL_APP);
      web_app::AppBrowserController::From(browser())->Uninstall(
          webapps::WebappUninstallSource::kAppMenu);
      break;
    case kExtensionsMenuCommandId:
      ExtensionsContainer::From(*browser())->ToggleExtensionsMenu();
      break;
    case IDC_OPEN_IN_CHROME:
      if (ShouldAllowOpenInChrome(browser())) {
        AppMenuModel::ExecuteCommand(command_id, event_flags);
      }
      break;
    case IDC_WEB_APP_UPGRADE_DIALOG:
      LogMenuAction(MENU_ACTION_TRIGGER_APP_UPDATE_DIALOG);
      web_app::AppBrowserController::From(browser())
          ->TriggerAppUpdateOrMigrationDialog(base::TimeTicks::Now());
      break;
    default:
      AppMenuModel::ExecuteCommand(command_id, event_flags);
      break;
  }
}

void WebAppMenuModel::Build() {
  auto* const app_browser_controller =
      web_app::AppBrowserController::From(browser());
  CHECK(app_browser_controller);
  web_app::WebAppBrowserController* app_controller =
      app_browser_controller->AsWebAppBrowserController();
  if (app_controller && (app_controller->HasPendingUpdate() ||
                         app_controller->HasPendingMigration())) {
    if (app_controller->HasPendingMigration()) {
      CHECK(base::FeatureList::IsEnabled(blink::features::kWebAppMigrationApi));
    }
    AddSeparator(ui::SPACING_SEPARATOR);
    gfx::ImageSkia icon = app_controller->GetAppMenuIcon();
    ui::ImageModel update_icon;
    if (!icon.isNull()) {
      update_icon = ui::ImageModel::FromImageSkia(icon);
    }
    if (update_icon.IsEmpty()) {
      update_icon = ui::ImageModel::FromVectorIcon(
          features::IsRoundedIconsEnabled()
              ? kRocketLaunchIcon
              : kBrowserToolsUpdateChromeRefreshOldIcon,
          ui::kColorMenuIconOnEmphasizedBackground, kDefaultIconSize);
    }
    AddItemWithStringIdAndIcon(IDC_WEB_APP_UPGRADE_DIALOG,
                               IDS_REVIEW_APP_UPDATE, update_icon);
    AddSeparator(ui::SPACING_SEPARATOR);
  }

  AddItemWithStringIdAndVectorIcon(
      this, IDC_WEB_APP_MENU_APP_INFO, IDS_APP_CONTEXT_MENU_SHOW_INFO,
      browser()->GetFeatures().location_bar_model()->GetVectorIcon());
  size_t app_info_index = GetItemCount() - 1;

  CHECK(browser());
  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();

  bool is_isolated_web_app =
      web_app::AppBrowserController::IsIsolatedWebApp(browser());

  if (web_contents) {
    std::u16string display_text =
        web_app::AppBrowserController::FormatUrlOrigin(
            web_contents->GetVisibleURL());

    // For Isolated Web Apps the origin's host name is a non-human-readable
    // string of characters, so instead of displaying the origin, the short name
    // of the app will be displayed.
    if (is_isolated_web_app) {
      std::u16string short_name = app_browser_controller->GetAppShortName();
      // For Isolated Web Apps, |GetAppShortName()| must be non-empty.
      display_text = short_name;
    } else {
      SetMinorTextIsUrlAt(app_info_index, true);
    }
    SetMinorText(app_info_index, display_text);
  }

  AddSeparator(ui::NORMAL_SEPARATOR);

  ExtensionsContainer* container = ExtensionsContainer::From(*browser());
  if (base::FeatureList::IsEnabled(
          features::kDesktopPWAsElidedExtensionsMenu) &&
      container && container->HasAnyExtensions() &&
      // Extensions are not supported inside Isolated Web Apps.
      !is_isolated_web_app) {
    AddItemWithStringIdAndVectorIcon(
        this, kExtensionsMenuCommandId, IDS_SHOW_EXTENSIONS,
        features::IsRoundedIconsEnabled()
            ? vector_icons::kChromeExtensionIcon
            : vector_icons::kExtensionChromeRefreshOldIcon);
    AddSeparator(ui::NORMAL_SEPARATOR);
  }

  if (app_browser_controller && app_browser_controller->has_tab_strip() &&
      !app_browser_controller->ShouldHideNewTabButton()) {
    AddItemWithStringIdAndVectorIcon(
        this, IDC_NEW_TAB, IDS_NEW_TAB,
        features::IsRoundedIconsEnabled() ? kTabIcon : kNewTabRefreshOldIcon);
  }
  AddItemWithStringIdAndVectorIcon(this, IDC_COPY_URL, IDS_COPY_URL,
                                   features::IsRoundedIconsEnabled()
                                       ? vector_icons::kLinkIcon
                                       : kLinkChromeRefreshOldIcon);

  if (ShouldAllowOpenInChrome(browser())) {
    AddItemWithStringIdAndVectorIcon(
        this, IDC_OPEN_IN_CHROME, IDS_OPEN_IN_CHROME,
        features::IsRoundedIconsEnabled() ? kChromeProductIcon
                                          : kBrowserLogoOldIcon);
  }

#if BUILDFLAG(IS_CHROMEOS)
  if (chromeos::MoveToDesksMenuDelegate::ShouldShowMoveToDesksMenu()) {
    AddSeparator(ui::NORMAL_SEPARATOR);
    move_to_desks_submenu_ = std::make_unique<chromeos::MoveToDesksMenuModel>(
        std::make_unique<chromeos::MoveToDesksMenuDelegate>(
            views::Widget::GetWidgetForNativeWindow(
                browser()->GetWindow()->GetNativeWindow())));
    AddSubMenuWithStringId(chromeos::MoveToDesksMenuModel::kMenuCommandId,
                           IDS_MOVE_TO_DESKS_MENU,
                           move_to_desks_submenu_.get());
  }
#endif

// Chrome OS's app list is prominent enough to not need a separate uninstall
// option in the app menu.
#if !BUILDFLAG(IS_CHROMEOS)
  DCHECK(app_browser_controller);
  if (app_browser_controller->IsInstalled()) {
    AddSeparator(ui::NORMAL_SEPARATOR);
    AddItemWithIcon(
        kUninstallAppCommandId,
        l10n_util::GetStringFUTF16(
            IDS_UNINSTALL_FROM_OS_LAUNCH_SURFACE,
            ui::EscapeMenuLabelAmpersands(
                app_browser_controller->GetAppShortName())),
        ui::ImageModel::FromVectorIcon(features::IsRoundedIconsEnabled()
                                           ? kDeleteIcon
                                           : kTrashCanRefreshOldIcon));
  }
#endif  // !BUILDFLAG(IS_CHROMEOS)
  AddSeparator(ui::NORMAL_SEPARATOR);
  CreateZoomMenu();
  AddSeparator(ui::NORMAL_SEPARATOR);
  AddItemWithStringIdAndVectorIcon(
      this, IDC_PRINT, IDS_PRINT,
      features::IsRoundedIconsEnabled() ? kPrintIcon : kPrintMenuOldIcon);
  CreateFindAndEditSubMenu();

  if (media_router::MediaRouterEnabled(browser()->GetProfile())) {
    AddItemWithStringIdAndVectorIcon(
        this, IDC_ROUTE_MEDIA, IDS_MEDIA_ROUTER_MENU_ITEM_TITLE,
        features::IsRoundedIconsEnabled() ? kCastIcon
                                          : kCastChromeRefreshOldIcon);
  }
}
