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
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
9 #include "ash/ash_switches.h"
10 #include "ash/desktop_background/desktop_background_controller.h"
11 #include "ash/multi_profile_uma.h"
12 #include "ash/root_window_controller.h"
13 #include "ash/shelf/shelf.h"
14 #include "ash/shelf/shelf_item_delegate_manager.h"
15 #include "ash/shelf/shelf_layout_manager.h"
16 #include "ash/shelf/shelf_model.h"
17 #include "ash/shelf/shelf_widget.h"
18 #include "ash/shell.h"
19 #include "ash/system/tray/system_tray_delegate.h"
20 #include "ash/wm/window_util.h"
21 #include "base/command_line.h"
22 #include "base/prefs/scoped_user_pref_update.h"
23 #include "base/strings/string_number_conversions.h"
24 #include "base/strings/utf_string_conversions.h"
25 #include "base/values.h"
26 #include "chrome/browser/app_mode/app_mode_utils.h"
27 #include "chrome/browser/chrome_notification_types.h"
28 #include "chrome/browser/defaults.h"
29 #include "chrome/browser/extensions/app_icon_loader_impl.h"
30 #include "chrome/browser/extensions/extension_service.h"
31 #include "chrome/browser/extensions/extension_util.h"
32 #include "chrome/browser/extensions/launch_util.h"
33 #include "chrome/browser/favicon/favicon_tab_helper.h"
34 #include "chrome/browser/prefs/incognito_mode_prefs.h"
35 #include "chrome/browser/prefs/pref_service_syncable.h"
36 #include "chrome/browser/profiles/profile.h"
37 #include "chrome/browser/profiles/profile_manager.h"
38 #include "chrome/browser/ui/ash/app_sync_ui_state.h"
39 #include "chrome/browser/ui/ash/chrome_launcher_prefs.h"
40 #include "chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.h"
41 #include "chrome/browser/ui/ash/launcher/app_window_launcher_controller.h"
42 #include "chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h"
43 #include "chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.h"
44 #include "chrome/browser/ui/ash/launcher/browser_status_monitor.h"
45 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h"
46 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_browser.h"
47 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_tab.h"
48 #include "chrome/browser/ui/ash/launcher/chrome_launcher_types.h"
49 #include "chrome/browser/ui/ash/launcher/launcher_app_tab_helper.h"
50 #include "chrome/browser/ui/ash/launcher/launcher_item_controller.h"
51 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
52 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager.h"
53 #include "chrome/browser/ui/browser.h"
54 #include "chrome/browser/ui/browser_commands.h"
55 #include "chrome/browser/ui/browser_finder.h"
56 #include "chrome/browser/ui/browser_list.h"
57 #include "chrome/browser/ui/browser_tabstrip.h"
58 #include "chrome/browser/ui/browser_window.h"
59 #include "chrome/browser/ui/extensions/application_launch.h"
60 #include "chrome/browser/ui/extensions/extension_enable_flow.h"
61 #include "chrome/browser/ui/host_desktop.h"
62 #include "chrome/browser/ui/tabs/tab_strip_model.h"
63 #include "chrome/browser/web_applications/web_app.h"
64 #include "chrome/common/chrome_switches.h"
65 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
66 #include "chrome/common/pref_names.h"
67 #include "chrome/common/url_constants.h"
68 #include "content/public/browser/navigation_entry.h"
69 #include "content/public/browser/notification_registrar.h"
70 #include "content/public/browser/notification_service.h"
71 #include "content/public/browser/web_contents.h"
72 #include "extensions/browser/extension_prefs.h"
73 #include "extensions/browser/extension_system.h"
74 #include "extensions/common/extension.h"
75 #include "extensions/common/extension_resource.h"
76 #include "extensions/common/manifest_handlers/icons_handler.h"
77 #include "extensions/common/url_pattern.h"
78 #include "grit/ash_resources.h"
79 #include "grit/chromium_strings.h"
80 #include "grit/generated_resources.h"
81 #include "grit/theme_resources.h"
82 #include "grit/ui_resources.h"
83 #include "net/base/url_util.h"
84 #include "ui/aura/window.h"
85 #include "ui/aura/window_event_dispatcher.h"
86 #include "ui/base/l10n/l10n_util.h"
87 #include "ui/keyboard/keyboard_util.h"
88 #include "ui/wm/core/window_animations.h"
90 #if defined(OS_CHROMEOS)
91 #include "chrome/browser/browser_process.h"
92 #include "chrome/browser/chromeos/login/user_manager.h"
93 #include "chrome/browser/ui/ash/chrome_shell_delegate.h"
94 #include "chrome/browser/ui/ash/launcher/multi_profile_app_window_launcher_controller.h"
95 #include "chrome/browser/ui/ash/launcher/multi_profile_browser_status_monitor.h"
98 using extensions::Extension;
99 using extensions::UnloadedExtensionInfo;
100 using extension_misc::kGmailAppId;
101 using content::WebContents;
104 ChromeLauncherController* ChromeLauncherController::instance_ = NULL;
108 // This will be used as placeholder in the list of the pinned applciatons.
109 // Note that this is NOT a valid extension identifier so that pre M31 versions
111 const char kAppShelfIdPlaceholder[] = "AppShelfIDPlaceholder--------";
113 std::string GetPrefKeyForRootWindow(aura::Window* root_window) {
114 gfx::Display display = gfx::Screen::GetScreenFor(
115 root_window)->GetDisplayNearestWindow(root_window);
116 DCHECK(display.is_valid());
118 return base::Int64ToString(display.id());
121 void UpdatePerDisplayPref(PrefService* pref_service,
122 aura::Window* root_window,
123 const char* pref_key,
124 const std::string& value) {
125 std::string key = GetPrefKeyForRootWindow(root_window);
129 DictionaryPrefUpdate update(pref_service, prefs::kShelfPreferences);
130 base::DictionaryValue* shelf_prefs = update.Get();
131 base::DictionaryValue* prefs = NULL;
132 if (!shelf_prefs->GetDictionary(key, &prefs)) {
133 prefs = new base::DictionaryValue();
134 shelf_prefs->Set(key, prefs);
136 prefs->SetStringWithoutPathExpansion(pref_key, value);
139 // Returns a pref value in |pref_service| for the display of |root_window|. The
140 // pref value is stored in |local_path| and |path|, but |pref_service| may have
141 // per-display preferences and the value can be specified by policy. Here is
143 // * A value managed by policy. This is a single value that applies to all
145 // * A user-set value for the specified display.
146 // * A user-set value in |local_path| or |path|, if no per-display settings are
147 // ever specified (see http://crbug.com/173719 for why). |local_path| is
148 // preferred. See comment in |kShelfAlignment| as to why we consider two
149 // prefs and why |local_path| is preferred.
150 // * A value recommended by policy. This is a single value that applies to all
152 // * The default value for |local_path| if the value is not recommended by
154 std::string GetPrefForRootWindow(PrefService* pref_service,
155 aura::Window* root_window,
156 const char* local_path,
158 const PrefService::Preference* local_pref =
159 pref_service->FindPreference(local_path);
160 const std::string value(pref_service->GetString(local_path));
161 if (local_pref->IsManaged())
164 std::string pref_key = GetPrefKeyForRootWindow(root_window);
165 bool has_per_display_prefs = false;
166 if (!pref_key.empty()) {
167 const base::DictionaryValue* shelf_prefs = pref_service->GetDictionary(
168 prefs::kShelfPreferences);
169 const base::DictionaryValue* display_pref = NULL;
170 std::string per_display_value;
171 if (shelf_prefs->GetDictionary(pref_key, &display_pref) &&
172 display_pref->GetString(path, &per_display_value))
173 return per_display_value;
175 // If the pref for the specified display is not found, scan the whole prefs
176 // and check if the prefs for other display is already specified.
177 std::string unused_value;
178 for (base::DictionaryValue::Iterator iter(*shelf_prefs);
179 !iter.IsAtEnd(); iter.Advance()) {
180 const base::DictionaryValue* display_pref = NULL;
181 if (iter.value().GetAsDictionary(&display_pref) &&
182 display_pref->GetString(path, &unused_value)) {
183 has_per_display_prefs = true;
189 if (local_pref->IsRecommended() || !has_per_display_prefs)
192 const base::Value* default_value =
193 pref_service->GetDefaultPrefValue(local_path);
194 std::string default_string;
195 default_value->GetAsString(&default_string);
196 return default_string;
199 // If prefs have synced and no user-set value exists at |local_path|, the value
200 // from |synced_path| is copied to |local_path|.
201 void MaybePropagatePrefToLocal(PrefServiceSyncable* pref_service,
202 const char* local_path,
203 const char* synced_path) {
204 if (!pref_service->FindPreference(local_path)->HasUserSetting() &&
205 pref_service->IsSyncing()) {
206 // First time the user is using this machine, propagate from remote to
208 pref_service->SetString(local_path, pref_service->GetString(synced_path));
212 std::string GetSourceFromAppListSource(ash::LaunchSource source) {
214 case ash::LAUNCH_FROM_APP_LIST:
215 return std::string(extension_urls::kLaunchSourceAppList);
216 case ash::LAUNCH_FROM_APP_LIST_SEARCH:
217 return std::string(extension_urls::kLaunchSourceAppListSearch);
218 default: return std::string();
224 #if defined(OS_CHROMEOS)
225 // A class to get events from ChromeOS when a user gets changed or added.
226 class ChromeLauncherControllerUserSwitchObserverChromeOS
227 : public ChromeLauncherControllerUserSwitchObserver,
228 public chromeos::UserManager::UserSessionStateObserver,
229 content::NotificationObserver {
231 ChromeLauncherControllerUserSwitchObserverChromeOS(
232 ChromeLauncherController* controller)
233 : controller_(controller) {
234 DCHECK(chromeos::UserManager::IsInitialized());
235 chromeos::UserManager::Get()->AddSessionStateObserver(this);
236 // A UserAddedToSession notification can be sent before a profile is loaded.
237 // Since our observers require that we have already a profile, we might have
238 // to postpone the notification until the ProfileManager lets us know that
239 // the profile for that newly added user was added to the ProfileManager.
240 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_ADDED,
241 content::NotificationService::AllSources());
243 virtual ~ChromeLauncherControllerUserSwitchObserverChromeOS() {
244 chromeos::UserManager::Get()->RemoveSessionStateObserver(this);
247 // chromeos::UserManager::UserSessionStateObserver overrides:
248 virtual void UserAddedToSession(const chromeos::User* added_user) OVERRIDE;
250 // content::NotificationObserver overrides:
251 virtual void Observe(int type,
252 const content::NotificationSource& source,
253 const content::NotificationDetails& details) OVERRIDE;
256 // Add a user to the session.
257 void AddUser(Profile* profile);
259 // The owning ChromeLauncherController.
260 ChromeLauncherController* controller_;
262 // The notification registrar to track the Profile creations after a user got
263 // added to the session (if required).
264 content::NotificationRegistrar registrar_;
266 // Users which were just added to the system, but which profiles were not yet
268 std::set<std::string> added_user_ids_waiting_for_profiles_;
270 DISALLOW_COPY_AND_ASSIGN(ChromeLauncherControllerUserSwitchObserverChromeOS);
273 void ChromeLauncherControllerUserSwitchObserverChromeOS::UserAddedToSession(
274 const chromeos::User* active_user) {
275 Profile* profile = multi_user_util::GetProfileFromUserID(
276 active_user->email());
277 // If we do not have a profile yet, we postpone forwarding the notification
278 // until it is loaded.
280 added_user_ids_waiting_for_profiles_.insert(active_user->email());
285 void ChromeLauncherControllerUserSwitchObserverChromeOS::Observe(
287 const content::NotificationSource& source,
288 const content::NotificationDetails& details) {
289 if (type == chrome::NOTIFICATION_PROFILE_ADDED &&
290 !added_user_ids_waiting_for_profiles_.empty()) {
291 // Check if the profile is from a user which was on the waiting list.
292 Profile* profile = content::Source<Profile>(source).ptr();
293 std::string user_id = multi_user_util::GetUserIDFromProfile(profile);
294 std::set<std::string>::iterator it = std::find(
295 added_user_ids_waiting_for_profiles_.begin(),
296 added_user_ids_waiting_for_profiles_.end(),
298 if (it != added_user_ids_waiting_for_profiles_.end()) {
299 added_user_ids_waiting_for_profiles_.erase(it);
300 AddUser(profile->GetOriginalProfile());
305 void ChromeLauncherControllerUserSwitchObserverChromeOS::AddUser(
307 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
308 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED)
309 chrome::MultiUserWindowManager::GetInstance()->AddUser(profile);
310 controller_->AdditionalUserAddedToSession(profile->GetOriginalProfile());
314 ChromeLauncherController::ChromeLauncherController(Profile* profile,
315 ash::ShelfModel* model)
317 item_delegate_manager_(NULL),
319 app_sync_ui_state_(NULL),
320 ignore_persist_pinned_state_change_(false) {
322 // If no profile was passed, we take the currently active profile and use it
323 // as the owner of the current desktop.
324 // Use the original profile as on chromeos we may get a temporary off the
325 // record profile, unless in guest session (where off the record profile is
327 Profile* active_profile = ProfileManager::GetActiveUserProfile();
328 profile_ = active_profile->IsGuestSession() ? active_profile :
329 active_profile->GetOriginalProfile();
331 app_sync_ui_state_ = AppSyncUIState::Get(profile_);
332 if (app_sync_ui_state_)
333 app_sync_ui_state_->AddObserver(this);
336 // All profile relevant settings get bound to the current profile.
337 AttachProfile(profile_);
338 model_->AddObserver(this);
340 // In multi profile mode we might have a window manager. We try to create it
341 // here. If the instantiation fails, the manager is not needed.
342 chrome::MultiUserWindowManager::CreateInstance();
344 #if defined(OS_CHROMEOS)
345 // On Chrome OS using multi profile we want to switch the content of the shelf
346 // with a user change. Note that for unit tests the instance can be NULL.
347 if (chrome::MultiUserWindowManager::GetMultiProfileMode() !=
348 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_OFF) {
349 user_switch_observer_.reset(
350 new ChromeLauncherControllerUserSwitchObserverChromeOS(this));
353 // Create our v1/v2 application / browser monitors which will inform the
354 // launcher of status changes.
355 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
356 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED) {
357 // If running in separated destkop mode, we create the multi profile version
358 // of status monitor.
359 browser_status_monitor_.reset(new MultiProfileBrowserStatusMonitor(this));
360 app_window_controller_.reset(
361 new MultiProfileAppWindowLauncherController(this));
363 // Create our v1/v2 application / browser monitors which will inform the
364 // launcher of status changes.
365 browser_status_monitor_.reset(new BrowserStatusMonitor(this));
366 app_window_controller_.reset(new AppWindowLauncherController(this));
369 // Create our v1/v2 application / browser monitors which will inform the
370 // launcher of status changes.
371 browser_status_monitor_.reset(new BrowserStatusMonitor(this));
372 app_window_controller_.reset(new AppWindowLauncherController(this));
375 // Right now ash::Shell isn't created for tests.
376 // TODO(mukai): Allows it to observe display change and write tests.
377 if (ash::Shell::HasInstance()) {
378 ash::Shell::GetInstance()->display_controller()->AddObserver(this);
379 item_delegate_manager_ =
380 ash::Shell::GetInstance()->shelf_item_delegate_manager();
383 notification_registrar_.Add(this,
384 chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED,
385 content::Source<Profile>(profile_));
386 notification_registrar_.Add(
388 chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
389 content::Source<Profile>(profile_));
392 ChromeLauncherController::~ChromeLauncherController() {
393 // Reset the BrowserStatusMonitor as it has a weak pointer to this.
394 browser_status_monitor_.reset();
396 // Reset the app window controller here since it has a weak pointer to this.
397 app_window_controller_.reset();
399 for (std::set<ash::Shelf*>::iterator iter = shelves_.begin();
400 iter != shelves_.end();
402 (*iter)->shelf_widget()->shelf_layout_manager()->RemoveObserver(this);
404 model_->RemoveObserver(this);
405 if (ash::Shell::HasInstance())
406 ash::Shell::GetInstance()->display_controller()->RemoveObserver(this);
407 for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin();
408 i != id_to_item_controller_map_.end(); ++i) {
409 int index = model_->ItemIndexByID(i->first);
410 // A "browser proxy" is not known to the model and this removal does
411 // therefore not need to be propagated to the model.
413 model_->items()[index].type != ash::TYPE_BROWSER_SHORTCUT)
414 model_->RemoveItemAt(index);
417 if (ash::Shell::HasInstance())
418 ash::Shell::GetInstance()->RemoveShellObserver(this);
420 // Release all profile dependent resources.
422 if (instance_ == this)
425 // Get rid of the multi user window manager instance.
426 chrome::MultiUserWindowManager::DeleteInstance();
430 ChromeLauncherController* ChromeLauncherController::CreateInstance(
432 ash::ShelfModel* model) {
433 // We do not check here for re-creation of the ChromeLauncherController since
434 // it appears that it might be intentional that the ChromeLauncherController
435 // can be re-created.
436 instance_ = new ChromeLauncherController(profile, model);
440 void ChromeLauncherController::Init() {
441 CreateBrowserShortcutLauncherItem();
442 UpdateAppLaunchersFromPref();
444 // TODO(sky): update unit test so that this test isn't necessary.
445 if (ash::Shell::HasInstance()) {
446 SetShelfAutoHideBehaviorFromPrefs();
447 SetShelfAlignmentFromPrefs();
448 PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_);
449 if (!prefs->FindPreference(prefs::kShelfAlignmentLocal)->HasUserSetting() ||
450 !prefs->FindPreference(prefs::kShelfAutoHideBehaviorLocal)->
452 // This causes OnIsSyncingChanged to be called when the value of
453 // PrefService::IsSyncing() changes.
454 prefs->AddObserver(this);
456 ash::Shell::GetInstance()->AddShellObserver(this);
460 ash::ShelfID ChromeLauncherController::CreateAppLauncherItem(
461 LauncherItemController* controller,
462 const std::string& app_id,
463 ash::ShelfItemStatus status) {
466 // Panels are inserted on the left so as not to push all existing panels over.
467 if (controller->GetShelfItemType() != ash::TYPE_APP_PANEL)
468 index = model_->item_count();
469 return InsertAppLauncherItem(controller,
473 controller->GetShelfItemType());
476 void ChromeLauncherController::SetItemStatus(ash::ShelfID id,
477 ash::ShelfItemStatus status) {
478 int index = model_->ItemIndexByID(id);
479 ash::ShelfItemStatus old_status = model_->items()[index].status;
480 // Since ordinary browser windows are not registered, we might get a negative
482 if (index >= 0 && old_status != status) {
483 ash::ShelfItem item = model_->items()[index];
484 item.status = status;
485 model_->Set(index, item);
489 void ChromeLauncherController::SetItemController(
491 LauncherItemController* controller) {
493 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
494 CHECK(iter != id_to_item_controller_map_.end());
495 controller->set_shelf_id(id);
496 iter->second = controller;
497 // Existing controller is destroyed and replaced by registering again.
498 SetShelfItemDelegate(id, controller);
501 void ChromeLauncherController::CloseLauncherItem(ash::ShelfID id) {
504 // Create a new shortcut controller.
505 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
506 CHECK(iter != id_to_item_controller_map_.end());
507 SetItemStatus(id, ash::STATUS_CLOSED);
508 std::string app_id = iter->second->app_id();
509 iter->second = new AppShortcutLauncherItemController(app_id, this);
510 iter->second->set_shelf_id(id);
511 // Existing controller is destroyed and replaced by registering again.
512 SetShelfItemDelegate(id, iter->second);
514 LauncherItemClosed(id);
518 void ChromeLauncherController::Pin(ash::ShelfID id) {
519 DCHECK(HasItemController(id));
521 int index = model_->ItemIndexByID(id);
524 ash::ShelfItem item = model_->items()[index];
526 if (item.type == ash::TYPE_PLATFORM_APP ||
527 item.type == ash::TYPE_WINDOWED_APP) {
528 item.type = ash::TYPE_APP_SHORTCUT;
529 model_->Set(index, item);
530 } else if (item.type != ash::TYPE_APP_SHORTCUT) {
535 PersistPinnedState();
538 void ChromeLauncherController::Unpin(ash::ShelfID id) {
539 DCHECK(HasItemController(id));
541 LauncherItemController* controller = id_to_item_controller_map_[id];
542 if (controller->type() == LauncherItemController::TYPE_APP ||
543 controller->locked()) {
544 UnpinRunningAppInternal(model_->ItemIndexByID(id));
546 LauncherItemClosed(id);
549 PersistPinnedState();
552 bool ChromeLauncherController::IsPinned(ash::ShelfID id) {
553 int index = model_->ItemIndexByID(id);
556 ash::ShelfItemType type = model_->items()[index].type;
557 return (type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_BROWSER_SHORTCUT);
560 void ChromeLauncherController::TogglePinned(ash::ShelfID id) {
561 if (!HasItemController(id))
562 return; // May happen if item closed with menu open.
570 bool ChromeLauncherController::IsPinnable(ash::ShelfID id) const {
571 int index = model_->ItemIndexByID(id);
575 ash::ShelfItemType type = model_->items()[index].type;
576 return ((type == ash::TYPE_APP_SHORTCUT ||
577 type == ash::TYPE_PLATFORM_APP ||
578 type == ash::TYPE_WINDOWED_APP) &&
582 void ChromeLauncherController::LockV1AppWithID(
583 const std::string& app_id) {
584 ash::ShelfID id = GetShelfIDForAppID(app_id);
585 if (!IsPinned(id) && !IsWindowedAppInLauncher(app_id)) {
586 CreateAppShortcutLauncherItemWithType(app_id,
587 model_->item_count(),
588 ash::TYPE_WINDOWED_APP);
589 id = GetShelfIDForAppID(app_id);
592 id_to_item_controller_map_[id]->lock();
595 void ChromeLauncherController::UnlockV1AppWithID(const std::string& app_id) {
596 ash::ShelfID id = GetShelfIDForAppID(app_id);
597 CHECK(IsPinned(id) || IsWindowedAppInLauncher(app_id));
599 LauncherItemController* controller = id_to_item_controller_map_[id];
600 controller->unlock();
601 if (!controller->locked() && !IsPinned(id))
602 CloseLauncherItem(id);
605 void ChromeLauncherController::Launch(ash::ShelfID id, int event_flags) {
606 if (!HasItemController(id))
607 return; // In case invoked from menu and item closed while menu up.
608 id_to_item_controller_map_[id]->Launch(ash::LAUNCH_FROM_UNKNOWN, event_flags);
611 void ChromeLauncherController::Close(ash::ShelfID id) {
612 if (!HasItemController(id))
613 return; // May happen if menu closed.
614 id_to_item_controller_map_[id]->Close();
617 bool ChromeLauncherController::IsOpen(ash::ShelfID id) {
618 if (!HasItemController(id))
620 return id_to_item_controller_map_[id]->IsOpen();
623 bool ChromeLauncherController::IsPlatformApp(ash::ShelfID id) {
624 if (!HasItemController(id))
627 std::string app_id = GetAppIDForShelfID(id);
628 const Extension* extension = GetExtensionForAppID(app_id);
629 // An extension can be synced / updated at any time and therefore not be
631 return extension ? extension->is_platform_app() : false;
634 void ChromeLauncherController::LaunchApp(const std::string& app_id,
635 ash::LaunchSource source,
637 // |extension| could be NULL when it is being unloaded for updating.
638 const Extension* extension = GetExtensionForAppID(app_id);
642 if (!extensions::util::IsAppLaunchableWithoutEnabling(app_id, profile_)) {
643 // Do nothing if there is already a running enable flow.
644 if (extension_enable_flow_)
647 extension_enable_flow_.reset(
648 new ExtensionEnableFlow(profile_, app_id, this));
649 extension_enable_flow_->StartForNativeWindow(NULL);
653 if (LaunchedInNativeDesktop(app_id))
656 // The app will be created for the currently active profile.
657 AppLaunchParams params(profile_,
660 chrome::HOST_DESKTOP_TYPE_ASH);
661 if (source != ash::LAUNCH_FROM_UNKNOWN &&
662 app_id == extension_misc::kWebStoreAppId) {
663 // Get the corresponding source string.
664 std::string source_value = GetSourceFromAppListSource(source);
666 // Set an override URL to include the source.
667 GURL extension_url = extensions::AppLaunchInfo::GetFullLaunchURL(extension);
668 params.override_url = net::AppendQueryParameter(
669 extension_url, extension_urls::kWebstoreSourceField, source_value);
672 OpenApplication(params);
675 void ChromeLauncherController::ActivateApp(const std::string& app_id,
676 ash::LaunchSource source,
678 // If there is an existing non-shortcut controller for this app, open it.
679 ash::ShelfID id = GetShelfIDForAppID(app_id);
681 LauncherItemController* controller = id_to_item_controller_map_[id];
682 controller->Activate(source);
686 // Create a temporary application launcher item and use it to see if there are
687 // running instances.
688 scoped_ptr<AppShortcutLauncherItemController> app_controller(
689 new AppShortcutLauncherItemController(app_id, this));
690 if (!app_controller->GetRunningApplications().empty())
691 app_controller->Activate(source);
693 LaunchApp(app_id, source, event_flags);
696 extensions::LaunchType ChromeLauncherController::GetLaunchType(
698 DCHECK(HasItemController(id));
700 const Extension* extension = GetExtensionForAppID(
701 id_to_item_controller_map_[id]->app_id());
703 // An extension can be unloaded/updated/unavailable at any time.
705 return extensions::LAUNCH_TYPE_DEFAULT;
707 return extensions::GetLaunchType(extensions::ExtensionPrefs::Get(profile_),
711 ash::ShelfID ChromeLauncherController::GetShelfIDForAppID(
712 const std::string& app_id) {
713 for (IDToItemControllerMap::const_iterator i =
714 id_to_item_controller_map_.begin();
715 i != id_to_item_controller_map_.end(); ++i) {
716 if (i->second->type() == LauncherItemController::TYPE_APP_PANEL)
717 continue; // Don't include panels
718 if (i->second->app_id() == app_id)
724 const std::string& ChromeLauncherController::GetAppIDForShelfID(
726 CHECK(HasItemController(id));
727 return id_to_item_controller_map_[id]->app_id();
730 void ChromeLauncherController::SetAppImage(const std::string& id,
731 const gfx::ImageSkia& image) {
732 // TODO: need to get this working for shortcuts.
733 for (IDToItemControllerMap::const_iterator i =
734 id_to_item_controller_map_.begin();
735 i != id_to_item_controller_map_.end(); ++i) {
736 LauncherItemController* controller = i->second;
737 if (controller->app_id() != id)
739 if (controller->image_set_by_controller())
741 int index = model_->ItemIndexByID(i->first);
744 ash::ShelfItem item = model_->items()[index];
746 model_->Set(index, item);
747 // It's possible we're waiting on more than one item, so don't break.
751 void ChromeLauncherController::OnAutoHideBehaviorChanged(
752 aura::Window* root_window,
753 ash::ShelfAutoHideBehavior new_behavior) {
754 SetShelfAutoHideBehaviorPrefs(new_behavior, root_window);
757 void ChromeLauncherController::SetLauncherItemImage(
758 ash::ShelfID shelf_id,
759 const gfx::ImageSkia& image) {
760 int index = model_->ItemIndexByID(shelf_id);
763 ash::ShelfItem item = model_->items()[index];
765 model_->Set(index, item);
768 bool ChromeLauncherController::CanPin() const {
769 const PrefService::Preference* pref =
770 profile_->GetPrefs()->FindPreference(prefs::kPinnedLauncherApps);
771 return pref && pref->IsUserModifiable();
774 bool ChromeLauncherController::IsAppPinned(const std::string& app_id) {
775 for (IDToItemControllerMap::const_iterator i =
776 id_to_item_controller_map_.begin();
777 i != id_to_item_controller_map_.end(); ++i) {
778 if (IsPinned(i->first) && i->second->app_id() == app_id)
784 bool ChromeLauncherController::IsWindowedAppInLauncher(
785 const std::string& app_id) {
786 int index = model_->ItemIndexByID(GetShelfIDForAppID(app_id));
790 ash::ShelfItemType type = model_->items()[index].type;
791 return type == ash::TYPE_WINDOWED_APP;
794 void ChromeLauncherController::PinAppWithID(const std::string& app_id) {
796 DoPinAppWithID(app_id);
801 void ChromeLauncherController::SetLaunchType(
803 extensions::LaunchType launch_type) {
804 if (!HasItemController(id))
807 extensions::SetLaunchType(profile_->GetExtensionService(),
808 id_to_item_controller_map_[id]->app_id(),
812 void ChromeLauncherController::UnpinAppWithID(const std::string& app_id) {
814 DoUnpinAppWithID(app_id);
819 bool ChromeLauncherController::IsLoggedInAsGuest() {
820 return profile_->IsGuestSession();
823 void ChromeLauncherController::CreateNewWindow() {
824 // Use the currently active user.
825 chrome::NewEmptyWindow(profile_, chrome::HOST_DESKTOP_TYPE_ASH);
828 void ChromeLauncherController::CreateNewIncognitoWindow() {
829 // Use the currently active user.
830 chrome::NewEmptyWindow(profile_->GetOffTheRecordProfile(),
831 chrome::HOST_DESKTOP_TYPE_ASH);
834 void ChromeLauncherController::PersistPinnedState() {
835 if (ignore_persist_pinned_state_change_)
837 // It is a coding error to call PersistPinnedState() if the pinned apps are
838 // not user-editable. The code should check earlier and not perform any
839 // modification actions that trigger persisting the state.
841 NOTREACHED() << "Can't pin but pinned state being updated";
844 // Mutating kPinnedLauncherApps is going to notify us and trigger us to
845 // process the change. We don't want that to happen so remove ourselves as a
847 pref_change_registrar_.Remove(prefs::kPinnedLauncherApps);
849 ListPrefUpdate updater(profile_->GetPrefs(), prefs::kPinnedLauncherApps);
851 for (size_t i = 0; i < model_->items().size(); ++i) {
852 if (model_->items()[i].type == ash::TYPE_APP_SHORTCUT) {
853 ash::ShelfID id = model_->items()[i].id;
854 if (HasItemController(id) && IsPinned(id)) {
855 base::DictionaryValue* app_value = ash::CreateAppDict(
856 id_to_item_controller_map_[id]->app_id());
858 updater->Append(app_value);
860 } else if (model_->items()[i].type == ash::TYPE_BROWSER_SHORTCUT) {
861 PersistChromeItemIndex(i);
862 } else if (model_->items()[i].type == ash::TYPE_APP_LIST) {
863 base::DictionaryValue* app_value = ash::CreateAppDict(
864 kAppShelfIdPlaceholder);
866 updater->Append(app_value);
870 pref_change_registrar_.Add(
871 prefs::kPinnedLauncherApps,
872 base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref,
873 base::Unretained(this)));
876 ash::ShelfModel* ChromeLauncherController::model() {
880 Profile* ChromeLauncherController::profile() {
884 ash::ShelfAutoHideBehavior ChromeLauncherController::GetShelfAutoHideBehavior(
885 aura::Window* root_window) const {
886 // Don't show the shelf in app mode.
887 if (chrome::IsRunningInAppMode())
888 return ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN;
890 // See comment in |kShelfAlignment| as to why we consider two prefs.
891 const std::string behavior_value(
892 GetPrefForRootWindow(profile_->GetPrefs(),
894 prefs::kShelfAutoHideBehaviorLocal,
895 prefs::kShelfAutoHideBehavior));
897 // Note: To maintain sync compatibility with old images of chrome/chromeos
898 // the set of values that may be encountered includes the now-extinct
899 // "Default" as well as "Never" and "Always", "Default" should now
900 // be treated as "Never" (http://crbug.com/146773).
901 if (behavior_value == ash::kShelfAutoHideBehaviorAlways)
902 return ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS;
903 return ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER;
906 bool ChromeLauncherController::CanUserModifyShelfAutoHideBehavior(
907 aura::Window* root_window) const {
908 return !ash::Shell::GetInstance()->IsMaximizeModeWindowManagerEnabled() &&
909 profile_->GetPrefs()->FindPreference(
910 prefs::kShelfAutoHideBehaviorLocal)->IsUserModifiable();
913 void ChromeLauncherController::ToggleShelfAutoHideBehavior(
914 aura::Window* root_window) {
915 ash::ShelfAutoHideBehavior behavior = GetShelfAutoHideBehavior(root_window) ==
916 ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS ?
917 ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER :
918 ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS;
919 SetShelfAutoHideBehaviorPrefs(behavior, root_window);
923 void ChromeLauncherController::UpdateAppState(content::WebContents* contents,
924 AppState app_state) {
925 std::string app_id = app_tab_helper_->GetAppID(contents);
927 // Check if the gMail app is loaded and it matches the given content.
928 // This special treatment is needed to address crbug.com/234268.
929 if (app_id.empty() && ContentCanBeHandledByGmailApp(contents))
930 app_id = kGmailAppId;
932 // Check the old |app_id| for a tab. If the contents has changed we need to
933 // remove it from the previous app.
934 if (web_contents_to_app_id_.find(contents) != web_contents_to_app_id_.end()) {
935 std::string last_app_id = web_contents_to_app_id_[contents];
936 if (last_app_id != app_id) {
937 ash::ShelfID id = GetShelfIDForAppID(last_app_id);
939 // Since GetAppState() will use |web_contents_to_app_id_| we remove
940 // the connection before calling it.
941 web_contents_to_app_id_.erase(contents);
942 SetItemStatus(id, GetAppState(last_app_id));
947 if (app_state == APP_STATE_REMOVED)
948 web_contents_to_app_id_.erase(contents);
950 web_contents_to_app_id_[contents] = app_id;
952 ash::ShelfID id = GetShelfIDForAppID(app_id);
954 SetItemStatus(id, (app_state == APP_STATE_WINDOW_ACTIVE ||
955 app_state == APP_STATE_ACTIVE) ? ash::STATUS_ACTIVE :
956 GetAppState(app_id));
960 ash::ShelfID ChromeLauncherController::GetShelfIDForWebContents(
961 content::WebContents* contents) {
964 std::string app_id = app_tab_helper_->GetAppID(contents);
966 if (app_id.empty() && ContentCanBeHandledByGmailApp(contents))
967 app_id = kGmailAppId;
969 ash::ShelfID id = GetShelfIDForAppID(app_id);
971 if (app_id.empty() || !id) {
972 int browser_index = model_->GetItemIndexForType(ash::TYPE_BROWSER_SHORTCUT);
973 return model_->items()[browser_index].id;
979 void ChromeLauncherController::SetRefocusURLPatternForTest(ash::ShelfID id,
981 DCHECK(HasItemController(id));
982 LauncherItemController* controller = id_to_item_controller_map_[id];
984 int index = model_->ItemIndexByID(id);
986 NOTREACHED() << "Invalid launcher id";
990 ash::ShelfItemType type = model_->items()[index].type;
991 if (type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_WINDOWED_APP) {
992 AppShortcutLauncherItemController* app_controller =
993 static_cast<AppShortcutLauncherItemController*>(controller);
994 app_controller->set_refocus_url(url);
996 NOTREACHED() << "Invalid launcher type";
1000 const Extension* ChromeLauncherController::GetExtensionForAppID(
1001 const std::string& app_id) const {
1002 // Some unit tests do not have a real extension.
1003 return (profile_->GetExtensionService()) ?
1004 profile_->GetExtensionService()->GetInstalledExtension(app_id) : NULL;
1007 void ChromeLauncherController::ActivateWindowOrMinimizeIfActive(
1008 ui::BaseWindow* window,
1009 bool allow_minimize) {
1010 // In separated desktop mode we might have to teleport a window back to the
1012 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
1013 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED) {
1014 aura::Window* native_window = window->GetNativeWindow();
1015 const std::string& current_user =
1016 multi_user_util::GetUserIDFromProfile(profile());
1017 chrome::MultiUserWindowManager* manager =
1018 chrome::MultiUserWindowManager::GetInstance();
1019 if (!manager->IsWindowOnDesktopOfUser(native_window, current_user)) {
1020 ash::MultiProfileUMA::RecordTeleportAction(
1021 ash::MultiProfileUMA::TELEPORT_WINDOW_RETURN_BY_LAUNCHER);
1022 manager->ShowWindowForUser(native_window, current_user);
1028 if (window->IsActive() && allow_minimize) {
1029 if (CommandLine::ForCurrentProcess()->HasSwitch(
1030 switches::kDisableMinimizeOnSecondLauncherItemClick)) {
1031 AnimateWindow(window->GetNativeWindow(),
1032 wm::WINDOW_ANIMATION_TYPE_BOUNCE);
1042 void ChromeLauncherController::OnShelfCreated(ash::Shelf* shelf) {
1043 shelves_.insert(shelf);
1044 shelf->shelf_widget()->shelf_layout_manager()->AddObserver(this);
1047 void ChromeLauncherController::OnShelfDestroyed(ash::Shelf* shelf) {
1048 shelves_.erase(shelf);
1049 // RemoveObserver is not called here, since by the time this method is called
1050 // Shelf is already in its destructor.
1053 void ChromeLauncherController::ShelfItemAdded(int index) {
1054 // The app list launcher can get added to the shelf after we applied the
1055 // preferences. In that case the item might be at the wrong spot. As such we
1056 // call the function again.
1057 if (model_->items()[index].type == ash::TYPE_APP_LIST)
1058 UpdateAppLaunchersFromPref();
1061 void ChromeLauncherController::ShelfItemRemoved(int index, ash::ShelfID id) {
1064 void ChromeLauncherController::ShelfItemMoved(int start_index,
1066 const ash::ShelfItem& item = model_->items()[target_index];
1067 // We remember the moved item position if it is either pinnable or
1068 // it is the app list with the alternate shelf layout.
1069 if ((HasItemController(item.id) && IsPinned(item.id)) ||
1070 item.type == ash::TYPE_APP_LIST)
1071 PersistPinnedState();
1074 void ChromeLauncherController::ShelfItemChanged(
1076 const ash::ShelfItem& old_item) {
1079 void ChromeLauncherController::ShelfStatusChanged() {
1082 void ChromeLauncherController::ActiveUserChanged(
1083 const std::string& user_email) {
1084 // Store the order of running applications for the user which gets inactive.
1085 RememberUnpinnedRunningApplicationOrder();
1086 // Coming here the default profile is already switched. All profile specific
1087 // resources get released and the new profile gets attached instead.
1089 // When coming here, the active user has already be changed so that we can
1090 // set it as active.
1091 AttachProfile(ProfileManager::GetActiveUserProfile());
1092 // Update the V1 applications.
1093 browser_status_monitor_->ActiveUserChanged(user_email);
1094 // Switch the running applications to the new user.
1095 app_window_controller_->ActiveUserChanged(user_email);
1096 // Update the user specific shell properties from the new user profile.
1097 UpdateAppLaunchersFromPref();
1098 SetShelfAlignmentFromPrefs();
1099 SetShelfAutoHideBehaviorFromPrefs();
1100 SetShelfBehaviorsFromPrefs();
1101 // Restore the order of running, but unpinned applications for the activated
1103 RestoreUnpinnedRunningApplicationOrder(user_email);
1104 // Inform the system tray of the change.
1105 ash::Shell::GetInstance()->system_tray_delegate()->ActiveUserWasChanged();
1106 // Force on-screen keyboard to reset.
1107 if (keyboard::IsKeyboardEnabled())
1108 ash::Shell::GetInstance()->CreateKeyboard();
1111 void ChromeLauncherController::AdditionalUserAddedToSession(Profile* profile) {
1112 // Switch the running applications to the new user.
1113 app_window_controller_->AdditionalUserAddedToSession(profile);
1116 void ChromeLauncherController::Observe(
1118 const content::NotificationSource& source,
1119 const content::NotificationDetails& details) {
1121 case chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED: {
1122 const Extension* extension =
1123 content::Details<const Extension>(details).ptr();
1124 if (IsAppPinned(extension->id())) {
1125 // Clear and re-fetch to ensure icon is up-to-date.
1126 app_icon_loader_->ClearImage(extension->id());
1127 app_icon_loader_->FetchImage(extension->id());
1130 UpdateAppLaunchersFromPref();
1133 case chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: {
1134 const content::Details<UnloadedExtensionInfo>& unload_info(details);
1135 const Extension* extension = unload_info->extension;
1136 const std::string& id = extension->id();
1137 // Since we might have windowed apps of this type which might have
1138 // outstanding locks which needs to be removed.
1139 if (GetShelfIDForAppID(id) &&
1140 unload_info->reason == UnloadedExtensionInfo::REASON_UNINSTALL) {
1141 CloseWindowedAppsFromRemovedExtension(id);
1144 if (IsAppPinned(id)) {
1145 if (unload_info->reason == UnloadedExtensionInfo::REASON_UNINSTALL) {
1146 DoUnpinAppWithID(id);
1147 app_icon_loader_->ClearImage(id);
1149 app_icon_loader_->UpdateImage(id);
1155 NOTREACHED() << "Unexpected notification type=" << type;
1159 void ChromeLauncherController::OnShelfAlignmentChanged(
1160 aura::Window* root_window) {
1161 const char* pref_value = NULL;
1162 switch (ash::Shell::GetInstance()->GetShelfAlignment(root_window)) {
1163 case ash::SHELF_ALIGNMENT_BOTTOM:
1164 pref_value = ash::kShelfAlignmentBottom;
1166 case ash::SHELF_ALIGNMENT_LEFT:
1167 pref_value = ash::kShelfAlignmentLeft;
1169 case ash::SHELF_ALIGNMENT_RIGHT:
1170 pref_value = ash::kShelfAlignmentRight;
1172 case ash::SHELF_ALIGNMENT_TOP:
1173 pref_value = ash::kShelfAlignmentTop;
1176 UpdatePerDisplayPref(
1177 profile_->GetPrefs(), root_window, prefs::kShelfAlignment, pref_value);
1179 if (root_window == ash::Shell::GetPrimaryRootWindow()) {
1180 // See comment in |kShelfAlignment| about why we have two prefs here.
1181 profile_->GetPrefs()->SetString(prefs::kShelfAlignmentLocal, pref_value);
1182 profile_->GetPrefs()->SetString(prefs::kShelfAlignment, pref_value);
1186 void ChromeLauncherController::OnDisplayConfigurationChanged() {
1187 SetShelfBehaviorsFromPrefs();
1190 void ChromeLauncherController::OnIsSyncingChanged() {
1191 PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_);
1192 MaybePropagatePrefToLocal(prefs,
1193 prefs::kShelfAlignmentLocal,
1194 prefs::kShelfAlignment);
1195 MaybePropagatePrefToLocal(prefs,
1196 prefs::kShelfAutoHideBehaviorLocal,
1197 prefs::kShelfAutoHideBehavior);
1200 void ChromeLauncherController::OnAppSyncUIStatusChanged() {
1201 if (app_sync_ui_state_->status() == AppSyncUIState::STATUS_SYNCING)
1202 model_->SetStatus(ash::ShelfModel::STATUS_LOADING);
1204 model_->SetStatus(ash::ShelfModel::STATUS_NORMAL);
1207 void ChromeLauncherController::ExtensionEnableFlowFinished() {
1208 LaunchApp(extension_enable_flow_->extension_id(),
1209 ash::LAUNCH_FROM_UNKNOWN,
1211 extension_enable_flow_.reset();
1214 void ChromeLauncherController::ExtensionEnableFlowAborted(bool user_initiated) {
1215 extension_enable_flow_.reset();
1218 ChromeLauncherAppMenuItems ChromeLauncherController::GetApplicationList(
1219 const ash::ShelfItem& item,
1221 // Make sure that there is a controller associated with the id and that the
1222 // extension itself is a valid application and not a panel.
1223 if (!HasItemController(item.id) ||
1224 !GetShelfIDForAppID(id_to_item_controller_map_[item.id]->app_id()))
1225 return ChromeLauncherAppMenuItems().Pass();
1227 return id_to_item_controller_map_[item.id]->GetApplicationList(event_flags);
1230 std::vector<content::WebContents*>
1231 ChromeLauncherController::GetV1ApplicationsFromAppId(std::string app_id) {
1232 ash::ShelfID id = GetShelfIDForAppID(app_id);
1234 // If there is no such an item pinned to the launcher, no menu gets created.
1236 LauncherItemController* controller = id_to_item_controller_map_[id];
1238 if (controller->type() == LauncherItemController::TYPE_SHORTCUT)
1239 return GetV1ApplicationsFromController(controller);
1241 return std::vector<content::WebContents*>();
1244 void ChromeLauncherController::ActivateShellApp(const std::string& app_id,
1246 ash::ShelfID id = GetShelfIDForAppID(app_id);
1248 LauncherItemController* controller = id_to_item_controller_map_[id];
1249 if (controller->type() == LauncherItemController::TYPE_APP) {
1250 AppWindowLauncherItemController* app_window_controller =
1251 static_cast<AppWindowLauncherItemController*>(controller);
1252 app_window_controller->ActivateIndexedApp(index);
1257 bool ChromeLauncherController::IsWebContentHandledByApplication(
1258 content::WebContents* web_contents,
1259 const std::string& app_id) {
1260 if ((web_contents_to_app_id_.find(web_contents) !=
1261 web_contents_to_app_id_.end()) &&
1262 (web_contents_to_app_id_[web_contents] == app_id))
1264 return (app_id == kGmailAppId && ContentCanBeHandledByGmailApp(web_contents));
1267 bool ChromeLauncherController::ContentCanBeHandledByGmailApp(
1268 content::WebContents* web_contents) {
1269 ash::ShelfID id = GetShelfIDForAppID(kGmailAppId);
1271 const GURL url = web_contents->GetURL();
1272 // We need to extend the application matching for the gMail app beyond the
1273 // manifest file's specification. This is required because of the namespace
1274 // overlap with the offline app ("/mail/mu/").
1275 if (!MatchPattern(url.path(), "/mail/mu/*") &&
1276 MatchPattern(url.path(), "/mail/*") &&
1277 GetExtensionForAppID(kGmailAppId) &&
1278 GetExtensionForAppID(kGmailAppId)->OverlapsWithOrigin(url))
1284 gfx::Image ChromeLauncherController::GetAppListIcon(
1285 content::WebContents* web_contents) const {
1286 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
1287 if (IsIncognito(web_contents))
1288 return rb.GetImageNamed(IDR_ASH_SHELF_LIST_INCOGNITO_BROWSER);
1289 FaviconTabHelper* favicon_tab_helper =
1290 FaviconTabHelper::FromWebContents(web_contents);
1291 gfx::Image result = favicon_tab_helper->GetFavicon();
1292 if (result.IsEmpty())
1293 return rb.GetImageNamed(IDR_DEFAULT_FAVICON);
1297 base::string16 ChromeLauncherController::GetAppListTitle(
1298 content::WebContents* web_contents) const {
1299 base::string16 title = web_contents->GetTitle();
1302 WebContentsToAppIDMap::const_iterator iter =
1303 web_contents_to_app_id_.find(web_contents);
1304 if (iter != web_contents_to_app_id_.end()) {
1305 std::string app_id = iter->second;
1306 const extensions::Extension* extension = GetExtensionForAppID(app_id);
1308 return base::UTF8ToUTF16(extension->name());
1310 return l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE);
1313 ash::ShelfID ChromeLauncherController::CreateAppShortcutLauncherItem(
1314 const std::string& app_id,
1316 return CreateAppShortcutLauncherItemWithType(app_id,
1318 ash::TYPE_APP_SHORTCUT);
1321 void ChromeLauncherController::SetAppTabHelperForTest(AppTabHelper* helper) {
1322 app_tab_helper_.reset(helper);
1325 void ChromeLauncherController::SetAppIconLoaderForTest(
1326 extensions::AppIconLoader* loader) {
1327 app_icon_loader_.reset(loader);
1330 const std::string& ChromeLauncherController::GetAppIdFromShelfIdForTest(
1332 return id_to_item_controller_map_[id]->app_id();
1335 void ChromeLauncherController::SetShelfItemDelegateManagerForTest(
1336 ash::ShelfItemDelegateManager* manager) {
1337 item_delegate_manager_ = manager;
1340 void ChromeLauncherController::RememberUnpinnedRunningApplicationOrder() {
1341 RunningAppListIds list;
1342 for (int i = 0; i < model_->item_count(); i++) {
1343 ash::ShelfItemType type = model_->items()[i].type;
1344 if (type == ash::TYPE_WINDOWED_APP || type == ash::TYPE_PLATFORM_APP)
1345 list.push_back(GetAppIDForShelfID(model_->items()[i].id));
1347 last_used_running_application_order_[
1348 multi_user_util::GetUserIDFromProfile(profile_)] = list;
1351 void ChromeLauncherController::RestoreUnpinnedRunningApplicationOrder(
1352 const std::string& user_id) {
1353 const RunningAppListIdMap::iterator app_id_list =
1354 last_used_running_application_order_.find(user_id);
1355 if (app_id_list == last_used_running_application_order_.end())
1358 // Find the first insertion point for running applications.
1359 int running_index = model_->FirstRunningAppIndex();
1360 for (RunningAppListIds::iterator app_id = app_id_list->second.begin();
1361 app_id != app_id_list->second.end(); ++app_id) {
1362 ash::ShelfID shelf_id = GetShelfIDForAppID(*app_id);
1364 int app_index = model_->ItemIndexByID(shelf_id);
1365 DCHECK_GE(app_index, 0);
1366 ash::ShelfItemType type = model_->items()[app_index].type;
1367 if (type == ash::TYPE_WINDOWED_APP || type == ash::TYPE_PLATFORM_APP) {
1368 if (running_index != app_index)
1369 model_->Move(running_index, app_index);
1376 ash::ShelfID ChromeLauncherController::CreateAppShortcutLauncherItemWithType(
1377 const std::string& app_id,
1379 ash::ShelfItemType shelf_item_type) {
1380 AppShortcutLauncherItemController* controller =
1381 new AppShortcutLauncherItemController(app_id, this);
1382 ash::ShelfID shelf_id = InsertAppLauncherItem(
1383 controller, app_id, ash::STATUS_CLOSED, index, shelf_item_type);
1387 LauncherItemController* ChromeLauncherController::GetLauncherItemController(
1388 const ash::ShelfID id) {
1389 if (!HasItemController(id))
1391 return id_to_item_controller_map_[id];
1394 bool ChromeLauncherController::IsBrowserFromActiveUser(Browser* browser) {
1395 // If running multi user mode with separate desktops, we have to check if the
1396 // browser is from the active user.
1397 if (chrome::MultiUserWindowManager::GetMultiProfileMode() !=
1398 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED)
1400 return multi_user_util::IsProfileFromActiveUser(browser->profile());
1403 void ChromeLauncherController::LauncherItemClosed(ash::ShelfID id) {
1404 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
1405 CHECK(iter != id_to_item_controller_map_.end());
1406 CHECK(iter->second);
1407 app_icon_loader_->ClearImage(iter->second->app_id());
1408 id_to_item_controller_map_.erase(iter);
1409 int index = model_->ItemIndexByID(id);
1410 // A "browser proxy" is not known to the model and this removal does
1411 // therefore not need to be propagated to the model.
1413 model_->RemoveItemAt(index);
1416 void ChromeLauncherController::DoPinAppWithID(const std::string& app_id) {
1417 // If there is an item, do nothing and return.
1418 if (IsAppPinned(app_id))
1421 ash::ShelfID shelf_id = GetShelfIDForAppID(app_id);
1423 // App item exists, pin it
1426 // Otherwise, create a shortcut item for it.
1427 CreateAppShortcutLauncherItem(app_id, model_->item_count());
1429 PersistPinnedState();
1433 void ChromeLauncherController::DoUnpinAppWithID(const std::string& app_id) {
1434 ash::ShelfID shelf_id = GetShelfIDForAppID(app_id);
1435 if (shelf_id && IsPinned(shelf_id))
1439 int ChromeLauncherController::PinRunningAppInternal(int index,
1440 ash::ShelfID shelf_id) {
1441 int running_index = model_->ItemIndexByID(shelf_id);
1442 ash::ShelfItem item = model_->items()[running_index];
1443 DCHECK(item.type == ash::TYPE_WINDOWED_APP ||
1444 item.type == ash::TYPE_PLATFORM_APP);
1445 item.type = ash::TYPE_APP_SHORTCUT;
1446 model_->Set(running_index, item);
1447 // The |ShelfModel|'s weight system might reposition the item to a
1448 // new index, so we get the index again.
1449 running_index = model_->ItemIndexByID(shelf_id);
1450 if (running_index < index)
1452 if (running_index != index)
1453 model_->Move(running_index, index);
1457 void ChromeLauncherController::UnpinRunningAppInternal(int index) {
1458 DCHECK_GE(index, 0);
1459 ash::ShelfItem item = model_->items()[index];
1460 DCHECK_EQ(item.type, ash::TYPE_APP_SHORTCUT);
1461 item.type = ash::TYPE_WINDOWED_APP;
1462 // A platform app and a windowed app are sharing TYPE_APP_SHORTCUT. As such
1463 // we have to check here what this was before it got a shortcut.
1464 if (HasItemController(item.id) &&
1465 id_to_item_controller_map_[item.id]->type() ==
1466 LauncherItemController::TYPE_APP)
1467 item.type = ash::TYPE_PLATFORM_APP;
1468 model_->Set(index, item);
1471 void ChromeLauncherController::UpdateAppLaunchersFromPref() {
1472 // There are various functions which will trigger a |PersistPinnedState| call
1473 // like a direct call to |DoPinAppWithID|, or an indirect call to the menu
1474 // model which will use weights to re-arrange the icons to new positions.
1475 // Since this function is meant to synchronize the "is state" with the
1476 // "sync state", it makes no sense to store any changes by this function back
1477 // into the pref state. Therefore we tell |persistPinnedState| to ignore any
1478 // invocations while we are running.
1479 base::AutoReset<bool> auto_reset(&ignore_persist_pinned_state_change_, true);
1480 std::vector<std::string> pinned_apps = GetListOfPinnedAppsAndBrowser();
1483 int max_index = model_->item_count();
1485 // When one of the two special items cannot be moved (and we do not know where
1486 // yet), we remember the current location in one of these variables.
1487 int chrome_index = -1;
1488 int app_list_index = -1;
1490 // Walk the model and |pinned_apps| from the pref lockstep, adding and
1491 // removing items as necessary. NB: This code uses plain old indexing instead
1492 // of iterators because of model mutations as part of the loop.
1493 std::vector<std::string>::const_iterator pref_app_id(pinned_apps.begin());
1494 for (; index < max_index && pref_app_id != pinned_apps.end(); ++index) {
1495 // Check if we have an item which we need to handle.
1496 if (*pref_app_id == extension_misc::kChromeAppId ||
1497 *pref_app_id == kAppShelfIdPlaceholder ||
1498 IsAppPinned(*pref_app_id)) {
1499 for (; index < max_index; ++index) {
1500 const ash::ShelfItem& item(model_->items()[index]);
1501 bool is_app_list = item.type == ash::TYPE_APP_LIST;
1502 bool is_chrome = item.type == ash::TYPE_BROWSER_SHORTCUT;
1503 if (item.type != ash::TYPE_APP_SHORTCUT && !is_app_list && !is_chrome)
1505 IDToItemControllerMap::const_iterator entry =
1506 id_to_item_controller_map_.find(item.id);
1507 if ((kAppShelfIdPlaceholder == *pref_app_id && is_app_list) ||
1508 (extension_misc::kChromeAppId == *pref_app_id && is_chrome) ||
1509 (entry != id_to_item_controller_map_.end() &&
1510 entry->second->app_id() == *pref_app_id)) {
1511 // Check if an item needs to be moved here.
1512 MoveChromeOrApplistToFinalPosition(
1513 is_chrome, is_app_list, index, &chrome_index, &app_list_index);
1517 if (is_chrome || is_app_list) {
1518 // We cannot delete any of these shortcuts. As such we remember
1519 // their positions and move them later where they belong.
1521 chrome_index = index;
1523 app_list_index = index;
1524 // And skip the item - or exit the loop if end is reached (note that
1525 // in that case we will reduce the index again by one and this only
1526 // compensates for it).
1527 if (index >= max_index - 1)
1531 // Check if this is a platform or a windowed app.
1532 if (item.type == ash::TYPE_APP_SHORTCUT &&
1533 (id_to_item_controller_map_[item.id]->locked() ||
1534 id_to_item_controller_map_[item.id]->type() ==
1535 LauncherItemController::TYPE_APP)) {
1536 // Note: This will not change the amount of items (|max_index|).
1537 // Even changes to the actual |index| due to item weighting
1538 // changes should be fine.
1539 UnpinRunningAppInternal(index);
1541 LauncherItemClosed(item.id);
1548 // If the item wasn't found, that means id_to_item_controller_map_
1550 DCHECK(index <= max_index);
1552 // Check if the item was already running but not yet pinned.
1553 ash::ShelfID shelf_id = GetShelfIDForAppID(*pref_app_id);
1555 // This app is running but not yet pinned. So pin and move it.
1556 index = PinRunningAppInternal(index, shelf_id);
1558 // This app wasn't pinned before, insert a new entry.
1559 shelf_id = CreateAppShortcutLauncherItem(*pref_app_id, index);
1561 index = model_->ItemIndexByID(shelf_id);
1567 // Remove any trailing existing items.
1568 while (index < model_->item_count()) {
1569 const ash::ShelfItem& item(model_->items()[index]);
1570 if (item.type == ash::TYPE_APP_SHORTCUT) {
1571 if (id_to_item_controller_map_[item.id]->locked() ||
1572 id_to_item_controller_map_[item.id]->type() ==
1573 LauncherItemController::TYPE_APP)
1574 UnpinRunningAppInternal(index);
1576 LauncherItemClosed(item.id);
1578 if (item.type == ash::TYPE_BROWSER_SHORTCUT)
1579 chrome_index = index;
1580 else if (item.type == ash::TYPE_APP_LIST)
1581 app_list_index = index;
1586 // Append unprocessed items from the pref to the end of the model.
1587 for (; pref_app_id != pinned_apps.end(); ++pref_app_id) {
1588 // All items but the chrome and / or app list shortcut needs to be added.
1589 bool is_chrome = *pref_app_id == extension_misc::kChromeAppId;
1590 bool is_app_list = *pref_app_id == kAppShelfIdPlaceholder;
1591 // Coming here we know the next item which can be finalized, either the
1592 // chrome item or the app launcher. The final position is the end of the
1593 // list. The menu model will make sure that the item is grouped according
1594 // to its weight (which we do not know here).
1595 if (!is_chrome && !is_app_list) {
1596 DoPinAppWithID(*pref_app_id);
1597 int target_index = FindInsertionPoint(false);
1598 ash::ShelfID id = GetShelfIDForAppID(*pref_app_id);
1599 int source_index = model_->ItemIndexByID(id);
1600 if (source_index != target_index)
1601 model_->Move(source_index, target_index);
1603 // Needed for the old layout - the weight might force it to be lower in
1605 if (app_list_index != -1 && target_index <= app_list_index)
1608 int target_index = FindInsertionPoint(is_app_list);
1609 MoveChromeOrApplistToFinalPosition(
1610 is_chrome, is_app_list, target_index, &chrome_index, &app_list_index);
1615 void ChromeLauncherController::SetShelfAutoHideBehaviorPrefs(
1616 ash::ShelfAutoHideBehavior behavior,
1617 aura::Window* root_window) {
1618 const char* value = NULL;
1620 case ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS:
1621 value = ash::kShelfAutoHideBehaviorAlways;
1623 case ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER:
1624 value = ash::kShelfAutoHideBehaviorNever;
1626 case ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN:
1627 // This one should not be a valid preference option for now. We only want
1628 // to completely hide it when we run in app mode - or while we temporarily
1629 // hide the shelf as part of an animation (e.g. the multi user change).
1633 UpdatePerDisplayPref(
1634 profile_->GetPrefs(), root_window, prefs::kShelfAutoHideBehavior, value);
1636 if (root_window == ash::Shell::GetPrimaryRootWindow()) {
1637 // See comment in |kShelfAlignment| about why we have two prefs here.
1638 profile_->GetPrefs()->SetString(prefs::kShelfAutoHideBehaviorLocal, value);
1639 profile_->GetPrefs()->SetString(prefs::kShelfAutoHideBehavior, value);
1643 void ChromeLauncherController::SetShelfAutoHideBehaviorFromPrefs() {
1644 aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
1646 for (aura::Window::Windows::const_iterator iter = root_windows.begin();
1647 iter != root_windows.end(); ++iter) {
1648 ash::Shell::GetInstance()->SetShelfAutoHideBehavior(
1649 GetShelfAutoHideBehavior(*iter), *iter);
1653 void ChromeLauncherController::SetShelfAlignmentFromPrefs() {
1654 if (!ash::ShelfWidget::ShelfAlignmentAllowed())
1657 aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
1659 for (aura::Window::Windows::const_iterator iter = root_windows.begin();
1660 iter != root_windows.end(); ++iter) {
1661 // See comment in |kShelfAlignment| as to why we consider two prefs.
1662 const std::string alignment_value(
1663 GetPrefForRootWindow(profile_->GetPrefs(),
1665 prefs::kShelfAlignmentLocal,
1666 prefs::kShelfAlignment));
1667 ash::ShelfAlignment alignment = ash::SHELF_ALIGNMENT_BOTTOM;
1668 if (alignment_value == ash::kShelfAlignmentLeft)
1669 alignment = ash::SHELF_ALIGNMENT_LEFT;
1670 else if (alignment_value == ash::kShelfAlignmentRight)
1671 alignment = ash::SHELF_ALIGNMENT_RIGHT;
1672 else if (alignment_value == ash::kShelfAlignmentTop)
1673 alignment = ash::SHELF_ALIGNMENT_TOP;
1674 ash::Shell::GetInstance()->SetShelfAlignment(alignment, *iter);
1678 void ChromeLauncherController::SetShelfBehaviorsFromPrefs() {
1679 SetShelfAutoHideBehaviorFromPrefs();
1680 SetShelfAlignmentFromPrefs();
1683 ash::ShelfItemStatus ChromeLauncherController::GetAppState(
1684 const::std::string& app_id) {
1685 ash::ShelfItemStatus status = ash::STATUS_CLOSED;
1686 for (WebContentsToAppIDMap::iterator it = web_contents_to_app_id_.begin();
1687 it != web_contents_to_app_id_.end();
1689 if (it->second == app_id) {
1690 Browser* browser = chrome::FindBrowserWithWebContents(it->first);
1691 // Usually there should never be an item in our |web_contents_to_app_id_|
1692 // list which got deleted already. However - in some situations e.g.
1693 // Browser::SwapTabContent there is temporarily no associated browser.
1696 if (browser->window()->IsActive()) {
1697 return browser->tab_strip_model()->GetActiveWebContents() == it->first ?
1698 ash::STATUS_ACTIVE : ash::STATUS_RUNNING;
1700 status = ash::STATUS_RUNNING;
1707 ash::ShelfID ChromeLauncherController::InsertAppLauncherItem(
1708 LauncherItemController* controller,
1709 const std::string& app_id,
1710 ash::ShelfItemStatus status,
1712 ash::ShelfItemType shelf_item_type) {
1713 ash::ShelfID id = model_->next_id();
1714 CHECK(!HasItemController(id));
1716 id_to_item_controller_map_[id] = controller;
1717 controller->set_shelf_id(id);
1719 ash::ShelfItem item;
1720 item.type = shelf_item_type;
1721 item.image = extensions::util::GetDefaultAppIcon();
1723 ash::ShelfItemStatus new_state = GetAppState(app_id);
1724 if (new_state != ash::STATUS_CLOSED)
1727 item.status = status;
1729 model_->AddAt(index, item);
1731 app_icon_loader_->FetchImage(app_id);
1733 SetShelfItemDelegate(id, controller);
1738 bool ChromeLauncherController::HasItemController(ash::ShelfID id) const {
1739 return id_to_item_controller_map_.find(id) !=
1740 id_to_item_controller_map_.end();
1743 std::vector<content::WebContents*>
1744 ChromeLauncherController::GetV1ApplicationsFromController(
1745 LauncherItemController* controller) {
1746 DCHECK(controller->type() == LauncherItemController::TYPE_SHORTCUT);
1747 AppShortcutLauncherItemController* app_controller =
1748 static_cast<AppShortcutLauncherItemController*>(controller);
1749 return app_controller->GetRunningApplications();
1752 BrowserShortcutLauncherItemController*
1753 ChromeLauncherController::GetBrowserShortcutLauncherItemController() {
1754 for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin();
1755 i != id_to_item_controller_map_.end(); ++i) {
1756 int index = model_->ItemIndexByID(i->first);
1757 const ash::ShelfItem& item = model_->items()[index];
1758 if (item.type == ash::TYPE_BROWSER_SHORTCUT)
1759 return static_cast<BrowserShortcutLauncherItemController*>(i->second);
1761 // Create a LauncherItemController for the Browser shortcut if it does not
1763 ash::ShelfID id = CreateBrowserShortcutLauncherItem();
1764 DCHECK(id_to_item_controller_map_[id]);
1765 return static_cast<BrowserShortcutLauncherItemController*>(
1766 id_to_item_controller_map_[id]);
1769 ash::ShelfID ChromeLauncherController::CreateBrowserShortcutLauncherItem() {
1770 ash::ShelfItem browser_shortcut;
1771 browser_shortcut.type = ash::TYPE_BROWSER_SHORTCUT;
1772 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
1773 browser_shortcut.image = *rb.GetImageSkiaNamed(IDR_PRODUCT_LOGO_32);
1774 ash::ShelfID id = model_->next_id();
1775 size_t index = GetChromeIconIndexForCreation();
1776 model_->AddAt(index, browser_shortcut);
1777 id_to_item_controller_map_[id] =
1778 new BrowserShortcutLauncherItemController(this);
1779 id_to_item_controller_map_[id]->set_shelf_id(id);
1780 // ShelfItemDelegateManager owns BrowserShortcutLauncherItemController.
1781 SetShelfItemDelegate(id, id_to_item_controller_map_[id]);
1785 void ChromeLauncherController::PersistChromeItemIndex(int index) {
1786 profile_->GetPrefs()->SetInteger(prefs::kShelfChromeIconIndex, index);
1789 int ChromeLauncherController::GetChromeIconIndexFromPref() const {
1790 size_t index = profile_->GetPrefs()->GetInteger(prefs::kShelfChromeIconIndex);
1791 const base::ListValue* pinned_apps_pref =
1792 profile_->GetPrefs()->GetList(prefs::kPinnedLauncherApps);
1793 return std::max(static_cast<size_t>(0),
1794 std::min(pinned_apps_pref->GetSize(), index));
1797 void ChromeLauncherController::MoveChromeOrApplistToFinalPosition(
1802 int* app_list_index) {
1803 if (is_chrome && *chrome_index != -1) {
1804 model_->Move(*chrome_index, target_index);
1805 if (*app_list_index != -1 &&
1806 *chrome_index < *app_list_index &&
1807 target_index > *app_list_index)
1808 --(*app_list_index);
1810 } else if (is_app_list && *app_list_index != -1) {
1811 model_->Move(*app_list_index, target_index);
1812 if (*chrome_index != -1 &&
1813 *app_list_index < *chrome_index &&
1814 target_index > *chrome_index)
1816 *app_list_index = -1;
1820 int ChromeLauncherController::FindInsertionPoint(bool is_app_list) {
1821 // Keeping this change small to backport to M33&32 (see crbug.com/329597).
1822 // TODO(skuhne): With the removal of the legacy shelf layout we should remove
1823 // the ability to move the app list item since this was never used. We should
1824 // instead ask the ShelfModel::ValidateInsertionIndex or similir for an index.
1828 for (int i = model_->item_count() - 1; i > 0; --i) {
1829 ash::ShelfItemType type = model_->items()[i].type;
1830 if (type == ash::TYPE_APP_SHORTCUT ||
1831 (is_app_list && type == ash::TYPE_APP_LIST) ||
1832 type == ash::TYPE_BROWSER_SHORTCUT) {
1839 int ChromeLauncherController::GetChromeIconIndexForCreation() {
1840 // We get the list of pinned apps as they currently would get pinned.
1841 // Within this list the chrome icon will be the correct location.
1842 std::vector<std::string> pinned_apps = GetListOfPinnedAppsAndBrowser();
1844 std::vector<std::string>::iterator it =
1845 std::find(pinned_apps.begin(),
1847 std::string(extension_misc::kChromeAppId));
1848 DCHECK(it != pinned_apps.end());
1849 int index = it - pinned_apps.begin();
1851 // We should do here a comparison between the is state and the "want to be"
1852 // state since some apps might be able to pin but are not yet. Instead - for
1853 // the time being we clamp against the amount of known items and wait for the
1854 // next |UpdateAppLaunchersFromPref()| call to correct it - it will come since
1855 // the pinning will be done then.
1856 return std::min(model_->item_count(), index);
1859 std::vector<std::string>
1860 ChromeLauncherController::GetListOfPinnedAppsAndBrowser() {
1861 // Adding the app list item to the list of items requires that the ID is not
1862 // a valid and known ID for the extension system. The ID was constructed that
1863 // way - but just to make sure...
1864 DCHECK(!app_tab_helper_->IsValidIDForCurrentUser(kAppShelfIdPlaceholder));
1866 std::vector<std::string> pinned_apps;
1868 // Get the new incarnation of the list.
1869 const base::ListValue* pinned_apps_pref =
1870 profile_->GetPrefs()->GetList(prefs::kPinnedLauncherApps);
1872 // Keep track of the addition of the chrome and the app list icon.
1873 bool chrome_icon_added = false;
1874 bool app_list_icon_added = false;
1875 size_t chrome_icon_index = GetChromeIconIndexFromPref();
1877 // See if the chrome string is already in the pinned list and remove it if
1879 base::Value* chrome_app = ash::CreateAppDict(extension_misc::kChromeAppId);
1881 chrome_icon_added = pinned_apps_pref->Find(*chrome_app) !=
1882 pinned_apps_pref->end();
1886 for (size_t index = 0; index < pinned_apps_pref->GetSize(); ++index) {
1887 // We need to position the chrome icon relative to it's place in the pinned
1888 // preference list - even if an item of that list isn't shown yet.
1889 if (index == chrome_icon_index && !chrome_icon_added) {
1890 pinned_apps.push_back(extension_misc::kChromeAppId);
1891 chrome_icon_added = true;
1893 const base::DictionaryValue* app = NULL;
1895 if (pinned_apps_pref->GetDictionary(index, &app) &&
1896 app->GetString(ash::kPinnedAppsPrefAppIDPath, &app_id) &&
1897 (std::find(pinned_apps.begin(), pinned_apps.end(), app_id) ==
1898 pinned_apps.end())) {
1899 if (app_id == extension_misc::kChromeAppId) {
1900 chrome_icon_added = true;
1901 pinned_apps.push_back(extension_misc::kChromeAppId);
1902 } else if (app_id == kAppShelfIdPlaceholder) {
1903 app_list_icon_added = true;
1904 pinned_apps.push_back(kAppShelfIdPlaceholder);
1905 } else if (app_tab_helper_->IsValidIDForCurrentUser(app_id)) {
1906 // Note: In multi profile scenarios we only want to show pinnable apps
1907 // here which is correct. Running applications from the other users will
1908 // continue to run. So no need for multi profile modifications.
1909 pinned_apps.push_back(app_id);
1914 // If not added yet, the chrome item will be the last item in the list.
1915 if (!chrome_icon_added)
1916 pinned_apps.push_back(extension_misc::kChromeAppId);
1918 // If not added yet, add the app list item either at the end or at the
1919 // beginning - depending on the shelf layout.
1920 if (!app_list_icon_added) {
1921 pinned_apps.insert(pinned_apps.begin(), kAppShelfIdPlaceholder);
1926 bool ChromeLauncherController::IsIncognito(
1927 const content::WebContents* web_contents) const {
1928 const Profile* profile =
1929 Profile::FromBrowserContext(web_contents->GetBrowserContext());
1930 return profile->IsOffTheRecord() && !profile->IsGuestSession();
1933 void ChromeLauncherController::CloseWindowedAppsFromRemovedExtension(
1934 const std::string& app_id) {
1935 // This function cannot rely on the controller's enumeration functionality
1936 // since the extension has already be unloaded.
1937 const BrowserList* ash_browser_list =
1938 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
1939 std::vector<Browser*> browser_to_close;
1940 for (BrowserList::const_reverse_iterator
1941 it = ash_browser_list->begin_last_active();
1942 it != ash_browser_list->end_last_active(); ++it) {
1943 Browser* browser = *it;
1944 if (!browser->is_type_tabbed() &&
1945 browser->is_type_popup() &&
1946 browser->is_app() &&
1947 app_id == web_app::GetExtensionIdFromApplicationName(
1948 browser->app_name())) {
1949 browser_to_close.push_back(browser);
1952 while (!browser_to_close.empty()) {
1953 TabStripModel* tab_strip = browser_to_close.back()->tab_strip_model();
1954 tab_strip->CloseWebContentsAt(0, TabStripModel::CLOSE_NONE);
1955 browser_to_close.pop_back();
1959 void ChromeLauncherController::SetShelfItemDelegate(
1961 ash::ShelfItemDelegate* item_delegate) {
1963 DCHECK(item_delegate);
1964 DCHECK(item_delegate_manager_);
1965 item_delegate_manager_->SetShelfItemDelegate(
1966 id, scoped_ptr<ash::ShelfItemDelegate>(item_delegate).Pass());
1969 void ChromeLauncherController::AttachProfile(Profile* profile) {
1971 // Either add the profile to the list of known profiles and make it the active
1972 // one for some functions of AppTabHelper or create a new one.
1973 if (!app_tab_helper_.get())
1974 app_tab_helper_.reset(new LauncherAppTabHelper(profile_));
1976 app_tab_helper_->SetCurrentUser(profile_);
1977 // TODO(skuhne): The AppIconLoaderImpl has the same problem. Each loaded
1978 // image is associated with a profile (it's loader requires the profile).
1979 // Since icon size changes are possible, the icon could be requested to be
1980 // reloaded. However - having it not multi profile aware would cause problems
1981 // if the icon cache gets deleted upon user switch.
1982 app_icon_loader_.reset(new extensions::AppIconLoaderImpl(
1983 profile_, extension_misc::EXTENSION_ICON_SMALL, this));
1985 pref_change_registrar_.Init(profile_->GetPrefs());
1986 pref_change_registrar_.Add(
1987 prefs::kPinnedLauncherApps,
1988 base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref,
1989 base::Unretained(this)));
1990 pref_change_registrar_.Add(
1991 prefs::kShelfAlignmentLocal,
1992 base::Bind(&ChromeLauncherController::SetShelfAlignmentFromPrefs,
1993 base::Unretained(this)));
1994 pref_change_registrar_.Add(
1995 prefs::kShelfAutoHideBehaviorLocal,
1996 base::Bind(&ChromeLauncherController::
1997 SetShelfAutoHideBehaviorFromPrefs,
1998 base::Unretained(this)));
1999 pref_change_registrar_.Add(
2000 prefs::kShelfPreferences,
2001 base::Bind(&ChromeLauncherController::SetShelfBehaviorsFromPrefs,
2002 base::Unretained(this)));
2005 void ChromeLauncherController::ReleaseProfile() {
2006 if (app_sync_ui_state_)
2007 app_sync_ui_state_->RemoveObserver(this);
2009 PrefServiceSyncable::FromProfile(profile_)->RemoveObserver(this);
2011 pref_change_registrar_.RemoveAll();