1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/ui/ash/launcher/app_window_launcher_controller.h" 6 7#include "apps/app_window.h" 8#include "ash/shelf/shelf_util.h" 9#include "ash/shell.h" 10#include "ash/wm/window_util.h" 11#include "base/strings/stringprintf.h" 12#include "chrome/browser/profiles/profile.h" 13#include "chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h" 14#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h" 15#include "chrome/browser/ui/ash/multi_user/multi_user_window_manager.h" 16#include "chrome/browser/ui/host_desktop.h" 17#include "extensions/common/extension.h" 18#include "ui/aura/window_event_dispatcher.h" 19#include "ui/wm/public/activation_client.h" 20 21using apps::AppWindow; 22 23namespace { 24 25std::string GetAppShelfId(AppWindow* app_window) { 26 if (app_window->window_type_is_panel()) 27 return base::StringPrintf("panel:%d", app_window->session_id().id()); 28 return app_window->extension_id(); 29} 30 31bool ControlsWindow(aura::Window* window) { 32 return chrome::GetHostDesktopTypeForNativeWindow(window) == 33 chrome::HOST_DESKTOP_TYPE_ASH; 34} 35 36} // namespace 37 38AppWindowLauncherController::AppWindowLauncherController( 39 ChromeLauncherController* owner) 40 : owner_(owner), activation_client_(NULL) { 41 apps::AppWindowRegistry* registry = 42 apps::AppWindowRegistry::Get(owner->profile()); 43 registry_.insert(registry); 44 registry->AddObserver(this); 45 if (ash::Shell::HasInstance()) { 46 if (ash::Shell::GetInstance()->GetPrimaryRootWindow()) { 47 activation_client_ = aura::client::GetActivationClient( 48 ash::Shell::GetInstance()->GetPrimaryRootWindow()); 49 if (activation_client_) 50 activation_client_->AddObserver(this); 51 } 52 } 53} 54 55AppWindowLauncherController::~AppWindowLauncherController() { 56 for (std::set<apps::AppWindowRegistry*>::iterator it = registry_.begin(); 57 it != registry_.end(); 58 ++it) 59 (*it)->RemoveObserver(this); 60 61 if (activation_client_) 62 activation_client_->RemoveObserver(this); 63 for (WindowToAppShelfIdMap::iterator iter = 64 window_to_app_shelf_id_map_.begin(); 65 iter != window_to_app_shelf_id_map_.end(); 66 ++iter) { 67 iter->first->RemoveObserver(this); 68 } 69} 70 71void AppWindowLauncherController::AdditionalUserAddedToSession( 72 Profile* profile) { 73 // TODO(skuhne): This was added for the legacy side by side mode in M32. If 74 // this mode gets no longer pursued this special case can be removed. 75 if (chrome::MultiUserWindowManager::GetMultiProfileMode() != 76 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_MIXED) 77 return; 78 79 apps::AppWindowRegistry* registry = apps::AppWindowRegistry::Get(profile); 80 if (registry_.find(registry) != registry_.end()) 81 return; 82 83 registry->AddObserver(this); 84 registry_.insert(registry); 85} 86 87void AppWindowLauncherController::OnAppWindowIconChanged( 88 AppWindow* app_window) { 89 if (!ControlsWindow(app_window->GetNativeWindow())) 90 return; 91 92 const std::string app_shelf_id = GetAppShelfId(app_window); 93 AppControllerMap::iterator iter = app_controller_map_.find(app_shelf_id); 94 if (iter == app_controller_map_.end()) 95 return; 96 AppWindowLauncherItemController* controller = iter->second; 97 controller->set_image_set_by_controller(true); 98 owner_->SetLauncherItemImage(controller->shelf_id(), 99 app_window->app_icon().AsImageSkia()); 100} 101 102void AppWindowLauncherController::OnAppWindowShown(AppWindow* app_window) { 103 aura::Window* window = app_window->GetNativeWindow(); 104 if (!ControlsWindow(window)) 105 return; 106 107 if (!IsRegisteredApp(window)) 108 RegisterApp(app_window); 109} 110 111void AppWindowLauncherController::OnAppWindowHidden(AppWindow* app_window) { 112 aura::Window* window = app_window->GetNativeWindow(); 113 if (!ControlsWindow(window)) 114 return; 115 116 if (IsRegisteredApp(window)) 117 UnregisterApp(window); 118} 119 120// Called from aura::Window::~Window(), before delegate_->OnWindowDestroyed() 121// which destroys AppWindow, so both |window| and the associated AppWindow 122// are valid here. 123void AppWindowLauncherController::OnWindowDestroying(aura::Window* window) { 124 if (!ControlsWindow(window)) 125 return; 126 UnregisterApp(window); 127} 128 129void AppWindowLauncherController::OnWindowActivated(aura::Window* new_active, 130 aura::Window* old_active) { 131 // Make the newly active window the active (first) entry in the controller. 132 AppWindowLauncherItemController* new_controller = 133 ControllerForWindow(new_active); 134 if (new_controller) { 135 new_controller->SetActiveWindow(new_active); 136 owner_->SetItemStatus(new_controller->shelf_id(), ash::STATUS_ACTIVE); 137 } 138 139 // Mark the old active window's launcher item as running (if different). 140 AppWindowLauncherItemController* old_controller = 141 ControllerForWindow(old_active); 142 if (old_controller && old_controller != new_controller) 143 owner_->SetItemStatus(old_controller->shelf_id(), ash::STATUS_RUNNING); 144} 145 146void AppWindowLauncherController::RegisterApp(AppWindow* app_window) { 147 aura::Window* window = app_window->GetNativeWindow(); 148 // Get the app's shelf identifier and add an entry to the map. 149 DCHECK(window_to_app_shelf_id_map_.find(window) == 150 window_to_app_shelf_id_map_.end()); 151 const std::string app_shelf_id = GetAppShelfId(app_window); 152 window_to_app_shelf_id_map_[window] = app_shelf_id; 153 window->AddObserver(this); 154 155 // Find or create an item controller and launcher item. 156 std::string app_id = app_window->extension_id(); 157 ash::ShelfItemStatus status = ash::wm::IsActiveWindow(window) 158 ? ash::STATUS_ACTIVE 159 : ash::STATUS_RUNNING; 160 AppControllerMap::iterator iter = app_controller_map_.find(app_shelf_id); 161 ash::ShelfID shelf_id = 0; 162 if (iter != app_controller_map_.end()) { 163 AppWindowLauncherItemController* controller = iter->second; 164 DCHECK(controller->app_id() == app_id); 165 shelf_id = controller->shelf_id(); 166 controller->AddAppWindow(app_window, status); 167 } else { 168 LauncherItemController::Type type = 169 app_window->window_type_is_panel() 170 ? LauncherItemController::TYPE_APP_PANEL 171 : LauncherItemController::TYPE_APP; 172 AppWindowLauncherItemController* controller = 173 new AppWindowLauncherItemController(type, app_shelf_id, app_id, owner_); 174 controller->AddAppWindow(app_window, status); 175 // If the app shelf id is not unique, and there is already a shelf 176 // item for this app id (e.g. pinned), use that shelf item. 177 if (app_shelf_id == app_id) 178 shelf_id = owner_->GetShelfIDForAppID(app_id); 179 if (shelf_id == 0) { 180 shelf_id = owner_->CreateAppLauncherItem(controller, app_id, status); 181 // Restore any existing app icon and flag as set. 182 const gfx::Image& app_icon = app_window->app_icon(); 183 if (!app_icon.IsEmpty()) { 184 owner_->SetLauncherItemImage(shelf_id, app_icon.AsImageSkia()); 185 controller->set_image_set_by_controller(true); 186 } 187 } else { 188 owner_->SetItemController(shelf_id, controller); 189 } 190 const std::string app_shelf_id = GetAppShelfId(app_window); 191 app_controller_map_[app_shelf_id] = controller; 192 } 193 owner_->SetItemStatus(shelf_id, status); 194 ash::SetShelfIDForWindow(shelf_id, window); 195} 196 197void AppWindowLauncherController::UnregisterApp(aura::Window* window) { 198 WindowToAppShelfIdMap::iterator iter1 = 199 window_to_app_shelf_id_map_.find(window); 200 DCHECK(iter1 != window_to_app_shelf_id_map_.end()); 201 std::string app_shelf_id = iter1->second; 202 window_to_app_shelf_id_map_.erase(iter1); 203 window->RemoveObserver(this); 204 205 AppControllerMap::iterator iter2 = app_controller_map_.find(app_shelf_id); 206 DCHECK(iter2 != app_controller_map_.end()); 207 AppWindowLauncherItemController* controller = iter2->second; 208 controller->RemoveAppWindowForWindow(window); 209 if (controller->app_window_count() == 0) { 210 // If this is the last window associated with the app shelf id, close the 211 // shelf item. 212 ash::ShelfID shelf_id = controller->shelf_id(); 213 owner_->CloseLauncherItem(shelf_id); 214 app_controller_map_.erase(iter2); 215 } 216} 217 218bool AppWindowLauncherController::IsRegisteredApp(aura::Window* window) { 219 return window_to_app_shelf_id_map_.find(window) != 220 window_to_app_shelf_id_map_.end(); 221} 222 223// Private Methods 224 225AppWindowLauncherItemController* 226AppWindowLauncherController::ControllerForWindow(aura::Window* window) { 227 WindowToAppShelfIdMap::iterator iter1 = 228 window_to_app_shelf_id_map_.find(window); 229 if (iter1 == window_to_app_shelf_id_map_.end()) 230 return NULL; 231 std::string app_shelf_id = iter1->second; 232 AppControllerMap::iterator iter2 = app_controller_map_.find(app_shelf_id); 233 if (iter2 == app_controller_map_.end()) 234 return NULL; 235 return iter2->second; 236} 237