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