| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h" | 5 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 // static |
| 8 ChromeLauncherController* ChromeLauncherController::instance_ = nullptr; |
| 8 | 9 |
| 9 #include <vector> | 10 ChromeLauncherController::~ChromeLauncherController() { |
| 10 | 11 if (instance_ == this) |
| 11 #include "ash/ash_switches.h" | 12 instance_ = nullptr; |
| 12 #include "ash/common/shelf/shelf_item_delegate_manager.h" | |
| 13 #include "ash/common/shelf/shelf_model.h" | |
| 14 #include "ash/common/system/tray/system_tray_delegate.h" | |
| 15 #include "ash/desktop_background/desktop_background_controller.h" | |
| 16 #include "ash/multi_profile_uma.h" | |
| 17 #include "ash/root_window_controller.h" | |
| 18 #include "ash/shelf/shelf.h" | |
| 19 #include "ash/shell.h" | |
| 20 #include "ash/wm/window_util.h" | |
| 21 #include "base/command_line.h" | |
| 22 #include "base/macros.h" | |
| 23 #include "base/strings/pattern.h" | |
| 24 #include "base/strings/string_util.h" | |
| 25 #include "base/strings/utf_string_conversions.h" | |
| 26 #include "base/values.h" | |
| 27 #include "build/build_config.h" | |
| 28 #include "chrome/browser/browser_process.h" | |
| 29 #include "chrome/browser/chrome_notification_types.h" | |
| 30 #include "chrome/browser/chromeos/arc/arc_support_host.h" | |
| 31 #include "chrome/browser/defaults.h" | |
| 32 #include "chrome/browser/extensions/extension_app_icon_loader.h" | |
| 33 #include "chrome/browser/extensions/extension_util.h" | |
| 34 #include "chrome/browser/extensions/launch_util.h" | |
| 35 #include "chrome/browser/prefs/incognito_mode_prefs.h" | |
| 36 #include "chrome/browser/profiles/profile.h" | |
| 37 #include "chrome/browser/profiles/profile_manager.h" | |
| 38 #include "chrome/browser/ui/app_list/arc/arc_app_icon_loader.h" | |
| 39 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h" | |
| 40 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h" | |
| 41 #include "chrome/browser/ui/ash/app_sync_ui_state.h" | |
| 42 #include "chrome/browser/ui/ash/chrome_launcher_prefs.h" | |
| 43 #include "chrome/browser/ui/ash/chrome_shell_delegate.h" | |
| 44 #include "chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.h
" | |
| 45 #include "chrome/browser/ui/ash/launcher/app_window_launcher_controller.h" | |
| 46 #include "chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h" | |
| 47 #include "chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_controller.h" | |
| 48 #include "chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.h" | |
| 49 #include "chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controll
er.h" | |
| 50 #include "chrome/browser/ui/ash/launcher/browser_status_monitor.h" | |
| 51 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h" | |
| 52 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_browser.h
" | |
| 53 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_tab.h" | |
| 54 #include "chrome/browser/ui/ash/launcher/chrome_launcher_types.h" | |
| 55 #include "chrome/browser/ui/ash/launcher/launcher_arc_app_updater.h" | |
| 56 #include "chrome/browser/ui/ash/launcher/launcher_controller_helper.h" | |
| 57 #include "chrome/browser/ui/ash/launcher/launcher_extension_app_updater.h" | |
| 58 #include "chrome/browser/ui/ash/launcher/launcher_item_controller.h" | |
| 59 #include "chrome/browser/ui/ash/launcher/multi_profile_app_window_launcher_contr
oller.h" | |
| 60 #include "chrome/browser/ui/ash/launcher/multi_profile_browser_status_monitor.h" | |
| 61 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h" | |
| 62 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager.h" | |
| 63 #include "chrome/browser/ui/browser.h" | |
| 64 #include "chrome/browser/ui/browser_finder.h" | |
| 65 #include "chrome/browser/ui/browser_list.h" | |
| 66 #include "chrome/browser/ui/browser_tabstrip.h" | |
| 67 #include "chrome/browser/ui/browser_window.h" | |
| 68 #include "chrome/browser/ui/tabs/tab_strip_model.h" | |
| 69 #include "chrome/browser/web_applications/web_app.h" | |
| 70 #include "chrome/common/chrome_switches.h" | |
| 71 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h" | |
| 72 #include "chrome/common/pref_names.h" | |
| 73 #include "chrome/common/url_constants.h" | |
| 74 #include "chrome/grit/generated_resources.h" | |
| 75 #include "components/favicon/content/content_favicon_driver.h" | |
| 76 #include "components/prefs/scoped_user_pref_update.h" | |
| 77 #include "components/signin/core/account_id/account_id.h" | |
| 78 #include "components/strings/grit/components_strings.h" | |
| 79 #include "components/user_manager/user_manager.h" | |
| 80 #include "content/public/browser/navigation_entry.h" | |
| 81 #include "content/public/browser/web_contents.h" | |
| 82 #include "extensions/browser/extension_prefs.h" | |
| 83 #include "extensions/browser/extension_registry.h" | |
| 84 #include "extensions/browser/extension_system.h" | |
| 85 #include "extensions/browser/extension_util.h" | |
| 86 #include "extensions/common/constants.h" | |
| 87 #include "extensions/common/extension.h" | |
| 88 #include "extensions/common/extension_resource.h" | |
| 89 #include "extensions/common/manifest_handlers/icons_handler.h" | |
| 90 #include "extensions/common/url_pattern.h" | |
| 91 #include "grit/ash_resources.h" | |
| 92 #include "grit/theme_resources.h" | |
| 93 #include "ui/aura/window.h" | |
| 94 #include "ui/aura/window_event_dispatcher.h" | |
| 95 #include "ui/base/l10n/l10n_util.h" | |
| 96 #include "ui/base/resource/resource_bundle.h" | |
| 97 #include "ui/base/window_open_disposition.h" | |
| 98 #include "ui/keyboard/keyboard_util.h" | |
| 99 #include "ui/resources/grit/ui_resources.h" | |
| 100 #include "ui/wm/core/window_animations.h" | |
| 101 | |
| 102 using extensions::Extension; | |
| 103 using extensions::UnloadedExtensionInfo; | |
| 104 using extension_misc::kGmailAppId; | |
| 105 using content::WebContents; | |
| 106 | |
| 107 // static | |
| 108 ChromeLauncherController* ChromeLauncherController::instance_ = NULL; | |
| 109 | |
| 110 namespace { | |
| 111 | |
| 112 int64_t GetDisplayIDForShelf(ash::Shelf* shelf) { | |
| 113 aura::Window* root_window = | |
| 114 shelf->shelf_widget()->GetNativeWindow()->GetRootWindow(); | |
| 115 display::Display display = | |
| 116 display::Screen::GetScreen()->GetDisplayNearestWindow(root_window); | |
| 117 DCHECK(display.is_valid()); | |
| 118 return display.id(); | |
| 119 } | 13 } |
| 120 | 14 |
| 121 /* | 15 ChromeLauncherController::ChromeLauncherController() {} |
| 122 * Return whether an app is pinned only by user. | |
| 123 * This function doesn't expect an app_id neither pinned by user nor by | |
| 124 * policy, the app_id in the arguments list MUST be pinned by either of | |
| 125 * those. Invalid input may lead to unexpected result. | |
| 126 * If this app is pinned by policy, but not by user, false is returned. | |
| 127 * If this app is pinned by both policy and user, false is returned. | |
| 128 * If this app is pinned not by policy, but by user, true is returned. | |
| 129 */ | |
| 130 bool IsAppForUserPinned(const std::string& app_id, | |
| 131 const base::ListValue* pinned_apps_pref, | |
| 132 const base::ListValue* policy_pinned_apps_pref) { | |
| 133 for (size_t index = 0; index < pinned_apps_pref->GetSize(); ++index) { | |
| 134 const base::DictionaryValue* app; | |
| 135 if (pinned_apps_pref->GetDictionary(index, &app)) { | |
| 136 std::string current_app_id; | |
| 137 bool pinned_by_policy = false; | |
| 138 if (app->GetString(ash::kPinnedAppsPrefAppIDPath, ¤t_app_id)) { | |
| 139 if (app_id == current_app_id) { | |
| 140 if (app->GetBoolean(ash::kPinnedAppsPrefPinnedByPolicy, | |
| 141 &pinned_by_policy) && | |
| 142 pinned_by_policy) { | |
| 143 // Pinned by policy in the past or present. | |
| 144 // Need to check policy_pinned_apps to determine | |
| 145 break; | |
| 146 } else { | |
| 147 // User Preference Already Pinned | |
| 148 return true; | |
| 149 } | |
| 150 } | |
| 151 } | |
| 152 } | |
| 153 } | |
| 154 for (size_t index = 0; index < policy_pinned_apps_pref->GetSize(); ++index) { | |
| 155 const base::DictionaryValue* app; | |
| 156 if (policy_pinned_apps_pref->GetDictionary(index, &app)) { | |
| 157 std::string app_id_; | |
| 158 if (app->GetString(ash::kPinnedAppsPrefAppIDPath, &app_id_)) { | |
| 159 // Only pinned by policy, which is not part of user-pinned | |
| 160 if (app_id == app_id_) | |
| 161 return false; | |
| 162 } | |
| 163 } | |
| 164 } | |
| 165 // Default, user added new pins | |
| 166 return true; | |
| 167 } | |
| 168 | |
| 169 const char* const kPinProhibitedExtensionIds[] = { | |
| 170 ArcSupportHost::kHostAppId, arc::kPlayStoreAppId, | |
| 171 }; | |
| 172 | |
| 173 const size_t kPinProhibitedExtensionIdsLength = | |
| 174 arraysize(kPinProhibitedExtensionIds); | |
| 175 | |
| 176 } // namespace | |
| 177 | |
| 178 // A class to get events from ChromeOS when a user gets changed or added. | |
| 179 class ChromeLauncherControllerUserSwitchObserver | |
| 180 : public user_manager::UserManager::UserSessionStateObserver { | |
| 181 public: | |
| 182 ChromeLauncherControllerUserSwitchObserver( | |
| 183 ChromeLauncherController* controller) | |
| 184 : controller_(controller) { | |
| 185 DCHECK(user_manager::UserManager::IsInitialized()); | |
| 186 user_manager::UserManager::Get()->AddSessionStateObserver(this); | |
| 187 } | |
| 188 ~ChromeLauncherControllerUserSwitchObserver() override { | |
| 189 user_manager::UserManager::Get()->RemoveSessionStateObserver(this); | |
| 190 } | |
| 191 | |
| 192 // user_manager::UserManager::UserSessionStateObserver overrides: | |
| 193 void UserAddedToSession(const user_manager::User* added_user) override; | |
| 194 | |
| 195 // ChromeLauncherControllerUserSwitchObserver: | |
| 196 void OnUserProfileReadyToSwitch(Profile* profile); | |
| 197 | |
| 198 private: | |
| 199 // Add a user to the session. | |
| 200 void AddUser(Profile* profile); | |
| 201 | |
| 202 // The owning ChromeLauncherController. | |
| 203 ChromeLauncherController* controller_; | |
| 204 | |
| 205 // Users which were just added to the system, but which profiles were not yet | |
| 206 // (fully) loaded. | |
| 207 std::set<std::string> added_user_ids_waiting_for_profiles_; | |
| 208 | |
| 209 DISALLOW_COPY_AND_ASSIGN(ChromeLauncherControllerUserSwitchObserver); | |
| 210 }; | |
| 211 | |
| 212 void ChromeLauncherControllerUserSwitchObserver::UserAddedToSession( | |
| 213 const user_manager::User* active_user) { | |
| 214 Profile* profile = | |
| 215 multi_user_util::GetProfileFromAccountId(active_user->GetAccountId()); | |
| 216 // If we do not have a profile yet, we postpone forwarding the notification | |
| 217 // until it is loaded. | |
| 218 if (!profile) | |
| 219 added_user_ids_waiting_for_profiles_.insert(active_user->email()); | |
| 220 else | |
| 221 AddUser(profile); | |
| 222 } | |
| 223 | |
| 224 void ChromeLauncherControllerUserSwitchObserver::OnUserProfileReadyToSwitch( | |
| 225 Profile* profile) { | |
| 226 if (!added_user_ids_waiting_for_profiles_.empty()) { | |
| 227 // Check if the profile is from a user which was on the waiting list. | |
| 228 std::string user_id = | |
| 229 multi_user_util::GetAccountIdFromProfile(profile).GetUserEmail(); | |
| 230 std::set<std::string>::iterator it = std::find( | |
| 231 added_user_ids_waiting_for_profiles_.begin(), | |
| 232 added_user_ids_waiting_for_profiles_.end(), | |
| 233 user_id); | |
| 234 if (it != added_user_ids_waiting_for_profiles_.end()) { | |
| 235 added_user_ids_waiting_for_profiles_.erase(it); | |
| 236 AddUser(profile->GetOriginalProfile()); | |
| 237 } | |
| 238 } | |
| 239 } | |
| 240 | |
| 241 void ChromeLauncherControllerUserSwitchObserver::AddUser(Profile* profile) { | |
| 242 if (chrome::MultiUserWindowManager::GetMultiProfileMode() == | |
| 243 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED) | |
| 244 chrome::MultiUserWindowManager::GetInstance()->AddUser(profile); | |
| 245 controller_->AdditionalUserAddedToSession(profile->GetOriginalProfile()); | |
| 246 } | |
| 247 | |
| 248 ChromeLauncherController::ChromeLauncherController(Profile* profile, | |
| 249 ash::ShelfModel* model) | |
| 250 : model_(model), | |
| 251 item_delegate_manager_(NULL), | |
| 252 profile_(profile), | |
| 253 app_sync_ui_state_(NULL), | |
| 254 ignore_persist_pinned_state_change_(false) { | |
| 255 if (!profile_) { | |
| 256 // If no profile was passed, we take the currently active profile and use it | |
| 257 // as the owner of the current desktop. | |
| 258 // Use the original profile as on chromeos we may get a temporary off the | |
| 259 // record profile, unless in guest session (where off the record profile is | |
| 260 // the right one). | |
| 261 profile_ = ProfileManager::GetActiveUserProfile(); | |
| 262 if (!profile_->IsGuestSession() && !profile_->IsSystemProfile()) | |
| 263 profile_ = profile_->GetOriginalProfile(); | |
| 264 | |
| 265 app_sync_ui_state_ = AppSyncUIState::Get(profile_); | |
| 266 if (app_sync_ui_state_) | |
| 267 app_sync_ui_state_->AddObserver(this); | |
| 268 } | |
| 269 | |
| 270 if (arc::ArcAuthService::IsAllowedForProfile(profile_)) { | |
| 271 arc_deferred_launcher_.reset(new ArcAppDeferredLauncherController(this)); | |
| 272 } | |
| 273 | |
| 274 // All profile relevant settings get bound to the current profile. | |
| 275 AttachProfile(profile_); | |
| 276 model_->AddObserver(this); | |
| 277 | |
| 278 // In multi profile mode we might have a window manager. We try to create it | |
| 279 // here. If the instantiation fails, the manager is not needed. | |
| 280 chrome::MultiUserWindowManager::CreateInstance(); | |
| 281 | |
| 282 // On Chrome OS using multi profile we want to switch the content of the shelf | |
| 283 // with a user change. Note that for unit tests the instance can be NULL. | |
| 284 if (chrome::MultiUserWindowManager::GetMultiProfileMode() != | |
| 285 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_OFF) { | |
| 286 user_switch_observer_.reset( | |
| 287 new ChromeLauncherControllerUserSwitchObserver(this)); | |
| 288 } | |
| 289 | |
| 290 std::unique_ptr<AppWindowLauncherController> extension_app_window_controller; | |
| 291 // Create our v1/v2 application / browser monitors which will inform the | |
| 292 // launcher of status changes. | |
| 293 if (chrome::MultiUserWindowManager::GetMultiProfileMode() == | |
| 294 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED) { | |
| 295 // If running in separated destkop mode, we create the multi profile version | |
| 296 // of status monitor. | |
| 297 browser_status_monitor_.reset(new MultiProfileBrowserStatusMonitor(this)); | |
| 298 extension_app_window_controller.reset( | |
| 299 new MultiProfileAppWindowLauncherController(this)); | |
| 300 } else { | |
| 301 // Create our v1/v2 application / browser monitors which will inform the | |
| 302 // launcher of status changes. | |
| 303 browser_status_monitor_.reset(new BrowserStatusMonitor(this)); | |
| 304 extension_app_window_controller.reset( | |
| 305 new ExtensionAppWindowLauncherController(this)); | |
| 306 } | |
| 307 app_window_controllers_.push_back(std::move(extension_app_window_controller)); | |
| 308 | |
| 309 std::unique_ptr<AppWindowLauncherController> arc_app_window_controller; | |
| 310 arc_app_window_controller.reset(new ArcAppWindowLauncherController(this)); | |
| 311 app_window_controllers_.push_back(std::move(arc_app_window_controller)); | |
| 312 | |
| 313 // Right now ash::Shell isn't created for tests. | |
| 314 // TODO(mukai): Allows it to observe display change and write tests. | |
| 315 if (ash::Shell::HasInstance()) { | |
| 316 ash::Shell::GetInstance()->window_tree_host_manager()->AddObserver(this); | |
| 317 // If it got already set, we remove the observer first again and swap the | |
| 318 // ItemDelegateManager. | |
| 319 if (item_delegate_manager_) | |
| 320 item_delegate_manager_->RemoveObserver(this); | |
| 321 item_delegate_manager_ = | |
| 322 ash::Shell::GetInstance()->shelf_item_delegate_manager(); | |
| 323 item_delegate_manager_->AddObserver(this); | |
| 324 } | |
| 325 } | |
| 326 | |
| 327 ChromeLauncherController::~ChromeLauncherController() { | |
| 328 if (item_delegate_manager_) | |
| 329 item_delegate_manager_->RemoveObserver(this); | |
| 330 | |
| 331 // Reset the BrowserStatusMonitor as it has a weak pointer to this. | |
| 332 browser_status_monitor_.reset(); | |
| 333 | |
| 334 // Reset the app window controllers here since it has a weak pointer to this. | |
| 335 app_window_controllers_.clear(); | |
| 336 | |
| 337 model_->RemoveObserver(this); | |
| 338 if (ash::Shell::HasInstance()) | |
| 339 ash::Shell::GetInstance()->window_tree_host_manager()->RemoveObserver(this); | |
| 340 for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin(); | |
| 341 i != id_to_item_controller_map_.end(); ++i) { | |
| 342 int index = model_->ItemIndexByID(i->first); | |
| 343 // A "browser proxy" is not known to the model and this removal does | |
| 344 // therefore not need to be propagated to the model. | |
| 345 if (index != -1 && | |
| 346 model_->items()[index].type != ash::TYPE_BROWSER_SHORTCUT) | |
| 347 model_->RemoveItemAt(index); | |
| 348 } | |
| 349 | |
| 350 // Release all profile dependent resources. | |
| 351 ReleaseProfile(); | |
| 352 if (instance_ == this) | |
| 353 instance_ = NULL; | |
| 354 | |
| 355 // Get rid of the multi user window manager instance. | |
| 356 chrome::MultiUserWindowManager::DeleteInstance(); | |
| 357 } | |
| 358 | |
| 359 // static | |
| 360 ChromeLauncherController* ChromeLauncherController::CreateInstance( | |
| 361 Profile* profile, | |
| 362 ash::ShelfModel* model) { | |
| 363 // We do not check here for re-creation of the ChromeLauncherController since | |
| 364 // it appears that it might be intentional that the ChromeLauncherController | |
| 365 // can be re-created. | |
| 366 instance_ = new ChromeLauncherController(profile, model); | |
| 367 return instance_; | |
| 368 } | |
| 369 | |
| 370 void ChromeLauncherController::Init() { | |
| 371 CreateBrowserShortcutLauncherItem(); | |
| 372 UpdateAppLaunchersFromPref(); | |
| 373 | |
| 374 // TODO(sky): update unit test so that this test isn't necessary. | |
| 375 if (ash::Shell::HasInstance()) | |
| 376 SetVirtualKeyboardBehaviorFromPrefs(); | |
| 377 | |
| 378 prefs_observer_ = | |
| 379 ash::ChromeLauncherPrefsObserver::CreateIfNecessary(profile_); | |
| 380 } | |
| 381 | |
| 382 ash::ShelfID ChromeLauncherController::CreateAppLauncherItem( | |
| 383 LauncherItemController* controller, | |
| 384 const std::string& app_id, | |
| 385 ash::ShelfItemStatus status) { | |
| 386 CHECK(controller); | |
| 387 int index = 0; | |
| 388 // Panels are inserted on the left so as not to push all existing panels over. | |
| 389 if (controller->GetShelfItemType() != ash::TYPE_APP_PANEL) | |
| 390 index = model_->item_count(); | |
| 391 return InsertAppLauncherItem(controller, | |
| 392 app_id, | |
| 393 status, | |
| 394 index, | |
| 395 controller->GetShelfItemType()); | |
| 396 } | |
| 397 | |
| 398 void ChromeLauncherController::SetItemStatus(ash::ShelfID id, | |
| 399 ash::ShelfItemStatus status) { | |
| 400 int index = model_->ItemIndexByID(id); | |
| 401 ash::ShelfItemStatus old_status = model_->items()[index].status; | |
| 402 // Since ordinary browser windows are not registered, we might get a negative | |
| 403 // index here. | |
| 404 if (index >= 0 && old_status != status) { | |
| 405 ash::ShelfItem item = model_->items()[index]; | |
| 406 item.status = status; | |
| 407 model_->Set(index, item); | |
| 408 } | |
| 409 } | |
| 410 | |
| 411 void ChromeLauncherController::SetItemController( | |
| 412 ash::ShelfID id, | |
| 413 LauncherItemController* controller) { | |
| 414 CHECK(controller); | |
| 415 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id); | |
| 416 CHECK(iter != id_to_item_controller_map_.end()); | |
| 417 controller->set_shelf_id(id); | |
| 418 iter->second = controller; | |
| 419 // Existing controller is destroyed and replaced by registering again. | |
| 420 SetShelfItemDelegate(id, controller); | |
| 421 } | |
| 422 | |
| 423 void ChromeLauncherController::CloseLauncherItem(ash::ShelfID id) { | |
| 424 CHECK(id); | |
| 425 if (IsPinned(id)) { | |
| 426 // Create a new shortcut controller. | |
| 427 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id); | |
| 428 CHECK(iter != id_to_item_controller_map_.end()); | |
| 429 SetItemStatus(id, ash::STATUS_CLOSED); | |
| 430 std::string app_id = iter->second->app_id(); | |
| 431 iter->second = AppShortcutLauncherItemController::Create(app_id, this); | |
| 432 iter->second->set_shelf_id(id); | |
| 433 // Existing controller is destroyed and replaced by registering again. | |
| 434 SetShelfItemDelegate(id, iter->second); | |
| 435 } else { | |
| 436 LauncherItemClosed(id); | |
| 437 } | |
| 438 } | |
| 439 | |
| 440 AppListControllerDelegate::Pinnable ChromeLauncherController::GetPinnable( | |
| 441 const std::string& app_id) { | |
| 442 for (size_t i = 0; i < kPinProhibitedExtensionIdsLength; ++i) { | |
| 443 if (kPinProhibitedExtensionIds[i] == app_id) | |
| 444 return AppListControllerDelegate::NO_PIN; | |
| 445 } | |
| 446 | |
| 447 const base::ListValue* pref = | |
| 448 profile_->GetPrefs()->GetList(prefs::kPolicyPinnedLauncherApps); | |
| 449 if (!pref) | |
| 450 return AppListControllerDelegate::PIN_EDITABLE; | |
| 451 | |
| 452 // Pinned ARC apps policy defines the package name of the apps, that must | |
| 453 // be pinned. All the launch activities of any package in policy are pinned. | |
| 454 // In turn the input parameter to this function is app_id, which | |
| 455 // is 32 chars hash. In case of ARC app this is a hash of | |
| 456 // (package name + activity). This means that we must identify the package | |
| 457 // from the hash, and check if this package is pinned by policy. | |
| 458 const ArcAppListPrefs* const arc_prefs = ArcAppListPrefs::Get(profile()); | |
| 459 std::string arc_app_packege_name; | |
| 460 if (arc_prefs) { | |
| 461 std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = | |
| 462 arc_prefs->GetApp(app_id); | |
| 463 if (app_info) | |
| 464 arc_app_packege_name = app_info->package_name; | |
| 465 } | |
| 466 | |
| 467 for (size_t index = 0; index < pref->GetSize(); ++index) { | |
| 468 const base::DictionaryValue* app = nullptr; | |
| 469 std::string app_id_or_package; | |
| 470 if (pref->GetDictionary(index, &app) && | |
| 471 app->GetString(ash::kPinnedAppsPrefAppIDPath, &app_id_or_package) && | |
| 472 (app_id == app_id_or_package || | |
| 473 arc_app_packege_name == app_id_or_package)) { | |
| 474 return AppListControllerDelegate::PIN_FIXED; | |
| 475 } | |
| 476 } | |
| 477 return AppListControllerDelegate::PIN_EDITABLE; | |
| 478 } | |
| 479 | |
| 480 void ChromeLauncherController::Pin(ash::ShelfID id) { | |
| 481 DCHECK(HasShelfIDToAppIDMapping(id)); | |
| 482 | |
| 483 int index = model_->ItemIndexByID(id); | |
| 484 DCHECK_GE(index, 0); | |
| 485 | |
| 486 ash::ShelfItem item = model_->items()[index]; | |
| 487 | |
| 488 if (item.type == ash::TYPE_PLATFORM_APP || | |
| 489 item.type == ash::TYPE_WINDOWED_APP) { | |
| 490 item.type = ash::TYPE_APP_SHORTCUT; | |
| 491 model_->Set(index, item); | |
| 492 } else if (item.type != ash::TYPE_APP_SHORTCUT) { | |
| 493 return; | |
| 494 } | |
| 495 | |
| 496 if (GetLauncherItemController(id)->CanPin()) | |
| 497 PersistPinnedState(); | |
| 498 } | |
| 499 | |
| 500 void ChromeLauncherController::Unpin(ash::ShelfID id) { | |
| 501 LauncherItemController* controller = GetLauncherItemController(id); | |
| 502 CHECK(controller); | |
| 503 const bool can_pin = controller->CanPin(); | |
| 504 | |
| 505 if (controller->type() == LauncherItemController::TYPE_APP || | |
| 506 controller->locked()) { | |
| 507 UnpinRunningAppInternal(model_->ItemIndexByID(id)); | |
| 508 } else { | |
| 509 LauncherItemClosed(id); | |
| 510 } | |
| 511 if (can_pin) | |
| 512 PersistPinnedState(); | |
| 513 } | |
| 514 | |
| 515 bool ChromeLauncherController::IsPinned(ash::ShelfID id) { | |
| 516 int index = model_->ItemIndexByID(id); | |
| 517 if (index < 0) | |
| 518 return false; | |
| 519 ash::ShelfItemType type = model_->items()[index].type; | |
| 520 return (type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_BROWSER_SHORTCUT); | |
| 521 } | |
| 522 | |
| 523 void ChromeLauncherController::TogglePinned(ash::ShelfID id) { | |
| 524 if (!HasShelfIDToAppIDMapping(id)) | |
| 525 return; // May happen if item closed with menu open. | |
| 526 | |
| 527 if (IsPinned(id)) | |
| 528 Unpin(id); | |
| 529 else | |
| 530 Pin(id); | |
| 531 } | |
| 532 | |
| 533 bool ChromeLauncherController::IsPinnable(ash::ShelfID id) const { | |
| 534 int index = model_->ItemIndexByID(id); | |
| 535 if (index == -1) | |
| 536 return false; | |
| 537 | |
| 538 ash::ShelfItemType type = model_->items()[index].type; | |
| 539 std::string app_id; | |
| 540 return ((type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_PLATFORM_APP || | |
| 541 type == ash::TYPE_WINDOWED_APP) && | |
| 542 item_delegate_manager_->GetShelfItemDelegate(id)->CanPin()); | |
| 543 } | |
| 544 | |
| 545 void ChromeLauncherController::LockV1AppWithID(const std::string& app_id) { | |
| 546 ash::ShelfID id = GetShelfIDForAppID(app_id); | |
| 547 if (!IsPinned(id) && !IsWindowedAppInLauncher(app_id)) { | |
| 548 CreateAppShortcutLauncherItemWithType(app_id, | |
| 549 model_->item_count(), | |
| 550 ash::TYPE_WINDOWED_APP); | |
| 551 id = GetShelfIDForAppID(app_id); | |
| 552 } | |
| 553 CHECK(id); | |
| 554 id_to_item_controller_map_[id]->lock(); | |
| 555 } | |
| 556 | |
| 557 void ChromeLauncherController::UnlockV1AppWithID(const std::string& app_id) { | |
| 558 ash::ShelfID id = GetShelfIDForAppID(app_id); | |
| 559 CHECK(id); | |
| 560 CHECK(IsPinned(id) || IsWindowedAppInLauncher(app_id)); | |
| 561 LauncherItemController* controller = id_to_item_controller_map_[id]; | |
| 562 controller->unlock(); | |
| 563 if (!controller->locked() && !IsPinned(id)) | |
| 564 CloseLauncherItem(id); | |
| 565 } | |
| 566 | |
| 567 void ChromeLauncherController::Launch(ash::ShelfID id, int event_flags) { | |
| 568 LauncherItemController* controller = GetLauncherItemController(id); | |
| 569 if (!controller) | |
| 570 return; // In case invoked from menu and item closed while menu up. | |
| 571 controller->Launch(ash::LAUNCH_FROM_UNKNOWN, event_flags); | |
| 572 } | |
| 573 | |
| 574 void ChromeLauncherController::Close(ash::ShelfID id) { | |
| 575 LauncherItemController* controller = GetLauncherItemController(id); | |
| 576 if (!controller) | |
| 577 return; // May happen if menu closed. | |
| 578 controller->Close(); | |
| 579 } | |
| 580 | |
| 581 bool ChromeLauncherController::IsOpen(ash::ShelfID id) { | |
| 582 LauncherItemController* controller = GetLauncherItemController(id); | |
| 583 if (!controller) | |
| 584 return false; | |
| 585 return controller->IsOpen(); | |
| 586 } | |
| 587 | |
| 588 bool ChromeLauncherController::IsPlatformApp(ash::ShelfID id) { | |
| 589 if (!HasShelfIDToAppIDMapping(id)) | |
| 590 return false; | |
| 591 | |
| 592 std::string app_id = GetAppIDForShelfID(id); | |
| 593 const Extension* extension = GetExtensionForAppID(app_id); | |
| 594 // An extension can be synced / updated at any time and therefore not be | |
| 595 // available. | |
| 596 return extension ? extension->is_platform_app() : false; | |
| 597 } | |
| 598 | |
| 599 void ChromeLauncherController::LaunchApp(const std::string& app_id, | |
| 600 ash::LaunchSource source, | |
| 601 int event_flags) { | |
| 602 launcher_controller_helper_->LaunchApp(app_id, source, event_flags); | |
| 603 } | |
| 604 | |
| 605 void ChromeLauncherController::ActivateApp(const std::string& app_id, | |
| 606 ash::LaunchSource source, | |
| 607 int event_flags) { | |
| 608 // If there is an existing non-shortcut controller for this app, open it. | |
| 609 ash::ShelfID id = GetShelfIDForAppID(app_id); | |
| 610 if (id) { | |
| 611 LauncherItemController* controller = GetLauncherItemController(id); | |
| 612 controller->Activate(source); | |
| 613 return; | |
| 614 } | |
| 615 | |
| 616 // Create a temporary application launcher item and use it to see if there are | |
| 617 // running instances. | |
| 618 std::unique_ptr<AppShortcutLauncherItemController> app_controller( | |
| 619 AppShortcutLauncherItemController::Create(app_id, this)); | |
| 620 if (!app_controller->GetRunningApplications().empty()) | |
| 621 app_controller->Activate(source); | |
| 622 else | |
| 623 LaunchApp(app_id, source, event_flags); | |
| 624 } | |
| 625 | |
| 626 extensions::LaunchType ChromeLauncherController::GetLaunchType( | |
| 627 ash::ShelfID id) { | |
| 628 const Extension* extension = GetExtensionForAppID(GetAppIDForShelfID(id)); | |
| 629 | |
| 630 // An extension can be unloaded/updated/unavailable at any time. | |
| 631 if (!extension) | |
| 632 return extensions::LAUNCH_TYPE_DEFAULT; | |
| 633 | |
| 634 return extensions::GetLaunchType(extensions::ExtensionPrefs::Get(profile_), | |
| 635 extension); | |
| 636 } | |
| 637 | |
| 638 ash::ShelfID ChromeLauncherController::GetShelfIDForAppID( | |
| 639 const std::string& app_id) { | |
| 640 for (IDToItemControllerMap::const_iterator i = | |
| 641 id_to_item_controller_map_.begin(); | |
| 642 i != id_to_item_controller_map_.end(); ++i) { | |
| 643 if (i->second->type() == LauncherItemController::TYPE_APP_PANEL) | |
| 644 continue; // Don't include panels | |
| 645 if (i->second->app_id() == app_id) | |
| 646 return i->first; | |
| 647 } | |
| 648 return 0; | |
| 649 } | |
| 650 | |
| 651 bool ChromeLauncherController::HasShelfIDToAppIDMapping(ash::ShelfID id) const { | |
| 652 return id_to_item_controller_map_.find(id) != | |
| 653 id_to_item_controller_map_.end(); | |
| 654 } | |
| 655 | |
| 656 const std::string& ChromeLauncherController::GetAppIDForShelfID( | |
| 657 ash::ShelfID id) { | |
| 658 LauncherItemController* controller = GetLauncherItemController(id); | |
| 659 return controller ? controller->app_id() : base::EmptyString(); | |
| 660 } | |
| 661 | |
| 662 void ChromeLauncherController::OnAppImageUpdated(const std::string& id, | |
| 663 const gfx::ImageSkia& image) { | |
| 664 // TODO: need to get this working for shortcuts. | |
| 665 for (IDToItemControllerMap::const_iterator i = | |
| 666 id_to_item_controller_map_.begin(); | |
| 667 i != id_to_item_controller_map_.end(); ++i) { | |
| 668 LauncherItemController* controller = i->second; | |
| 669 if (controller->app_id() != id) | |
| 670 continue; | |
| 671 if (controller->image_set_by_controller()) | |
| 672 continue; | |
| 673 int index = model_->ItemIndexByID(i->first); | |
| 674 if (index == -1) | |
| 675 continue; | |
| 676 ash::ShelfItem item = model_->items()[index]; | |
| 677 item.image = image; | |
| 678 model_->Set(index, item); | |
| 679 // It's possible we're waiting on more than one item, so don't break. | |
| 680 } | |
| 681 } | |
| 682 | |
| 683 void ChromeLauncherController::SetLauncherItemImage( | |
| 684 ash::ShelfID shelf_id, | |
| 685 const gfx::ImageSkia& image) { | |
| 686 int index = model_->ItemIndexByID(shelf_id); | |
| 687 if (index == -1) | |
| 688 return; | |
| 689 ash::ShelfItem item = model_->items()[index]; | |
| 690 item.image = image; | |
| 691 model_->Set(index, item); | |
| 692 } | |
| 693 | |
| 694 bool ChromeLauncherController::IsAppPinned(const std::string& app_id) { | |
| 695 for (IDToItemControllerMap::const_iterator i = | |
| 696 id_to_item_controller_map_.begin(); | |
| 697 i != id_to_item_controller_map_.end(); ++i) { | |
| 698 if (IsPinned(i->first) && i->second->app_id() == app_id) | |
| 699 return true; | |
| 700 } | |
| 701 return false; | |
| 702 } | |
| 703 | |
| 704 bool ChromeLauncherController::IsWindowedAppInLauncher( | |
| 705 const std::string& app_id) { | |
| 706 int index = model_->ItemIndexByID(GetShelfIDForAppID(app_id)); | |
| 707 if (index < 0) | |
| 708 return false; | |
| 709 | |
| 710 ash::ShelfItemType type = model_->items()[index].type; | |
| 711 return type == ash::TYPE_WINDOWED_APP; | |
| 712 } | |
| 713 | |
| 714 void ChromeLauncherController::PinAppWithID(const std::string& app_id) { | |
| 715 if (GetPinnable(app_id) == AppListControllerDelegate::PIN_EDITABLE) | |
| 716 DoPinAppWithID(app_id); | |
| 717 else | |
| 718 NOTREACHED(); | |
| 719 } | |
| 720 | |
| 721 void ChromeLauncherController::SetLaunchType( | |
| 722 ash::ShelfID id, | |
| 723 extensions::LaunchType launch_type) { | |
| 724 LauncherItemController* controller = GetLauncherItemController(id); | |
| 725 if (!controller) | |
| 726 return; | |
| 727 | |
| 728 extensions::SetLaunchType(profile_, controller->app_id(), launch_type); | |
| 729 } | |
| 730 | |
| 731 void ChromeLauncherController::UnpinAppWithID(const std::string& app_id) { | |
| 732 if (GetPinnable(app_id) == AppListControllerDelegate::PIN_EDITABLE) | |
| 733 DoUnpinAppWithID(app_id); | |
| 734 else | |
| 735 NOTREACHED(); | |
| 736 } | |
| 737 | |
| 738 void ChromeLauncherController::OnSetShelfItemDelegate( | |
| 739 ash::ShelfID id, | |
| 740 ash::ShelfItemDelegate* item_delegate) { | |
| 741 // TODO(skuhne): This fixes crbug.com/429870, but it does not answer why we | |
| 742 // get into this state in the first place. | |
| 743 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id); | |
| 744 if (iter == id_to_item_controller_map_.end() || item_delegate == iter->second) | |
| 745 return; | |
| 746 LOG(ERROR) << "Unexpected change of shelf item id: " << id; | |
| 747 id_to_item_controller_map_.erase(iter); | |
| 748 } | |
| 749 | |
| 750 void ChromeLauncherController::PersistPinnedState() { | |
| 751 if (ignore_persist_pinned_state_change_) | |
| 752 return; | |
| 753 // It is a coding error to call PersistPinnedState() if the pinned apps are | |
| 754 // not user-editable. The code should check earlier and not perform any | |
| 755 // modification actions that trigger persisting the state. | |
| 756 // Mutating kPinnedLauncherApps is going to notify us and trigger us to | |
| 757 // process the change. We don't want that to happen so remove ourselves as a | |
| 758 // listener. | |
| 759 pref_change_registrar_.Remove(prefs::kPinnedLauncherApps); | |
| 760 { | |
| 761 std::unique_ptr<const base::ListValue> pinned_apps_pref = | |
| 762 profile_->GetPrefs() | |
| 763 ->GetList(prefs::kPinnedLauncherApps) | |
| 764 ->CreateDeepCopy(); | |
| 765 | |
| 766 const base::ListValue* policy_pinned_apps_pref = | |
| 767 profile_->GetPrefs()->GetList(prefs::kPolicyPinnedLauncherApps); | |
| 768 | |
| 769 ListPrefUpdate updater(profile_->GetPrefs(), prefs::kPinnedLauncherApps); | |
| 770 updater->Clear(); | |
| 771 for (size_t i = 0; i < model_->items().size(); ++i) { | |
| 772 if (model_->items()[i].type == ash::TYPE_APP_SHORTCUT) { | |
| 773 ash::ShelfID id = model_->items()[i].id; | |
| 774 LauncherItemController* controller = GetLauncherItemController(id); | |
| 775 // Don't persist pinning state for apps that are handled internally and | |
| 776 // have pinnable state AppListControllerDelegate::NO_PIN. | |
| 777 if (controller && IsPinned(id) && | |
| 778 GetPinnable(controller->app_id()) != | |
| 779 AppListControllerDelegate::NO_PIN) { | |
| 780 base::DictionaryValue* app_value = ash::CreateAppDict( | |
| 781 controller->app_id()); | |
| 782 if (app_value) { | |
| 783 if (!IsAppForUserPinned(controller->app_id(), | |
| 784 pinned_apps_pref.get(), | |
| 785 policy_pinned_apps_pref)) | |
| 786 app_value->SetBoolean(ash::kPinnedAppsPrefPinnedByPolicy, true); | |
| 787 updater->Append(app_value); | |
| 788 } | |
| 789 } | |
| 790 } else if (model_->items()[i].type == ash::TYPE_BROWSER_SHORTCUT) { | |
| 791 PersistChromeItemIndex(i); | |
| 792 } else if (model_->items()[i].type == ash::TYPE_APP_LIST) { | |
| 793 base::DictionaryValue* app_value = | |
| 794 ash::CreateAppDict(ash::kPinnedAppsPlaceholder); | |
| 795 if (app_value) | |
| 796 updater->Append(app_value); | |
| 797 } | |
| 798 } | |
| 799 } | |
| 800 pref_change_registrar_.Add( | |
| 801 prefs::kPinnedLauncherApps, | |
| 802 base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref, | |
| 803 base::Unretained(this))); | |
| 804 } | |
| 805 | |
| 806 Profile* ChromeLauncherController::profile() { | |
| 807 return profile_; | |
| 808 } | |
| 809 | |
| 810 void ChromeLauncherController::UpdateAppState(content::WebContents* contents, | |
| 811 AppState app_state) { | |
| 812 std::string app_id = launcher_controller_helper_->GetAppID(contents); | |
| 813 | |
| 814 // Check if the gMail app is loaded and it matches the given content. | |
| 815 // This special treatment is needed to address crbug.com/234268. | |
| 816 if (app_id.empty() && ContentCanBeHandledByGmailApp(contents)) | |
| 817 app_id = kGmailAppId; | |
| 818 | |
| 819 // Check the old |app_id| for a tab. If the contents has changed we need to | |
| 820 // remove it from the previous app. | |
| 821 if (web_contents_to_app_id_.find(contents) != web_contents_to_app_id_.end()) { | |
| 822 std::string last_app_id = web_contents_to_app_id_[contents]; | |
| 823 if (last_app_id != app_id) { | |
| 824 ash::ShelfID id = GetShelfIDForAppID(last_app_id); | |
| 825 if (id) { | |
| 826 // Since GetAppState() will use |web_contents_to_app_id_| we remove | |
| 827 // the connection before calling it. | |
| 828 web_contents_to_app_id_.erase(contents); | |
| 829 SetItemStatus(id, GetAppState(last_app_id)); | |
| 830 } | |
| 831 } | |
| 832 } | |
| 833 | |
| 834 if (app_state == APP_STATE_REMOVED) | |
| 835 web_contents_to_app_id_.erase(contents); | |
| 836 else | |
| 837 web_contents_to_app_id_[contents] = app_id; | |
| 838 | |
| 839 ash::ShelfID id = GetShelfIDForAppID(app_id); | |
| 840 if (id) { | |
| 841 SetItemStatus(id, (app_state == APP_STATE_WINDOW_ACTIVE || | |
| 842 app_state == APP_STATE_ACTIVE) ? ash::STATUS_ACTIVE : | |
| 843 GetAppState(app_id)); | |
| 844 } | |
| 845 } | |
| 846 | |
| 847 ash::ShelfID ChromeLauncherController::GetShelfIDForWebContents( | |
| 848 content::WebContents* contents) { | |
| 849 DCHECK(contents); | |
| 850 | |
| 851 std::string app_id = launcher_controller_helper_->GetAppID(contents); | |
| 852 | |
| 853 if (app_id.empty() && ContentCanBeHandledByGmailApp(contents)) | |
| 854 app_id = kGmailAppId; | |
| 855 | |
| 856 ash::ShelfID id = GetShelfIDForAppID(app_id); | |
| 857 | |
| 858 if (app_id.empty() || !id) { | |
| 859 int browser_index = model_->GetItemIndexForType(ash::TYPE_BROWSER_SHORTCUT); | |
| 860 return model_->items()[browser_index].id; | |
| 861 } | |
| 862 | |
| 863 return id; | |
| 864 } | |
| 865 | |
| 866 void ChromeLauncherController::SetRefocusURLPatternForTest(ash::ShelfID id, | |
| 867 const GURL& url) { | |
| 868 LauncherItemController* controller = GetLauncherItemController(id); | |
| 869 DCHECK(controller); | |
| 870 | |
| 871 int index = model_->ItemIndexByID(id); | |
| 872 if (index == -1) { | |
| 873 NOTREACHED() << "Invalid launcher id"; | |
| 874 return; | |
| 875 } | |
| 876 | |
| 877 ash::ShelfItemType type = model_->items()[index].type; | |
| 878 if (type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_WINDOWED_APP) { | |
| 879 AppShortcutLauncherItemController* app_controller = | |
| 880 static_cast<AppShortcutLauncherItemController*>(controller); | |
| 881 app_controller->set_refocus_url(url); | |
| 882 } else { | |
| 883 NOTREACHED() << "Invalid launcher type"; | |
| 884 } | |
| 885 } | |
| 886 | |
| 887 const Extension* ChromeLauncherController::GetExtensionForAppID( | |
| 888 const std::string& app_id) const { | |
| 889 return extensions::ExtensionRegistry::Get(profile_)->GetExtensionById( | |
| 890 app_id, extensions::ExtensionRegistry::EVERYTHING); | |
| 891 } | |
| 892 | |
| 893 ash::ShelfItemDelegate::PerformedAction | |
| 894 ChromeLauncherController::ActivateWindowOrMinimizeIfActive( | |
| 895 ui::BaseWindow* window, | |
| 896 bool allow_minimize) { | |
| 897 // In separated desktop mode we might have to teleport a window back to the | |
| 898 // current user. | |
| 899 if (chrome::MultiUserWindowManager::GetMultiProfileMode() == | |
| 900 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED) { | |
| 901 aura::Window* native_window = window->GetNativeWindow(); | |
| 902 const AccountId& current_account_id = | |
| 903 multi_user_util::GetAccountIdFromProfile(profile()); | |
| 904 chrome::MultiUserWindowManager* manager = | |
| 905 chrome::MultiUserWindowManager::GetInstance(); | |
| 906 if (!manager->IsWindowOnDesktopOfUser(native_window, current_account_id)) { | |
| 907 ash::MultiProfileUMA::RecordTeleportAction( | |
| 908 ash::MultiProfileUMA::TELEPORT_WINDOW_RETURN_BY_LAUNCHER); | |
| 909 manager->ShowWindowForUser(native_window, current_account_id); | |
| 910 window->Activate(); | |
| 911 return ash::ShelfItemDelegate::kExistingWindowActivated; | |
| 912 } | |
| 913 } | |
| 914 | |
| 915 if (window->IsActive() && allow_minimize) { | |
| 916 window->Minimize(); | |
| 917 return ash::ShelfItemDelegate::kNoAction; | |
| 918 } | |
| 919 | |
| 920 window->Show(); | |
| 921 window->Activate(); | |
| 922 return ash::ShelfItemDelegate::kExistingWindowActivated; | |
| 923 } | |
| 924 | |
| 925 void ChromeLauncherController::OnShelfCreated(ash::Shelf* shelf) { | |
| 926 PrefService* prefs = profile_->GetPrefs(); | |
| 927 const int64_t display = GetDisplayIDForShelf(shelf); | |
| 928 | |
| 929 shelf->SetAutoHideBehavior(ash::GetShelfAutoHideBehaviorPref(prefs, display)); | |
| 930 | |
| 931 if (ash::ShelfWidget::ShelfAlignmentAllowed()) | |
| 932 shelf->SetAlignment(ash::GetShelfAlignmentPref(prefs, display)); | |
| 933 } | |
| 934 | |
| 935 void ChromeLauncherController::OnShelfDestroyed(ash::Shelf* shelf) {} | |
| 936 | |
| 937 void ChromeLauncherController::OnShelfAlignmentChanged(ash::Shelf* shelf) { | |
| 938 ash::SetShelfAlignmentPref(profile_->GetPrefs(), GetDisplayIDForShelf(shelf), | |
| 939 shelf->alignment()); | |
| 940 } | |
| 941 | |
| 942 void ChromeLauncherController::OnShelfAutoHideBehaviorChanged( | |
| 943 ash::Shelf* shelf) { | |
| 944 ash::SetShelfAutoHideBehaviorPref(profile_->GetPrefs(), | |
| 945 GetDisplayIDForShelf(shelf), | |
| 946 shelf->auto_hide_behavior()); | |
| 947 } | |
| 948 | |
| 949 void ChromeLauncherController::OnShelfAutoHideStateChanged(ash::Shelf* shelf) {} | |
| 950 | |
| 951 void ChromeLauncherController::OnShelfVisibilityStateChanged( | |
| 952 ash::Shelf* shelf) {} | |
| 953 | |
| 954 void ChromeLauncherController::ShelfItemAdded(int index) { | |
| 955 // The app list launcher can get added to the shelf after we applied the | |
| 956 // preferences. In that case the item might be at the wrong spot. As such we | |
| 957 // call the function again. | |
| 958 if (model_->items()[index].type == ash::TYPE_APP_LIST) | |
| 959 UpdateAppLaunchersFromPref(); | |
| 960 } | |
| 961 | |
| 962 void ChromeLauncherController::ShelfItemRemoved(int index, ash::ShelfID id) { | |
| 963 // TODO(skuhne): This fixes crbug.com/429870, but it does not answer why we | |
| 964 // get into this state in the first place. | |
| 965 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id); | |
| 966 if (iter == id_to_item_controller_map_.end()) | |
| 967 return; | |
| 968 | |
| 969 LOG(ERROR) << "Unexpected change of shelf item id: " << id; | |
| 970 | |
| 971 id_to_item_controller_map_.erase(iter); | |
| 972 } | |
| 973 | |
| 974 void ChromeLauncherController::ShelfItemMoved(int start_index, | |
| 975 int target_index) { | |
| 976 const ash::ShelfItem& item = model_->items()[target_index]; | |
| 977 // We remember the moved item position if it is either pinnable or | |
| 978 // it is the app list with the alternate shelf layout. | |
| 979 if ((HasShelfIDToAppIDMapping(item.id) && IsPinned(item.id)) || | |
| 980 item.type == ash::TYPE_APP_LIST) | |
| 981 PersistPinnedState(); | |
| 982 } | |
| 983 | |
| 984 void ChromeLauncherController::ShelfItemChanged( | |
| 985 int index, | |
| 986 const ash::ShelfItem& old_item) {} | |
| 987 | |
| 988 void ChromeLauncherController::ActiveUserChanged( | |
| 989 const std::string& user_email) { | |
| 990 // Store the order of running applications for the user which gets inactive. | |
| 991 RememberUnpinnedRunningApplicationOrder(); | |
| 992 // Coming here the default profile is already switched. All profile specific | |
| 993 // resources get released and the new profile gets attached instead. | |
| 994 ReleaseProfile(); | |
| 995 // When coming here, the active user has already be changed so that we can | |
| 996 // set it as active. | |
| 997 AttachProfile(ProfileManager::GetActiveUserProfile()); | |
| 998 // Update the V1 applications. | |
| 999 browser_status_monitor_->ActiveUserChanged(user_email); | |
| 1000 // Switch the running applications to the new user. | |
| 1001 for (auto& controller : app_window_controllers_) | |
| 1002 controller->ActiveUserChanged(user_email); | |
| 1003 // Update the user specific shell properties from the new user profile. | |
| 1004 UpdateAppLaunchersFromPref(); | |
| 1005 SetShelfBehaviorsFromPrefs(); | |
| 1006 SetVirtualKeyboardBehaviorFromPrefs(); | |
| 1007 | |
| 1008 // Restore the order of running, but unpinned applications for the activated | |
| 1009 // user. | |
| 1010 RestoreUnpinnedRunningApplicationOrder(user_email); | |
| 1011 // Inform the system tray of the change. | |
| 1012 ash::Shell::GetInstance()->system_tray_delegate()->ActiveUserWasChanged(); | |
| 1013 // Force on-screen keyboard to reset. | |
| 1014 if (keyboard::IsKeyboardEnabled()) | |
| 1015 ash::Shell::GetInstance()->CreateKeyboard(); | |
| 1016 } | |
| 1017 | |
| 1018 void ChromeLauncherController::AdditionalUserAddedToSession(Profile* profile) { | |
| 1019 // Switch the running applications to the new user. | |
| 1020 for (auto& controller : app_window_controllers_) | |
| 1021 controller->AdditionalUserAddedToSession(profile); | |
| 1022 } | |
| 1023 | |
| 1024 void ChromeLauncherController::OnAppInstalled( | |
| 1025 content::BrowserContext* browser_context, | |
| 1026 const std::string& app_id) { | |
| 1027 if (IsAppPinned(app_id)) { | |
| 1028 // Clear and re-fetch to ensure icon is up-to-date. | |
| 1029 AppIconLoader* app_icon_loader = GetAppIconLoaderForApp(app_id); | |
| 1030 if (app_icon_loader) { | |
| 1031 app_icon_loader->ClearImage(app_id); | |
| 1032 app_icon_loader->FetchImage(app_id); | |
| 1033 } | |
| 1034 } | |
| 1035 | |
| 1036 UpdateAppLaunchersFromPref(); | |
| 1037 } | |
| 1038 | |
| 1039 void ChromeLauncherController::OnAppUpdated( | |
| 1040 content::BrowserContext* browser_context, | |
| 1041 const std::string& app_id) { | |
| 1042 AppIconLoader* app_icon_loader = GetAppIconLoaderForApp(app_id); | |
| 1043 if (app_icon_loader) | |
| 1044 app_icon_loader->UpdateImage(app_id); | |
| 1045 } | |
| 1046 | |
| 1047 void ChromeLauncherController::OnAppUninstalled( | |
| 1048 content::BrowserContext* browser_context, | |
| 1049 const std::string& app_id) { | |
| 1050 // Since we might have windowed apps of this type which might have | |
| 1051 // outstanding locks which needs to be removed. | |
| 1052 const Profile* profile = Profile::FromBrowserContext(browser_context); | |
| 1053 if (GetShelfIDForAppID(app_id)) | |
| 1054 CloseWindowedAppsFromRemovedExtension(app_id, profile); | |
| 1055 | |
| 1056 if (IsAppPinned(app_id)) { | |
| 1057 if (profile == profile_) | |
| 1058 DoUnpinAppWithID(app_id); | |
| 1059 AppIconLoader* app_icon_loader = GetAppIconLoaderForApp(app_id); | |
| 1060 if (app_icon_loader) | |
| 1061 app_icon_loader->ClearImage(app_id); | |
| 1062 } | |
| 1063 } | |
| 1064 | |
| 1065 void ChromeLauncherController::OnDisplayConfigurationChanged() { | |
| 1066 SetShelfBehaviorsFromPrefs(); | |
| 1067 } | |
| 1068 | |
| 1069 void ChromeLauncherController::OnAppSyncUIStatusChanged() { | |
| 1070 if (app_sync_ui_state_->status() == AppSyncUIState::STATUS_SYNCING) | |
| 1071 model_->set_status(ash::ShelfModel::STATUS_LOADING); | |
| 1072 else | |
| 1073 model_->set_status(ash::ShelfModel::STATUS_NORMAL); | |
| 1074 } | |
| 1075 | |
| 1076 ChromeLauncherAppMenuItems ChromeLauncherController::GetApplicationList( | |
| 1077 const ash::ShelfItem& item, | |
| 1078 int event_flags) { | |
| 1079 // Make sure that there is a controller associated with the id and that the | |
| 1080 // extension itself is a valid application and not a panel. | |
| 1081 LauncherItemController* controller = GetLauncherItemController(item.id); | |
| 1082 if (!controller || !GetShelfIDForAppID(controller->app_id())) | |
| 1083 return ChromeLauncherAppMenuItems(); | |
| 1084 | |
| 1085 return controller->GetApplicationList(event_flags); | |
| 1086 } | |
| 1087 | |
| 1088 std::vector<content::WebContents*> | |
| 1089 ChromeLauncherController::GetV1ApplicationsFromAppId( | |
| 1090 const std::string& app_id) { | |
| 1091 ash::ShelfID id = GetShelfIDForAppID(app_id); | |
| 1092 | |
| 1093 // If there is no such an item pinned to the launcher, no menu gets created. | |
| 1094 if (id) { | |
| 1095 LauncherItemController* controller = GetLauncherItemController(id); | |
| 1096 DCHECK(controller); | |
| 1097 if (controller->type() == LauncherItemController::TYPE_SHORTCUT) | |
| 1098 return GetV1ApplicationsFromController(controller); | |
| 1099 } | |
| 1100 return std::vector<content::WebContents*>(); | |
| 1101 } | |
| 1102 | |
| 1103 void ChromeLauncherController::ActivateShellApp(const std::string& app_id, | |
| 1104 int index) { | |
| 1105 ash::ShelfID id = GetShelfIDForAppID(app_id); | |
| 1106 if (id) { | |
| 1107 LauncherItemController* controller = GetLauncherItemController(id); | |
| 1108 if (controller && controller->type() == LauncherItemController::TYPE_APP) { | |
| 1109 AppWindowLauncherItemController* app_window_controller = | |
| 1110 static_cast<AppWindowLauncherItemController*>(controller); | |
| 1111 app_window_controller->ActivateIndexedApp(index); | |
| 1112 } | |
| 1113 } | |
| 1114 } | |
| 1115 | |
| 1116 bool ChromeLauncherController::IsWebContentHandledByApplication( | |
| 1117 content::WebContents* web_contents, | |
| 1118 const std::string& app_id) { | |
| 1119 if ((web_contents_to_app_id_.find(web_contents) != | |
| 1120 web_contents_to_app_id_.end()) && | |
| 1121 (web_contents_to_app_id_[web_contents] == app_id)) | |
| 1122 return true; | |
| 1123 return (app_id == kGmailAppId && ContentCanBeHandledByGmailApp(web_contents)); | |
| 1124 } | |
| 1125 | |
| 1126 bool ChromeLauncherController::ContentCanBeHandledByGmailApp( | |
| 1127 content::WebContents* web_contents) { | |
| 1128 ash::ShelfID id = GetShelfIDForAppID(kGmailAppId); | |
| 1129 if (id) { | |
| 1130 const GURL url = web_contents->GetURL(); | |
| 1131 // We need to extend the application matching for the gMail app beyond the | |
| 1132 // manifest file's specification. This is required because of the namespace | |
| 1133 // overlap with the offline app ("/mail/mu/"). | |
| 1134 if (!base::MatchPattern(url.path(), "/mail/mu/*") && | |
| 1135 base::MatchPattern(url.path(), "/mail/*") && | |
| 1136 GetExtensionForAppID(kGmailAppId) && | |
| 1137 GetExtensionForAppID(kGmailAppId)->OverlapsWithOrigin(url)) | |
| 1138 return true; | |
| 1139 } | |
| 1140 return false; | |
| 1141 } | |
| 1142 | |
| 1143 gfx::Image ChromeLauncherController::GetAppListIcon( | |
| 1144 content::WebContents* web_contents) const { | |
| 1145 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | |
| 1146 if (IsIncognito(web_contents)) | |
| 1147 return rb.GetImageNamed(IDR_ASH_SHELF_LIST_INCOGNITO_BROWSER); | |
| 1148 favicon::FaviconDriver* favicon_driver = | |
| 1149 favicon::ContentFaviconDriver::FromWebContents(web_contents); | |
| 1150 gfx::Image result = favicon_driver->GetFavicon(); | |
| 1151 if (result.IsEmpty()) | |
| 1152 return rb.GetImageNamed(IDR_DEFAULT_FAVICON); | |
| 1153 return result; | |
| 1154 } | |
| 1155 | |
| 1156 base::string16 ChromeLauncherController::GetAppListTitle( | |
| 1157 content::WebContents* web_contents) const { | |
| 1158 base::string16 title = web_contents->GetTitle(); | |
| 1159 if (!title.empty()) | |
| 1160 return title; | |
| 1161 WebContentsToAppIDMap::const_iterator iter = | |
| 1162 web_contents_to_app_id_.find(web_contents); | |
| 1163 if (iter != web_contents_to_app_id_.end()) { | |
| 1164 std::string app_id = iter->second; | |
| 1165 const extensions::Extension* extension = GetExtensionForAppID(app_id); | |
| 1166 if (extension) | |
| 1167 return base::UTF8ToUTF16(extension->name()); | |
| 1168 } | |
| 1169 return l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE); | |
| 1170 } | |
| 1171 | |
| 1172 ash::ShelfID ChromeLauncherController::CreateAppShortcutLauncherItem( | |
| 1173 const std::string& app_id, | |
| 1174 int index) { | |
| 1175 return CreateAppShortcutLauncherItemWithType(app_id, | |
| 1176 index, | |
| 1177 ash::TYPE_APP_SHORTCUT); | |
| 1178 } | |
| 1179 | |
| 1180 void ChromeLauncherController::SetLauncherControllerHelperForTest( | |
| 1181 LauncherControllerHelper* helper) { | |
| 1182 launcher_controller_helper_.reset(helper); | |
| 1183 } | |
| 1184 | |
| 1185 void ChromeLauncherController::SetAppIconLoadersForTest( | |
| 1186 std::vector<std::unique_ptr<AppIconLoader>>& loaders) { | |
| 1187 app_icon_loaders_.clear(); | |
| 1188 for (auto& loader : loaders) | |
| 1189 app_icon_loaders_.push_back(std::move(loader)); | |
| 1190 } | |
| 1191 | |
| 1192 const std::string& ChromeLauncherController::GetAppIdFromShelfIdForTest( | |
| 1193 ash::ShelfID id) { | |
| 1194 return id_to_item_controller_map_[id]->app_id(); | |
| 1195 } | |
| 1196 | |
| 1197 bool ChromeLauncherController::GetAppIDForShelfIDConst( | |
| 1198 ash::ShelfID id, | |
| 1199 std::string* app_id) const { | |
| 1200 auto app = id_to_item_controller_map_.find(id); | |
| 1201 if (app == id_to_item_controller_map_.end()) { | |
| 1202 return false; | |
| 1203 } else { | |
| 1204 *app_id = app->second->app_id(); | |
| 1205 return true; | |
| 1206 } | |
| 1207 } | |
| 1208 | |
| 1209 void ChromeLauncherController::SetShelfItemDelegateManagerForTest( | |
| 1210 ash::ShelfItemDelegateManager* manager) { | |
| 1211 if (item_delegate_manager_) | |
| 1212 item_delegate_manager_->RemoveObserver(this); | |
| 1213 | |
| 1214 item_delegate_manager_ = manager; | |
| 1215 | |
| 1216 if (item_delegate_manager_) | |
| 1217 item_delegate_manager_->AddObserver(this); | |
| 1218 } | |
| 1219 | |
| 1220 void ChromeLauncherController::RememberUnpinnedRunningApplicationOrder() { | |
| 1221 RunningAppListIds list; | |
| 1222 for (int i = 0; i < model_->item_count(); i++) { | |
| 1223 ash::ShelfItemType type = model_->items()[i].type; | |
| 1224 if (type == ash::TYPE_WINDOWED_APP || type == ash::TYPE_PLATFORM_APP) | |
| 1225 list.push_back(GetAppIDForShelfID(model_->items()[i].id)); | |
| 1226 } | |
| 1227 const std::string user_email = | |
| 1228 multi_user_util::GetAccountIdFromProfile(profile_).GetUserEmail(); | |
| 1229 last_used_running_application_order_[user_email] = list; | |
| 1230 } | |
| 1231 | |
| 1232 void ChromeLauncherController::RestoreUnpinnedRunningApplicationOrder( | |
| 1233 const std::string& user_id) { | |
| 1234 const RunningAppListIdMap::iterator app_id_list = | |
| 1235 last_used_running_application_order_.find(user_id); | |
| 1236 if (app_id_list == last_used_running_application_order_.end()) | |
| 1237 return; | |
| 1238 | |
| 1239 // Find the first insertion point for running applications. | |
| 1240 int running_index = model_->FirstRunningAppIndex(); | |
| 1241 for (RunningAppListIds::iterator app_id = app_id_list->second.begin(); | |
| 1242 app_id != app_id_list->second.end(); ++app_id) { | |
| 1243 ash::ShelfID shelf_id = GetShelfIDForAppID(*app_id); | |
| 1244 if (shelf_id) { | |
| 1245 int app_index = model_->ItemIndexByID(shelf_id); | |
| 1246 DCHECK_GE(app_index, 0); | |
| 1247 ash::ShelfItemType type = model_->items()[app_index].type; | |
| 1248 if (type == ash::TYPE_WINDOWED_APP || type == ash::TYPE_PLATFORM_APP) { | |
| 1249 if (running_index != app_index) | |
| 1250 model_->Move(running_index, app_index); | |
| 1251 running_index++; | |
| 1252 } | |
| 1253 } | |
| 1254 } | |
| 1255 } | |
| 1256 | |
| 1257 ash::ShelfID ChromeLauncherController::CreateAppShortcutLauncherItemWithType( | |
| 1258 const std::string& app_id, | |
| 1259 int index, | |
| 1260 ash::ShelfItemType shelf_item_type) { | |
| 1261 AppShortcutLauncherItemController* controller = | |
| 1262 AppShortcutLauncherItemController::Create(app_id, this); | |
| 1263 ash::ShelfID shelf_id = InsertAppLauncherItem( | |
| 1264 controller, app_id, ash::STATUS_CLOSED, index, shelf_item_type); | |
| 1265 return shelf_id; | |
| 1266 } | |
| 1267 | |
| 1268 LauncherItemController* ChromeLauncherController::GetLauncherItemController( | |
| 1269 const ash::ShelfID id) { | |
| 1270 if (!HasShelfIDToAppIDMapping(id)) | |
| 1271 return NULL; | |
| 1272 return id_to_item_controller_map_[id]; | |
| 1273 } | |
| 1274 | |
| 1275 bool ChromeLauncherController::IsBrowserFromActiveUser(Browser* browser) { | |
| 1276 // If running multi user mode with separate desktops, we have to check if the | |
| 1277 // browser is from the active user. | |
| 1278 if (chrome::MultiUserWindowManager::GetMultiProfileMode() != | |
| 1279 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED) | |
| 1280 return true; | |
| 1281 return multi_user_util::IsProfileFromActiveUser(browser->profile()); | |
| 1282 } | |
| 1283 | |
| 1284 bool ChromeLauncherController::ShelfBoundsChangesProbablyWithUser( | |
| 1285 ash::Shelf* shelf, | |
| 1286 const std::string& user_id) const { | |
| 1287 Profile* other_profile = multi_user_util::GetProfileFromAccountId( | |
| 1288 AccountId::FromUserEmail(user_id)); | |
| 1289 if (other_profile == profile_) | |
| 1290 return false; | |
| 1291 | |
| 1292 // Note: The Auto hide state from preferences is not the same as the actual | |
| 1293 // visibility of the shelf. Depending on all the various states (full screen, | |
| 1294 // no window on desktop, multi user, ..) the shelf could be shown - or not. | |
| 1295 PrefService* prefs = profile_->GetPrefs(); | |
| 1296 PrefService* other_prefs = other_profile->GetPrefs(); | |
| 1297 const int64_t display = GetDisplayIDForShelf(shelf); | |
| 1298 bool currently_shown = ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER == | |
| 1299 ash::GetShelfAutoHideBehaviorPref(prefs, display); | |
| 1300 bool other_shown = ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER == | |
| 1301 ash::GetShelfAutoHideBehaviorPref(other_prefs, display); | |
| 1302 | |
| 1303 return currently_shown != other_shown || | |
| 1304 ash::GetShelfAlignmentPref(prefs, display) != | |
| 1305 ash::GetShelfAlignmentPref(other_prefs, display); | |
| 1306 } | |
| 1307 | |
| 1308 void ChromeLauncherController::OnUserProfileReadyToSwitch(Profile* profile) { | |
| 1309 if (user_switch_observer_.get()) | |
| 1310 user_switch_observer_->OnUserProfileReadyToSwitch(profile); | |
| 1311 } | |
| 1312 | |
| 1313 void ChromeLauncherController::LauncherItemClosed(ash::ShelfID id) { | |
| 1314 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id); | |
| 1315 CHECK(iter != id_to_item_controller_map_.end()); | |
| 1316 CHECK(iter->second); | |
| 1317 const std::string& app_id = iter->second->app_id(); | |
| 1318 AppIconLoader* app_icon_loader = GetAppIconLoaderForApp(app_id); | |
| 1319 if (app_icon_loader) | |
| 1320 app_icon_loader->ClearImage(app_id); | |
| 1321 id_to_item_controller_map_.erase(iter); | |
| 1322 int index = model_->ItemIndexByID(id); | |
| 1323 // A "browser proxy" is not known to the model and this removal does | |
| 1324 // therefore not need to be propagated to the model. | |
| 1325 if (index != -1) | |
| 1326 model_->RemoveItemAt(index); | |
| 1327 } | |
| 1328 | |
| 1329 void ChromeLauncherController::DoPinAppWithID(const std::string& app_id) { | |
| 1330 // If there is an item, do nothing and return. | |
| 1331 if (IsAppPinned(app_id)) | |
| 1332 return; | |
| 1333 | |
| 1334 ash::ShelfID shelf_id = GetShelfIDForAppID(app_id); | |
| 1335 if (shelf_id) { | |
| 1336 // App item exists, pin it | |
| 1337 Pin(shelf_id); | |
| 1338 } else { | |
| 1339 // Otherwise, create a shortcut item for it. | |
| 1340 shelf_id = CreateAppShortcutLauncherItem(app_id, model_->item_count()); | |
| 1341 if (GetPinnable(app_id) == AppListControllerDelegate::PIN_EDITABLE) | |
| 1342 PersistPinnedState(); | |
| 1343 } | |
| 1344 } | |
| 1345 | |
| 1346 void ChromeLauncherController::DoUnpinAppWithID(const std::string& app_id) { | |
| 1347 ash::ShelfID shelf_id = GetShelfIDForAppID(app_id); | |
| 1348 if (shelf_id && IsPinned(shelf_id)) | |
| 1349 Unpin(shelf_id); | |
| 1350 } | |
| 1351 | |
| 1352 int ChromeLauncherController::PinRunningAppInternal(int index, | |
| 1353 ash::ShelfID shelf_id) { | |
| 1354 int running_index = model_->ItemIndexByID(shelf_id); | |
| 1355 ash::ShelfItem item = model_->items()[running_index]; | |
| 1356 DCHECK(item.type == ash::TYPE_WINDOWED_APP || | |
| 1357 item.type == ash::TYPE_PLATFORM_APP); | |
| 1358 item.type = ash::TYPE_APP_SHORTCUT; | |
| 1359 model_->Set(running_index, item); | |
| 1360 // The |ShelfModel|'s weight system might reposition the item to a | |
| 1361 // new index, so we get the index again. | |
| 1362 running_index = model_->ItemIndexByID(shelf_id); | |
| 1363 if (running_index < index) | |
| 1364 --index; | |
| 1365 if (running_index != index) | |
| 1366 model_->Move(running_index, index); | |
| 1367 return index; | |
| 1368 } | |
| 1369 | |
| 1370 void ChromeLauncherController::UnpinRunningAppInternal(int index) { | |
| 1371 DCHECK_GE(index, 0); | |
| 1372 ash::ShelfItem item = model_->items()[index]; | |
| 1373 DCHECK_EQ(item.type, ash::TYPE_APP_SHORTCUT); | |
| 1374 item.type = ash::TYPE_WINDOWED_APP; | |
| 1375 // A platform app and a windowed app are sharing TYPE_APP_SHORTCUT. As such | |
| 1376 // we have to check here what this was before it got a shortcut. | |
| 1377 LauncherItemController* controller = GetLauncherItemController(item.id); | |
| 1378 if (controller && controller->type() == LauncherItemController::TYPE_APP) | |
| 1379 item.type = ash::TYPE_PLATFORM_APP; | |
| 1380 model_->Set(index, item); | |
| 1381 } | |
| 1382 | |
| 1383 void ChromeLauncherController::UpdateAppLaunchersFromPref() { | |
| 1384 // There are various functions which will trigger a |PersistPinnedState| call | |
| 1385 // like a direct call to |DoPinAppWithID|, or an indirect call to the menu | |
| 1386 // model which will use weights to re-arrange the icons to new positions. | |
| 1387 // Since this function is meant to synchronize the "is state" with the | |
| 1388 // "sync state", it makes no sense to store any changes by this function back | |
| 1389 // into the pref state. Therefore we tell |persistPinnedState| to ignore any | |
| 1390 // invocations while we are running. | |
| 1391 base::AutoReset<bool> auto_reset(&ignore_persist_pinned_state_change_, true); | |
| 1392 std::vector<std::string> pinned_apps = ash::GetPinnedAppsFromPrefs( | |
| 1393 profile_->GetPrefs(), launcher_controller_helper_.get()); | |
| 1394 | |
| 1395 int index = 0; | |
| 1396 int max_index = model_->item_count(); | |
| 1397 | |
| 1398 // When one of the two special items cannot be moved (and we do not know where | |
| 1399 // yet), we remember the current location in one of these variables. | |
| 1400 int chrome_index = -1; | |
| 1401 int app_list_index = -1; | |
| 1402 | |
| 1403 // Walk the model and |pinned_apps| from the pref lockstep, adding and | |
| 1404 // removing items as necessary. NB: This code uses plain old indexing instead | |
| 1405 // of iterators because of model mutations as part of the loop. | |
| 1406 std::vector<std::string>::const_iterator pref_app_id(pinned_apps.begin()); | |
| 1407 for (; index < max_index && pref_app_id != pinned_apps.end(); ++index) { | |
| 1408 // Check if we have an item which we need to handle. | |
| 1409 if (*pref_app_id == extension_misc::kChromeAppId || | |
| 1410 *pref_app_id == ash::kPinnedAppsPlaceholder || | |
| 1411 IsAppPinned(*pref_app_id)) { | |
| 1412 for (; index < max_index; ++index) { | |
| 1413 const ash::ShelfItem& item(model_->items()[index]); | |
| 1414 bool is_app_list = item.type == ash::TYPE_APP_LIST; | |
| 1415 bool is_chrome = item.type == ash::TYPE_BROWSER_SHORTCUT; | |
| 1416 if (item.type != ash::TYPE_APP_SHORTCUT && !is_app_list && !is_chrome) | |
| 1417 continue; | |
| 1418 LauncherItemController* controller = GetLauncherItemController(item.id); | |
| 1419 if ((ash::kPinnedAppsPlaceholder == *pref_app_id && is_app_list) || | |
| 1420 (extension_misc::kChromeAppId == *pref_app_id && is_chrome) || | |
| 1421 (controller && controller->app_id() == *pref_app_id)) { | |
| 1422 // Check if an item needs to be moved here. | |
| 1423 MoveChromeOrApplistToFinalPosition( | |
| 1424 is_chrome, is_app_list, index, &chrome_index, &app_list_index); | |
| 1425 ++pref_app_id; | |
| 1426 break; | |
| 1427 } else { | |
| 1428 if (is_chrome || is_app_list) { | |
| 1429 // We cannot delete any of these shortcuts. As such we remember | |
| 1430 // their positions and move them later where they belong. | |
| 1431 if (is_chrome) | |
| 1432 chrome_index = index; | |
| 1433 else | |
| 1434 app_list_index = index; | |
| 1435 // And skip the item - or exit the loop if end is reached (note that | |
| 1436 // in that case we will reduce the index again by one and this only | |
| 1437 // compensates for it). | |
| 1438 if (index >= max_index - 1) | |
| 1439 break; | |
| 1440 ++index; | |
| 1441 } else { | |
| 1442 // Check if this is a platform or a windowed app. | |
| 1443 if (item.type == ash::TYPE_APP_SHORTCUT && | |
| 1444 controller && | |
| 1445 (controller->locked() || | |
| 1446 controller->type() == LauncherItemController::TYPE_APP)) { | |
| 1447 // Note: This will not change the amount of items (|max_index|). | |
| 1448 // Even changes to the actual |index| due to item weighting | |
| 1449 // changes should be fine. | |
| 1450 UnpinRunningAppInternal(index); | |
| 1451 } else { | |
| 1452 if (controller) | |
| 1453 LauncherItemClosed(item.id); | |
| 1454 --max_index; | |
| 1455 } | |
| 1456 } | |
| 1457 --index; | |
| 1458 } | |
| 1459 } | |
| 1460 // If the item wasn't found, that means id_to_item_controller_map_ | |
| 1461 // is out of sync. | |
| 1462 DCHECK(index <= max_index); | |
| 1463 } else { | |
| 1464 // Check if the item was already running but not yet pinned. | |
| 1465 ash::ShelfID shelf_id = GetShelfIDForAppID(*pref_app_id); | |
| 1466 if (shelf_id) { | |
| 1467 // This app is running but not yet pinned. So pin and move it. | |
| 1468 index = PinRunningAppInternal(index, shelf_id); | |
| 1469 } else { | |
| 1470 // This app wasn't pinned before, insert a new entry. | |
| 1471 shelf_id = CreateAppShortcutLauncherItem(*pref_app_id, index); | |
| 1472 ++max_index; | |
| 1473 index = model_->ItemIndexByID(shelf_id); | |
| 1474 } | |
| 1475 ++pref_app_id; | |
| 1476 } | |
| 1477 } | |
| 1478 | |
| 1479 // Remove any trailing existing items. | |
| 1480 while (index < model_->item_count()) { | |
| 1481 const ash::ShelfItem& item(model_->items()[index]); | |
| 1482 if (item.type == ash::TYPE_APP_SHORTCUT) { | |
| 1483 LauncherItemController* controller = GetLauncherItemController(item.id); | |
| 1484 if (controller) { | |
| 1485 if (controller->locked() || | |
| 1486 controller->type() == LauncherItemController::TYPE_APP) { | |
| 1487 UnpinRunningAppInternal(index); | |
| 1488 } else { | |
| 1489 LauncherItemClosed(item.id); | |
| 1490 } | |
| 1491 } | |
| 1492 } else { | |
| 1493 if (item.type == ash::TYPE_BROWSER_SHORTCUT) | |
| 1494 chrome_index = index; | |
| 1495 else if (item.type == ash::TYPE_APP_LIST) | |
| 1496 app_list_index = index; | |
| 1497 ++index; | |
| 1498 } | |
| 1499 } | |
| 1500 | |
| 1501 // Append unprocessed items from the pref to the end of the model. | |
| 1502 for (; pref_app_id != pinned_apps.end(); ++pref_app_id) { | |
| 1503 // All items but the chrome and / or app list shortcut needs to be added. | |
| 1504 bool is_chrome = *pref_app_id == extension_misc::kChromeAppId; | |
| 1505 bool is_app_list = *pref_app_id == ash::kPinnedAppsPlaceholder; | |
| 1506 // Coming here we know the next item which can be finalized, either the | |
| 1507 // chrome item or the app launcher. The final position is the end of the | |
| 1508 // list. The menu model will make sure that the item is grouped according | |
| 1509 // to its weight (which we do not know here). | |
| 1510 if (!is_chrome && !is_app_list) { | |
| 1511 DoPinAppWithID(*pref_app_id); | |
| 1512 int target_index = FindInsertionPoint(false); | |
| 1513 ash::ShelfID id = GetShelfIDForAppID(*pref_app_id); | |
| 1514 int source_index = model_->ItemIndexByID(id); | |
| 1515 if (source_index != target_index) | |
| 1516 model_->Move(source_index, target_index); | |
| 1517 | |
| 1518 // Needed for the old layout - the weight might force it to be lower in | |
| 1519 // rank. | |
| 1520 if (app_list_index != -1 && target_index <= app_list_index) | |
| 1521 ++app_list_index; | |
| 1522 } else { | |
| 1523 int target_index = FindInsertionPoint(is_app_list); | |
| 1524 MoveChromeOrApplistToFinalPosition( | |
| 1525 is_chrome, is_app_list, target_index, &chrome_index, &app_list_index); | |
| 1526 } | |
| 1527 } | |
| 1528 } | |
| 1529 | |
| 1530 void ChromeLauncherController::SetShelfAutoHideBehaviorFromPrefs() { | |
| 1531 for (auto* window : ash::Shell::GetAllRootWindows()) { | |
| 1532 ash::Shelf* shelf = ash::Shelf::ForWindow(window); | |
| 1533 if (shelf) { | |
| 1534 shelf->SetAutoHideBehavior(ash::GetShelfAutoHideBehaviorPref( | |
| 1535 profile_->GetPrefs(), GetDisplayIDForShelf(shelf))); | |
| 1536 } | |
| 1537 } | |
| 1538 } | |
| 1539 | |
| 1540 void ChromeLauncherController::SetShelfAlignmentFromPrefs() { | |
| 1541 if (!ash::ShelfWidget::ShelfAlignmentAllowed()) | |
| 1542 return; | |
| 1543 | |
| 1544 for (auto* window : ash::Shell::GetAllRootWindows()) { | |
| 1545 ash::Shelf* shelf = ash::Shelf::ForWindow(window); | |
| 1546 if (shelf) { | |
| 1547 shelf->SetAlignment(ash::GetShelfAlignmentPref( | |
| 1548 profile_->GetPrefs(), GetDisplayIDForShelf(shelf))); | |
| 1549 } | |
| 1550 } | |
| 1551 } | |
| 1552 | |
| 1553 void ChromeLauncherController::SetShelfBehaviorsFromPrefs() { | |
| 1554 SetShelfAutoHideBehaviorFromPrefs(); | |
| 1555 SetShelfAlignmentFromPrefs(); | |
| 1556 } | |
| 1557 | |
| 1558 void ChromeLauncherController::SetVirtualKeyboardBehaviorFromPrefs() { | |
| 1559 const PrefService* service = profile_->GetPrefs(); | |
| 1560 const bool was_enabled = keyboard::IsKeyboardEnabled(); | |
| 1561 if (!service->HasPrefPath(prefs::kTouchVirtualKeyboardEnabled)) { | |
| 1562 keyboard::SetKeyboardShowOverride(keyboard::KEYBOARD_SHOW_OVERRIDE_NONE); | |
| 1563 } else { | |
| 1564 const bool enable = service->GetBoolean( | |
| 1565 prefs::kTouchVirtualKeyboardEnabled); | |
| 1566 keyboard::SetKeyboardShowOverride( | |
| 1567 enable ? keyboard::KEYBOARD_SHOW_OVERRIDE_ENABLED | |
| 1568 : keyboard::KEYBOARD_SHOW_OVERRIDE_DISABLED); | |
| 1569 } | |
| 1570 const bool is_enabled = keyboard::IsKeyboardEnabled(); | |
| 1571 if (was_enabled && !is_enabled) | |
| 1572 ash::Shell::GetInstance()->DeactivateKeyboard(); | |
| 1573 else if (is_enabled && !was_enabled) | |
| 1574 ash::Shell::GetInstance()->CreateKeyboard(); | |
| 1575 } | |
| 1576 | |
| 1577 ash::ShelfItemStatus ChromeLauncherController::GetAppState( | |
| 1578 const std::string& app_id) { | |
| 1579 ash::ShelfItemStatus status = ash::STATUS_CLOSED; | |
| 1580 for (WebContentsToAppIDMap::iterator it = web_contents_to_app_id_.begin(); | |
| 1581 it != web_contents_to_app_id_.end(); | |
| 1582 ++it) { | |
| 1583 if (it->second == app_id) { | |
| 1584 Browser* browser = chrome::FindBrowserWithWebContents(it->first); | |
| 1585 // Usually there should never be an item in our |web_contents_to_app_id_| | |
| 1586 // list which got deleted already. However - in some situations e.g. | |
| 1587 // Browser::SwapTabContent there is temporarily no associated browser. | |
| 1588 if (!browser) | |
| 1589 continue; | |
| 1590 if (browser->window()->IsActive()) { | |
| 1591 return browser->tab_strip_model()->GetActiveWebContents() == it->first ? | |
| 1592 ash::STATUS_ACTIVE : ash::STATUS_RUNNING; | |
| 1593 } else { | |
| 1594 status = ash::STATUS_RUNNING; | |
| 1595 } | |
| 1596 } | |
| 1597 } | |
| 1598 return status; | |
| 1599 } | |
| 1600 | |
| 1601 ash::ShelfID ChromeLauncherController::InsertAppLauncherItem( | |
| 1602 LauncherItemController* controller, | |
| 1603 const std::string& app_id, | |
| 1604 ash::ShelfItemStatus status, | |
| 1605 int index, | |
| 1606 ash::ShelfItemType shelf_item_type) { | |
| 1607 ash::ShelfID id = model_->next_id(); | |
| 1608 CHECK(!HasShelfIDToAppIDMapping(id)); | |
| 1609 CHECK(controller); | |
| 1610 id_to_item_controller_map_[id] = controller; | |
| 1611 controller->set_shelf_id(id); | |
| 1612 | |
| 1613 ash::ShelfItem item; | |
| 1614 item.type = shelf_item_type; | |
| 1615 item.image = extensions::util::GetDefaultAppIcon(); | |
| 1616 | |
| 1617 ash::ShelfItemStatus new_state = GetAppState(app_id); | |
| 1618 if (new_state != ash::STATUS_CLOSED) | |
| 1619 status = new_state; | |
| 1620 | |
| 1621 item.status = status; | |
| 1622 | |
| 1623 model_->AddAt(index, item); | |
| 1624 | |
| 1625 AppIconLoader* app_icon_loader = GetAppIconLoaderForApp(app_id); | |
| 1626 if (app_icon_loader) { | |
| 1627 app_icon_loader->FetchImage(app_id); | |
| 1628 app_icon_loader->UpdateImage(app_id); | |
| 1629 } | |
| 1630 | |
| 1631 SetShelfItemDelegate(id, controller); | |
| 1632 | |
| 1633 return id; | |
| 1634 } | |
| 1635 | |
| 1636 std::vector<content::WebContents*> | |
| 1637 ChromeLauncherController::GetV1ApplicationsFromController( | |
| 1638 LauncherItemController* controller) { | |
| 1639 DCHECK(controller->type() == LauncherItemController::TYPE_SHORTCUT); | |
| 1640 AppShortcutLauncherItemController* app_controller = | |
| 1641 static_cast<AppShortcutLauncherItemController*>(controller); | |
| 1642 return app_controller->GetRunningApplications(); | |
| 1643 } | |
| 1644 | |
| 1645 BrowserShortcutLauncherItemController* | |
| 1646 ChromeLauncherController::GetBrowserShortcutLauncherItemController() { | |
| 1647 for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin(); | |
| 1648 i != id_to_item_controller_map_.end(); ++i) { | |
| 1649 int index = model_->ItemIndexByID(i->first); | |
| 1650 const ash::ShelfItem& item = model_->items()[index]; | |
| 1651 if (item.type == ash::TYPE_BROWSER_SHORTCUT) | |
| 1652 return static_cast<BrowserShortcutLauncherItemController*>(i->second); | |
| 1653 } | |
| 1654 NOTREACHED() | |
| 1655 << "There should be always be a BrowserShortcutLauncherItemController."; | |
| 1656 return nullptr; | |
| 1657 } | |
| 1658 | |
| 1659 ash::ShelfID ChromeLauncherController::CreateBrowserShortcutLauncherItem() { | |
| 1660 ash::ShelfItem browser_shortcut; | |
| 1661 browser_shortcut.type = ash::TYPE_BROWSER_SHORTCUT; | |
| 1662 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
| 1663 browser_shortcut.image = *rb.GetImageSkiaNamed(IDR_PRODUCT_LOGO_32); | |
| 1664 ash::ShelfID id = model_->next_id(); | |
| 1665 size_t index = GetChromeIconIndexForCreation(); | |
| 1666 model_->AddAt(index, browser_shortcut); | |
| 1667 id_to_item_controller_map_[id] = | |
| 1668 new BrowserShortcutLauncherItemController(this, model_); | |
| 1669 id_to_item_controller_map_[id]->set_shelf_id(id); | |
| 1670 // ShelfItemDelegateManager owns BrowserShortcutLauncherItemController. | |
| 1671 SetShelfItemDelegate(id, id_to_item_controller_map_[id]); | |
| 1672 return id; | |
| 1673 } | |
| 1674 | |
| 1675 void ChromeLauncherController::PersistChromeItemIndex(int index) { | |
| 1676 profile_->GetPrefs()->SetInteger(prefs::kShelfChromeIconIndex, index); | |
| 1677 } | |
| 1678 | |
| 1679 void ChromeLauncherController::MoveChromeOrApplistToFinalPosition( | |
| 1680 bool is_chrome, | |
| 1681 bool is_app_list, | |
| 1682 int target_index, | |
| 1683 int* chrome_index, | |
| 1684 int* app_list_index) { | |
| 1685 if (is_chrome && *chrome_index != -1) { | |
| 1686 model_->Move(*chrome_index, target_index); | |
| 1687 if (*app_list_index != -1 && | |
| 1688 *chrome_index < *app_list_index && | |
| 1689 target_index > *app_list_index) | |
| 1690 --(*app_list_index); | |
| 1691 *chrome_index = -1; | |
| 1692 } else if (is_app_list && *app_list_index != -1) { | |
| 1693 model_->Move(*app_list_index, target_index); | |
| 1694 if (*chrome_index != -1 && | |
| 1695 *app_list_index < *chrome_index && | |
| 1696 target_index > *chrome_index) | |
| 1697 --(*chrome_index); | |
| 1698 *app_list_index = -1; | |
| 1699 } | |
| 1700 } | |
| 1701 | |
| 1702 int ChromeLauncherController::FindInsertionPoint(bool is_app_list) { | |
| 1703 // Keeping this change small to backport to M33&32 (see crbug.com/329597). | |
| 1704 // TODO(skuhne): With the removal of the legacy shelf layout we should remove | |
| 1705 // the ability to move the app list item since this was never used. We should | |
| 1706 // instead ask the ShelfModel::ValidateInsertionIndex or similir for an index. | |
| 1707 if (is_app_list) | |
| 1708 return 0; | |
| 1709 | |
| 1710 for (int i = model_->item_count() - 1; i > 0; --i) { | |
| 1711 ash::ShelfItemType type = model_->items()[i].type; | |
| 1712 if (type == ash::TYPE_APP_SHORTCUT || | |
| 1713 (is_app_list && type == ash::TYPE_APP_LIST) || | |
| 1714 type == ash::TYPE_BROWSER_SHORTCUT) { | |
| 1715 return i; | |
| 1716 } | |
| 1717 } | |
| 1718 return 0; | |
| 1719 } | |
| 1720 | |
| 1721 int ChromeLauncherController::GetChromeIconIndexForCreation() { | |
| 1722 // We get the list of pinned apps as they currently would get pinned. | |
| 1723 // Within this list the chrome icon will be the correct location. | |
| 1724 std::vector<std::string> pinned_apps = ash::GetPinnedAppsFromPrefs( | |
| 1725 profile_->GetPrefs(), launcher_controller_helper_.get()); | |
| 1726 | |
| 1727 std::vector<std::string>::iterator it = | |
| 1728 std::find(pinned_apps.begin(), | |
| 1729 pinned_apps.end(), | |
| 1730 std::string(extension_misc::kChromeAppId)); | |
| 1731 DCHECK(it != pinned_apps.end()); | |
| 1732 int index = it - pinned_apps.begin(); | |
| 1733 | |
| 1734 // We should do here a comparison between the is state and the "want to be" | |
| 1735 // state since some apps might be able to pin but are not yet. Instead - for | |
| 1736 // the time being we clamp against the amount of known items and wait for the | |
| 1737 // next |UpdateAppLaunchersFromPref()| call to correct it - it will come since | |
| 1738 // the pinning will be done then. | |
| 1739 return std::min(model_->item_count(), index); | |
| 1740 } | |
| 1741 | |
| 1742 bool ChromeLauncherController::IsIncognito( | |
| 1743 const content::WebContents* web_contents) const { | |
| 1744 const Profile* profile = | |
| 1745 Profile::FromBrowserContext(web_contents->GetBrowserContext()); | |
| 1746 return profile->IsOffTheRecord() && !profile->IsGuestSession() && | |
| 1747 !profile->IsSystemProfile(); | |
| 1748 } | |
| 1749 | |
| 1750 void ChromeLauncherController::CloseWindowedAppsFromRemovedExtension( | |
| 1751 const std::string& app_id, | |
| 1752 const Profile* profile) { | |
| 1753 // This function cannot rely on the controller's enumeration functionality | |
| 1754 // since the extension has already be unloaded. | |
| 1755 const BrowserList* browser_list = BrowserList::GetInstance(); | |
| 1756 std::vector<Browser*> browser_to_close; | |
| 1757 for (BrowserList::const_reverse_iterator it = | |
| 1758 browser_list->begin_last_active(); | |
| 1759 it != browser_list->end_last_active(); ++it) { | |
| 1760 Browser* browser = *it; | |
| 1761 if (!browser->is_type_tabbed() && browser->is_type_popup() && | |
| 1762 browser->is_app() && | |
| 1763 app_id == | |
| 1764 web_app::GetExtensionIdFromApplicationName(browser->app_name()) && | |
| 1765 profile == browser->profile()) { | |
| 1766 browser_to_close.push_back(browser); | |
| 1767 } | |
| 1768 } | |
| 1769 while (!browser_to_close.empty()) { | |
| 1770 TabStripModel* tab_strip = browser_to_close.back()->tab_strip_model(); | |
| 1771 tab_strip->CloseWebContentsAt(0, TabStripModel::CLOSE_NONE); | |
| 1772 browser_to_close.pop_back(); | |
| 1773 } | |
| 1774 } | |
| 1775 | |
| 1776 void ChromeLauncherController::SetShelfItemDelegate( | |
| 1777 ash::ShelfID id, | |
| 1778 ash::ShelfItemDelegate* item_delegate) { | |
| 1779 DCHECK_GT(id, 0); | |
| 1780 DCHECK(item_delegate); | |
| 1781 DCHECK(item_delegate_manager_); | |
| 1782 item_delegate_manager_->SetShelfItemDelegate( | |
| 1783 id, std::unique_ptr<ash::ShelfItemDelegate>(item_delegate)); | |
| 1784 } | |
| 1785 | |
| 1786 void ChromeLauncherController::AttachProfile(Profile* profile) { | |
| 1787 profile_ = profile; | |
| 1788 // Either add the profile to the list of known profiles and make it the active | |
| 1789 // one for some functions of LauncherControllerHelper or create a new one. | |
| 1790 if (!launcher_controller_helper_.get()) | |
| 1791 launcher_controller_helper_.reset(new LauncherControllerHelper(profile_)); | |
| 1792 else | |
| 1793 launcher_controller_helper_->SetCurrentUser(profile_); | |
| 1794 // TODO(skuhne): The AppIconLoaderImpl has the same problem. Each loaded | |
| 1795 // image is associated with a profile (its loader requires the profile). | |
| 1796 // Since icon size changes are possible, the icon could be requested to be | |
| 1797 // reloaded. However - having it not multi profile aware would cause problems | |
| 1798 // if the icon cache gets deleted upon user switch. | |
| 1799 std::unique_ptr<AppIconLoader> extension_app_icon_loader( | |
| 1800 new extensions::ExtensionAppIconLoader( | |
| 1801 profile_, extension_misc::EXTENSION_ICON_SMALL, this)); | |
| 1802 app_icon_loaders_.push_back(std::move(extension_app_icon_loader)); | |
| 1803 | |
| 1804 if (arc::ArcAuthService::IsAllowedForProfile(profile_)) { | |
| 1805 DCHECK(arc_deferred_launcher()); | |
| 1806 std::unique_ptr<AppIconLoader> arc_app_icon_loader( | |
| 1807 new ArcAppIconLoader(profile_, extension_misc::EXTENSION_ICON_SMALL, | |
| 1808 arc_deferred_launcher(), this)); | |
| 1809 app_icon_loaders_.push_back(std::move(arc_app_icon_loader)); | |
| 1810 } | |
| 1811 | |
| 1812 pref_change_registrar_.Init(profile_->GetPrefs()); | |
| 1813 pref_change_registrar_.Add( | |
| 1814 prefs::kPinnedLauncherApps, | |
| 1815 base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref, | |
| 1816 base::Unretained(this))); | |
| 1817 pref_change_registrar_.Add( | |
| 1818 prefs::kPolicyPinnedLauncherApps, | |
| 1819 base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref, | |
| 1820 base::Unretained(this))); | |
| 1821 pref_change_registrar_.Add( | |
| 1822 prefs::kShelfAlignmentLocal, | |
| 1823 base::Bind(&ChromeLauncherController::SetShelfAlignmentFromPrefs, | |
| 1824 base::Unretained(this))); | |
| 1825 pref_change_registrar_.Add( | |
| 1826 prefs::kShelfAutoHideBehaviorLocal, | |
| 1827 base::Bind(&ChromeLauncherController:: | |
| 1828 SetShelfAutoHideBehaviorFromPrefs, | |
| 1829 base::Unretained(this))); | |
| 1830 pref_change_registrar_.Add( | |
| 1831 prefs::kShelfPreferences, | |
| 1832 base::Bind(&ChromeLauncherController::SetShelfBehaviorsFromPrefs, | |
| 1833 base::Unretained(this))); | |
| 1834 pref_change_registrar_.Add( | |
| 1835 prefs::kTouchVirtualKeyboardEnabled, | |
| 1836 base::Bind(&ChromeLauncherController::SetVirtualKeyboardBehaviorFromPrefs, | |
| 1837 base::Unretained(this))); | |
| 1838 | |
| 1839 std::unique_ptr<LauncherAppUpdater> extension_app_updater( | |
| 1840 new LauncherExtensionAppUpdater(this, profile_)); | |
| 1841 app_updaters_.push_back(std::move(extension_app_updater)); | |
| 1842 | |
| 1843 if (arc::ArcAuthService::IsAllowedForProfile(profile_)) { | |
| 1844 std::unique_ptr<LauncherAppUpdater> arc_app_updater( | |
| 1845 new LauncherArcAppUpdater(this, profile_)); | |
| 1846 app_updaters_.push_back(std::move(arc_app_updater)); | |
| 1847 } | |
| 1848 } | |
| 1849 | |
| 1850 void ChromeLauncherController::ReleaseProfile() { | |
| 1851 if (app_sync_ui_state_) | |
| 1852 app_sync_ui_state_->RemoveObserver(this); | |
| 1853 | |
| 1854 app_updaters_.clear(); | |
| 1855 | |
| 1856 prefs_observer_.reset(); | |
| 1857 | |
| 1858 pref_change_registrar_.RemoveAll(); | |
| 1859 } | |
| 1860 | |
| 1861 AppIconLoader* ChromeLauncherController::GetAppIconLoaderForApp( | |
| 1862 const std::string& app_id) { | |
| 1863 for (const auto& app_icon_loader : app_icon_loaders_) { | |
| 1864 if (app_icon_loader->CanLoadImageForApp(app_id)) | |
| 1865 return app_icon_loader.get(); | |
| 1866 } | |
| 1867 | |
| 1868 return nullptr; | |
| 1869 } | |
| OLD | NEW |