// Copyright 2021 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/ash/crosapi/chrome_app_window_tracker_ash.h"
#include <memory>
#include <utility>
#include "ash/public/cpp/shelf_model.h"
#include "chrome/browser/ash/crosapi/browser_util.h"
#include "chrome/browser/extensions/extension_keeplist_chromeos.h"
#include "chrome/browser/ui/ash/shelf/standalone_browser_extension_app_shelf_item_controller.h"
#include "components/app_restore/full_restore_utils.h"
#include "components/exo/shell_surface_util.h"
namespace crosapi {
ChromeAppWindowTrackerAsh::ChromeAppWindowTrackerAsh() {
env_observation_.Observe(aura::Env::GetInstance());
}
ChromeAppWindowTrackerAsh::~ChromeAppWindowTrackerAsh() = default;
void ChromeAppWindowTrackerAsh::BindReceiver(
mojo::PendingReceiver<mojom::AppWindowTracker> pending_receiver) {
receivers_.Add(this, std::move(pending_receiver));
}
void ChromeAppWindowTrackerAsh::OnAppWindowAdded(const std::string& app_id,
const std::string& window_id) {
pending_window_ids_[window_id].app_id = app_id;
CheckWindowNoLongerPending(window_id);
full_restore::OnLacrosChromeAppWindowAdded(app_id, window_id);
}
void ChromeAppWindowTrackerAsh::OnAppWindowRemoved(
const std::string& app_id,
const std::string& window_id) {
pending_window_ids_.erase(window_id);
full_restore::OnLacrosChromeAppWindowRemoved(app_id, window_id);
}
void ChromeAppWindowTrackerAsh::OnWindowInitialized(aura::Window* window) {
if (!crosapi::browser_util::IsLacrosWindow(window)) {
return;
}
std::string window_id = *exo::GetShellApplicationId(window);
// All Lacros windows get tracked, including non-app windows. Only app windows
// will become StandaloneBrowserExtensionAppShelfItemControllers.
pending_window_ids_[window_id].window = window;
window_observations_.AddObservation(window);
CheckWindowNoLongerPending(window_id);
}
void ChromeAppWindowTrackerAsh::OnWindowDestroying(aura::Window* window) {
auto it = pending_window_ids_.find(*exo::GetShellApplicationId(window));
if (it == pending_window_ids_.end())
return;
DCHECK_EQ(it->second.window, window);
window_observations_.RemoveObservation(window);
pending_window_ids_.erase(it);
}
void ChromeAppWindowTrackerAsh::UpdateShelf(const std::string& app_id,
aura::Window* window) {
ash::ShelfID shelf_id(app_id);
ash::ShelfItemDelegate* existing_delegate =
ash::ShelfModel::Get()->GetShelfItemDelegate(shelf_id);
if (existing_delegate) {
// If there's already a delegate in the shelf then it must be an instance of
// StandaloneBrowserExtensionAppShelfItemController.
auto* controller =
static_cast<StandaloneBrowserExtensionAppShelfItemController*>(
existing_delegate);
controller->StartTrackingInstance(window);
} else {
// Since there's no delegate or item in the shelf, we know the item isn't
// pinned. Therefore, the type must be TYPE_APP.
ash::ShelfItem item;
item.id = shelf_id;
item.type = ash::TYPE_APP;
auto delegate =
std::make_unique<StandaloneBrowserExtensionAppShelfItemController>(
shelf_id, window);
ash::ShelfModel::Get()->Add(item, std::move(delegate));
}
}
void ChromeAppWindowTrackerAsh::CheckWindowNoLongerPending(
const std::string& window_id) {
auto pending_window = pending_window_ids_.find(window_id);
if (pending_window == pending_window_ids_.end())
return;
// The window is still pending
if (pending_window->second.app_id.empty() || !pending_window->second.window)
return;
std::string app_id = std::move(pending_window->second.app_id);
aura::Window* window = pending_window->second.window;
// Now that both pieces of metadata are available, we can stop tracking the
// window.
pending_window_ids_.erase(pending_window);
window_observations_.RemoveObservation(window);
UpdateShelf(app_id, window);
}
} // namespace crosapi