text: // Copyright 2021 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/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/ui/ash/shelf/standalone_browser_extension_app_shelf_item_controller.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);
  }
  
  void ChromeAppWindowTrackerAsh::OnAppWindowRemoved(
      const std::string& app_id,
      const std::string& window_id) {
    pending_window_ids_.erase(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
  