chromium/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_item_controller.cc

// Copyright 2019 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/ash/shelf/app_service/app_service_app_window_shelf_item_controller.h"

#include "chrome/browser/apps/app_service/app_service_proxy.h"
#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
#include "chrome/browser/ash/app_list/arc/arc_app_utils.h"
#include "chrome/browser/ash/arc/pip/arc_pip_bridge.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_controller.h"
#include "chrome/browser/ui/ash/shelf/app_window_base.h"
#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
#include "chromeos/ui/base/window_properties.h"
#include "chromeos/ui/base/window_state_type.h"
#include "components/favicon/content/content_favicon_driver.h"
#include "components/services/app_service/public/cpp/app_types.h"
#include "extensions/browser/app_window/app_window.h"
#include "extensions/browser/app_window/app_window_registry.h"
#include "extensions/browser/app_window/native_app_window.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/window.h"
#include "ui/gfx/image/image.h"

AppServiceAppWindowShelfItemController::AppServiceAppWindowShelfItemController(
    const ash::ShelfID& shelf_id,
    AppServiceAppWindowShelfController* controller)
    : AppWindowShelfItemController(shelf_id), controller_(controller) {
  DCHECK(controller_);
}

AppServiceAppWindowShelfItemController::
    ~AppServiceAppWindowShelfItemController() = default;

void AppServiceAppWindowShelfItemController::ItemSelected(
    std::unique_ptr<ui::Event> event,
    int64_t display_id,
    ash::ShelfLaunchSource source,
    ItemSelectedCallback callback,
    const ItemFilterPredicate& filter_predicate) {
  if (window_count()) {
    // Tapping the shelf icon of an app that's showing PIP means expanding PIP.
    // Even if the app contains multiple windows, we just expand PIP without
    // showing the menu on the shelf icon.
    for (AppWindowBase* window : windows()) {
      aura::Window* native_window = window->GetNativeWindow();
      if (native_window->GetProperty(chromeos::kWindowStateTypeKey) ==
          chromeos::WindowStateType::kPip) {
        Profile* profile = ChromeShelfController::instance()->profile();
        arc::ArcPipBridge* pip_bridge =
            arc::ArcPipBridge::GetForBrowserContext(profile);
        if (pip_bridge) {
          // ClosePip() actually expands PIP.
          pip_bridge->ClosePip();
          std::move(callback).Run(ash::SHELF_ACTION_NONE, {});
          return;
        }
      }
    }
    AppWindowShelfItemController::ItemSelected(std::move(event), display_id,
                                               source, std::move(callback),
                                               filter_predicate);
    return;
  }

  if (!task_ids_.empty()) {
    arc::SetTaskActive(*task_ids_.begin());
    std::move(callback).Run(ash::SHELF_ACTION_NEW_WINDOW_CREATED, {});
    return;
  }

  if (session_ids_.empty()) {
    NOTREACHED_IN_MIGRATION();
  }

  std::move(callback).Run(ash::SHELF_ACTION_NONE, {});
}

ash::ShelfItemDelegate::AppMenuItems
AppServiceAppWindowShelfItemController::GetAppMenuItems(
    int event_flags,
    const ItemFilterPredicate& filter_predicate) {
  if (!IsChromeApp()) {
    return AppWindowShelfItemController::GetAppMenuItems(event_flags,
                                                         filter_predicate);
  }

  // The window could be teleported from the inactive user's profile to the
  // current active user, so search all profiles.
  for (Profile* profile : controller_->GetProfileList()) {
    extensions::AppWindowRegistry* const app_window_registry =
        extensions::AppWindowRegistry::Get(profile);
    DCHECK(app_window_registry);

    AppMenuItems items;
    bool switch_profile = false;
    int command_id = -1;
    for (const ui::BaseWindow* window : windows()) {
      ++command_id;
      auto* native_window = window->GetNativeWindow();
      if (!filter_predicate.is_null() && !filter_predicate.Run(native_window))
        continue;

      extensions::AppWindow* const app_window =
          app_window_registry->GetAppWindowForNativeWindow(native_window);
      if (!app_window) {
        switch_profile = true;
        break;
      }

      // Use the app's web contents favicon, or the app window's icon.
      favicon::FaviconDriver* const favicon_driver =
          favicon::ContentFaviconDriver::FromWebContents(
              app_window->web_contents());
      DCHECK(favicon_driver);
      gfx::ImageSkia image = favicon_driver->GetFavicon().AsImageSkia();
      if (image.isNull()) {
        const gfx::ImageSkia* app_icon = nullptr;
        if (app_window->GetNativeWindow()) {
          app_icon = app_window->GetNativeWindow()->GetProperty(
              aura::client::kAppIconKey);
        }
        if (app_icon && !app_icon->isNull())
          image = *app_icon;
      }

      items.push_back({command_id, app_window->GetTitle(), image});
    }
    if (!switch_profile)
      return items;
  }
  return AppMenuItems();
}

void AppServiceAppWindowShelfItemController::OnWindowTitleChanged(
    aura::Window* window) {
  if (!IsChromeApp())
    return;

  ui::BaseWindow* const base_window =
      GetAppWindow(window, true /*include_hidden*/);

  // For Chrome apps, use the window title (if set) to differentiate
  // show_in_shelf window shelf items instead of the default behavior of using
  // the app name.
  //
  // The window could be teleported from the inactive user's profile to the
  // current active user, so search all profiles.
  for (Profile* profile : controller_->GetProfileList()) {
    extensions::AppWindowRegistry* const app_window_registry =
        extensions::AppWindowRegistry::Get(profile);
    DCHECK(app_window_registry);

    extensions::AppWindow* const app_window =
        app_window_registry->GetAppWindowForNativeWindow(
            base_window->GetNativeWindow());
    if (!app_window)
      continue;

    if (app_window->show_in_shelf()) {
      const std::u16string title = window->GetTitle();
      if (!title.empty())
        ChromeShelfController::instance()->SetItemTitle(shelf_id(), title);
    }
    return;
  }
}

void AppServiceAppWindowShelfItemController::AddTaskId(int task_id) {
  task_ids_.insert(task_id);
}

void AppServiceAppWindowShelfItemController::RemoveTaskId(int task_id) {
  task_ids_.erase(task_id);
}

bool AppServiceAppWindowShelfItemController::HasAnyTasks() const {
  return !task_ids_.empty();
}

void AppServiceAppWindowShelfItemController::AddSessionId(int session_id) {
  session_ids_.insert(session_id);
}

void AppServiceAppWindowShelfItemController::RemoveSessionId(int session_id) {
  session_ids_.erase(session_id);
}

bool AppServiceAppWindowShelfItemController::HasAnySessions() const {
  return !session_ids_.empty();
}

bool AppServiceAppWindowShelfItemController::IsChromeApp() {
  Profile* const profile = ChromeShelfController::instance()->profile();
  return apps::AppServiceProxyFactory::GetForProfile(profile)
             ->AppRegistryCache()
             .GetAppType(shelf_id().app_id) == apps::AppType::kChromeApp;
}