#ChromeOS #Ash

Ash(Aura Shell)는 Aura를 기반으로 하는 쉘이다. Shell 클래스가 핵심적인 역할을 하는 것으로 보인다. Shell 객체는 싱글턴 객체이고 init 메소드를 통하여 컨트롤러라 불리우는 다른 객체를 여럿 생성하고 초기화한다. 아래는 배경화면에 관한 컨트롤러를 생성 및 초기화 하는 부분이다.

Ash::Shell::Init() {
   ...
  wallpaper_controller_ = std::make_unique<WallpaperControllerImpl>(local_state_);
   ...
}

싱글턴 객체인 Shell 객체를 전역적으로 참조하기 위하여 Shell 클래스는 정적 메소드인 Get 메소드를 제공한다. WallpaperControllerImpl 객체는 Shell 객체에 대하여 자기 자신을 옵저버로 등록한다. 또한, WindowTreeHostManager 객체에 대해서도 자기 자신을 옵저버로 등록한다.

WallpaperControllerImpl::WallpaperControllerImpl(PrefService* local_state) {
   ...
   Shell::Get()->window_tree_host_manager()->AddObserver(this);
    Shell::Get()->AddShellObserver(this);
   ...
}

void Shell::AddShellObserver(ShellObserver* observer) {
   shell_observers_.AddObserver(observer);
}

결국, 언젠가 Shell 객체의 OnRootWindowAdded 메서드가 호출되면 아래와 같은 콜 플로우로 CreateWallpaperWidget 메서드를 통해 배경화면을 품는 Widget 객체가 생성된다.

void Shell::OnRootWindowAdded(aura::Window* root_window);
void WallpaperControllerImpl::OnRootWindowAdded(...);
void WallpaperControllerImpl::InstallDesktopController(...);
views::Widget* CreateWallpaperWidget(...);

Shell 객체와 WallpaperController 객체는 싱글턴 패턴이 적용된 싱글턴 객체이다. 그리고 두 객체는 옵저버 패턴으로 관계를 맺고, 이 관계에서 Shell 객체는 관찰 대상, WallpaperController 객체는 옵저버임을 알 수 있다.

옵저버인 WallpaperController 객체의 OnRootWindowAdded 메서드는 관찰 대상인 Shell 객체로부터 무언가 변경 사항이 있었음을 통지받는 수단으로 호출된다. 메서드의 이름으로 미루어 변경 사항이란 “RootWindow가 생성되어 어느 리스트? 배열?에 추가될 때” 이다.

그런데 이상한 점은, 옵저버가 변경 사항을 통지받는 OnRootWindowAdded 메서드는 동일한 이름의 관찰 대상의 메서드라는 점이다. 여기서 코드를 보지 않고 유추해 볼 수 있는 건,

  1. Shell 클래스와 WallpaperController 클래스는 동일한 클래스를 상속받고 있을 수 있다는 것. 실제로 두 클래스는 ShellObserver 클래스의 자식 클래스이다. 심지어, non-primary root window?가 생성되었음을 관찰 대상이 옵저버로 통지한다는 사실을 주석을 통해서 알 수 있다.
class ASH_EXPORT ShellObserver {
 public:
    ...
    // Invoked after a non-primary root window is created.
    virtual void OnRootWindowAdded(aura::Window* root_window) { ... }
  1. Shell 객체는 WallpaperController 객체의 관찰 대상이면서 또 다른 어떤 객체에 대한 옵저버일 수 있다는 것. 실제로 Shell 객체는 RootWindowController 객체에 대한 옵저버이다. Shell 객체는 싱글턴 객체이므로 굳이 옵저버 배열을 사용하지 않고 직접적으로 변경 사항을 통지하고 있다. 판단문을 통해 PRIMARY 타입이 아닌 RootWindow 생성 시에만 통지하고 있다.
void RootWindowController::Init(RootWindowType root_window_type) {
   ...  
   if (root_window_type == RootWindowType::PRIMARY) {
   ...
   } else {
      // Notify shell observers about new root window.
      shell->OnRootWindowAdded(root_window);
  }
  ...