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/wm/core/window_animations.h"
89 #if defined(OS_CHROMEOS)
90 #include "chrome/browser/browser_process.h"
91 #include "chrome/browser/chromeos/login/user_manager.h"
92 #include "chrome/browser/ui/ash/chrome_shell_delegate.h"
93 #include "chrome/browser/ui/ash/launcher/multi_profile_app_window_launcher_controller.h"
94 #include "chrome/browser/ui/ash/launcher/multi_profile_browser_status_monitor.h"
97 using extensions::Extension
;
98 using extensions::UnloadedExtensionInfo
;
99 using extension_misc::kGmailAppId
;
100 using content::WebContents
;
103 ChromeLauncherController
* ChromeLauncherController::instance_
= NULL
;
107 // This will be used as placeholder in the list of the pinned applciatons.
108 // Note that this is NOT a valid extension identifier so that pre M31 versions
110 const char kAppShelfIdPlaceholder
[] = "AppShelfIDPlaceholder--------";
112 std::string
GetPrefKeyForRootWindow(aura::Window
* root_window
) {
113 gfx::Display display
= gfx::Screen::GetScreenFor(
114 root_window
)->GetDisplayNearestWindow(root_window
);
115 DCHECK(display
.is_valid());
117 return base::Int64ToString(display
.id());
120 void UpdatePerDisplayPref(PrefService
* pref_service
,
121 aura::Window
* root_window
,
122 const char* pref_key
,
123 const std::string
& value
) {
124 std::string key
= GetPrefKeyForRootWindow(root_window
);
128 DictionaryPrefUpdate
update(pref_service
, prefs::kShelfPreferences
);
129 base::DictionaryValue
* shelf_prefs
= update
.Get();
130 base::DictionaryValue
* prefs
= NULL
;
131 if (!shelf_prefs
->GetDictionary(key
, &prefs
)) {
132 prefs
= new base::DictionaryValue();
133 shelf_prefs
->Set(key
, prefs
);
135 prefs
->SetStringWithoutPathExpansion(pref_key
, value
);
138 // Returns a pref value in |pref_service| for the display of |root_window|. The
139 // pref value is stored in |local_path| and |path|, but |pref_service| may have
140 // per-display preferences and the value can be specified by policy. Here is
142 // * A value managed by policy. This is a single value that applies to all
144 // * A user-set value for the specified display.
145 // * A user-set value in |local_path| or |path|, if no per-display settings are
146 // ever specified (see http://crbug.com/173719 for why). |local_path| is
147 // preferred. See comment in |kShelfAlignment| as to why we consider two
148 // prefs and why |local_path| is preferred.
149 // * A value recommended by policy. This is a single value that applies to all
151 // * The default value for |local_path| if the value is not recommended by
153 std::string
GetPrefForRootWindow(PrefService
* pref_service
,
154 aura::Window
* root_window
,
155 const char* local_path
,
157 const PrefService::Preference
* local_pref
=
158 pref_service
->FindPreference(local_path
);
159 const std::string
value(pref_service
->GetString(local_path
));
160 if (local_pref
->IsManaged())
163 std::string pref_key
= GetPrefKeyForRootWindow(root_window
);
164 bool has_per_display_prefs
= false;
165 if (!pref_key
.empty()) {
166 const base::DictionaryValue
* shelf_prefs
= pref_service
->GetDictionary(
167 prefs::kShelfPreferences
);
168 const base::DictionaryValue
* display_pref
= NULL
;
169 std::string per_display_value
;
170 if (shelf_prefs
->GetDictionary(pref_key
, &display_pref
) &&
171 display_pref
->GetString(path
, &per_display_value
))
172 return per_display_value
;
174 // If the pref for the specified display is not found, scan the whole prefs
175 // and check if the prefs for other display is already specified.
176 std::string unused_value
;
177 for (base::DictionaryValue::Iterator
iter(*shelf_prefs
);
178 !iter
.IsAtEnd(); iter
.Advance()) {
179 const base::DictionaryValue
* display_pref
= NULL
;
180 if (iter
.value().GetAsDictionary(&display_pref
) &&
181 display_pref
->GetString(path
, &unused_value
)) {
182 has_per_display_prefs
= true;
188 if (local_pref
->IsRecommended() || !has_per_display_prefs
)
191 const base::Value
* default_value
=
192 pref_service
->GetDefaultPrefValue(local_path
);
193 std::string default_string
;
194 default_value
->GetAsString(&default_string
);
195 return default_string
;
198 // If prefs have synced and no user-set value exists at |local_path|, the value
199 // from |synced_path| is copied to |local_path|.
200 void MaybePropagatePrefToLocal(PrefServiceSyncable
* pref_service
,
201 const char* local_path
,
202 const char* synced_path
) {
203 if (!pref_service
->FindPreference(local_path
)->HasUserSetting() &&
204 pref_service
->IsSyncing()) {
205 // First time the user is using this machine, propagate from remote to
207 pref_service
->SetString(local_path
, pref_service
->GetString(synced_path
));
211 std::string
GetSourceFromAppListSource(ash::LaunchSource source
) {
213 case ash::LAUNCH_FROM_APP_LIST
:
214 return std::string(extension_urls::kLaunchSourceAppList
);
215 case ash::LAUNCH_FROM_APP_LIST_SEARCH
:
216 return std::string(extension_urls::kLaunchSourceAppListSearch
);
217 default: return std::string();
223 #if defined(OS_CHROMEOS)
224 // A class to get events from ChromeOS when a user gets changed or added.
225 class ChromeLauncherControllerUserSwitchObserverChromeOS
226 : public ChromeLauncherControllerUserSwitchObserver
,
227 public chromeos::UserManager::UserSessionStateObserver
,
228 content::NotificationObserver
{
230 ChromeLauncherControllerUserSwitchObserverChromeOS(
231 ChromeLauncherController
* controller
)
232 : controller_(controller
) {
233 DCHECK(chromeos::UserManager::IsInitialized());
234 chromeos::UserManager::Get()->AddSessionStateObserver(this);
235 // A UserAddedToSession notification can be sent before a profile is loaded.
236 // Since our observers require that we have already a profile, we might have
237 // to postpone the notification until the ProfileManager lets us know that
238 // the profile for that newly added user was added to the ProfileManager.
239 registrar_
.Add(this, chrome::NOTIFICATION_PROFILE_ADDED
,
240 content::NotificationService::AllSources());
242 virtual ~ChromeLauncherControllerUserSwitchObserverChromeOS() {
243 chromeos::UserManager::Get()->RemoveSessionStateObserver(this);
246 // chromeos::UserManager::UserSessionStateObserver overrides:
247 virtual void UserAddedToSession(const chromeos::User
* added_user
) OVERRIDE
;
249 // content::NotificationObserver overrides:
250 virtual void Observe(int type
,
251 const content::NotificationSource
& source
,
252 const content::NotificationDetails
& details
) OVERRIDE
;
255 // Add a user to the session.
256 void AddUser(Profile
* profile
);
258 // The owning ChromeLauncherController.
259 ChromeLauncherController
* controller_
;
261 // The notification registrar to track the Profile creations after a user got
262 // added to the session (if required).
263 content::NotificationRegistrar registrar_
;
265 // Users which were just added to the system, but which profiles were not yet
267 std::set
<std::string
> added_user_ids_waiting_for_profiles_
;
269 DISALLOW_COPY_AND_ASSIGN(ChromeLauncherControllerUserSwitchObserverChromeOS
);
272 void ChromeLauncherControllerUserSwitchObserverChromeOS::UserAddedToSession(
273 const chromeos::User
* active_user
) {
274 Profile
* profile
= multi_user_util::GetProfileFromUserID(
275 active_user
->email());
276 // If we do not have a profile yet, we postpone forwarding the notification
277 // until it is loaded.
279 added_user_ids_waiting_for_profiles_
.insert(active_user
->email());
284 void ChromeLauncherControllerUserSwitchObserverChromeOS::Observe(
286 const content::NotificationSource
& source
,
287 const content::NotificationDetails
& details
) {
288 if (type
== chrome::NOTIFICATION_PROFILE_ADDED
&&
289 !added_user_ids_waiting_for_profiles_
.empty()) {
290 // Check if the profile is from a user which was on the waiting list.
291 Profile
* profile
= content::Source
<Profile
>(source
).ptr();
292 std::string user_id
= multi_user_util::GetUserIDFromProfile(profile
);
293 std::set
<std::string
>::iterator it
= std::find(
294 added_user_ids_waiting_for_profiles_
.begin(),
295 added_user_ids_waiting_for_profiles_
.end(),
297 if (it
!= added_user_ids_waiting_for_profiles_
.end()) {
298 added_user_ids_waiting_for_profiles_
.erase(it
);
299 AddUser(profile
->GetOriginalProfile());
304 void ChromeLauncherControllerUserSwitchObserverChromeOS::AddUser(
306 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
307 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED
)
308 chrome::MultiUserWindowManager::GetInstance()->AddUser(profile
);
309 controller_
->AdditionalUserAddedToSession(profile
->GetOriginalProfile());
313 ChromeLauncherController::ChromeLauncherController(Profile
* profile
,
314 ash::ShelfModel
* model
)
316 item_delegate_manager_(NULL
),
318 app_sync_ui_state_(NULL
),
319 ignore_persist_pinned_state_change_(false) {
321 // If no profile was passed, we take the currently active profile and use it
322 // as the owner of the current desktop.
323 // Use the original profile as on chromeos we may get a temporary off the
324 // record profile, unless in guest session (where off the record profile is
326 Profile
* active_profile
= ProfileManager::GetActiveUserProfile();
327 profile_
= active_profile
->IsGuestSession() ? active_profile
:
328 active_profile
->GetOriginalProfile();
330 app_sync_ui_state_
= AppSyncUIState::Get(profile_
);
331 if (app_sync_ui_state_
)
332 app_sync_ui_state_
->AddObserver(this);
335 // All profile relevant settings get bound to the current profile.
336 AttachProfile(profile_
);
337 model_
->AddObserver(this);
339 // In multi profile mode we might have a window manager. We try to create it
340 // here. If the instantiation fails, the manager is not needed.
341 chrome::MultiUserWindowManager::CreateInstance();
343 #if defined(OS_CHROMEOS)
344 // On Chrome OS using multi profile we want to switch the content of the shelf
345 // with a user change. Note that for unit tests the instance can be NULL.
346 if (chrome::MultiUserWindowManager::GetMultiProfileMode() !=
347 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_OFF
) {
348 user_switch_observer_
.reset(
349 new ChromeLauncherControllerUserSwitchObserverChromeOS(this));
352 // Create our v1/v2 application / browser monitors which will inform the
353 // launcher of status changes.
354 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
355 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED
) {
356 // If running in separated destkop mode, we create the multi profile version
357 // of status monitor.
358 browser_status_monitor_
.reset(new MultiProfileBrowserStatusMonitor(this));
359 app_window_controller_
.reset(
360 new MultiProfileAppWindowLauncherController(this));
362 // Create our v1/v2 application / browser monitors which will inform the
363 // launcher of status changes.
364 browser_status_monitor_
.reset(new BrowserStatusMonitor(this));
365 app_window_controller_
.reset(new AppWindowLauncherController(this));
368 // Create our v1/v2 application / browser monitors which will inform the
369 // launcher of status changes.
370 browser_status_monitor_
.reset(new BrowserStatusMonitor(this));
371 app_window_controller_
.reset(new AppWindowLauncherController(this));
374 // Right now ash::Shell isn't created for tests.
375 // TODO(mukai): Allows it to observe display change and write tests.
376 if (ash::Shell::HasInstance()) {
377 ash::Shell::GetInstance()->display_controller()->AddObserver(this);
378 item_delegate_manager_
=
379 ash::Shell::GetInstance()->shelf_item_delegate_manager();
382 notification_registrar_
.Add(this,
383 chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED
,
384 content::Source
<Profile
>(profile_
));
385 notification_registrar_
.Add(
387 chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED
,
388 content::Source
<Profile
>(profile_
));
391 ChromeLauncherController::~ChromeLauncherController() {
392 // Reset the BrowserStatusMonitor as it has a weak pointer to this.
393 browser_status_monitor_
.reset();
395 // Reset the app window controller here since it has a weak pointer to this.
396 app_window_controller_
.reset();
398 for (std::set
<ash::Shelf
*>::iterator iter
= shelves_
.begin();
399 iter
!= shelves_
.end();
401 (*iter
)->shelf_widget()->shelf_layout_manager()->RemoveObserver(this);
403 model_
->RemoveObserver(this);
404 if (ash::Shell::HasInstance())
405 ash::Shell::GetInstance()->display_controller()->RemoveObserver(this);
406 for (IDToItemControllerMap::iterator i
= id_to_item_controller_map_
.begin();
407 i
!= id_to_item_controller_map_
.end(); ++i
) {
408 int index
= model_
->ItemIndexByID(i
->first
);
409 // A "browser proxy" is not known to the model and this removal does
410 // therefore not need to be propagated to the model.
412 model_
->items()[index
].type
!= ash::TYPE_BROWSER_SHORTCUT
)
413 model_
->RemoveItemAt(index
);
416 if (ash::Shell::HasInstance())
417 ash::Shell::GetInstance()->RemoveShellObserver(this);
419 // Release all profile dependent resources.
421 if (instance_
== this)
424 // Get rid of the multi user window manager instance.
425 chrome::MultiUserWindowManager::DeleteInstance();
429 ChromeLauncherController
* ChromeLauncherController::CreateInstance(
431 ash::ShelfModel
* model
) {
432 // We do not check here for re-creation of the ChromeLauncherController since
433 // it appears that it might be intentional that the ChromeLauncherController
434 // can be re-created.
435 instance_
= new ChromeLauncherController(profile
, model
);
439 void ChromeLauncherController::Init() {
440 CreateBrowserShortcutLauncherItem();
441 UpdateAppLaunchersFromPref();
443 // TODO(sky): update unit test so that this test isn't necessary.
444 if (ash::Shell::HasInstance()) {
445 SetShelfAutoHideBehaviorFromPrefs();
446 SetShelfAlignmentFromPrefs();
447 PrefServiceSyncable
* prefs
= PrefServiceSyncable::FromProfile(profile_
);
448 if (!prefs
->FindPreference(prefs::kShelfAlignmentLocal
)->HasUserSetting() ||
449 !prefs
->FindPreference(prefs::kShelfAutoHideBehaviorLocal
)->
451 // This causes OnIsSyncingChanged to be called when the value of
452 // PrefService::IsSyncing() changes.
453 prefs
->AddObserver(this);
455 ash::Shell::GetInstance()->AddShellObserver(this);
459 ash::ShelfID
ChromeLauncherController::CreateAppLauncherItem(
460 LauncherItemController
* controller
,
461 const std::string
& app_id
,
462 ash::ShelfItemStatus status
) {
465 // Panels are inserted on the left so as not to push all existing panels over.
466 if (controller
->GetShelfItemType() != ash::TYPE_APP_PANEL
)
467 index
= model_
->item_count();
468 return InsertAppLauncherItem(controller
,
472 controller
->GetShelfItemType());
475 void ChromeLauncherController::SetItemStatus(ash::ShelfID id
,
476 ash::ShelfItemStatus status
) {
477 int index
= model_
->ItemIndexByID(id
);
478 ash::ShelfItemStatus old_status
= model_
->items()[index
].status
;
479 // Since ordinary browser windows are not registered, we might get a negative
481 if (index
>= 0 && old_status
!= status
) {
482 ash::ShelfItem item
= model_
->items()[index
];
483 item
.status
= status
;
484 model_
->Set(index
, item
);
488 void ChromeLauncherController::SetItemController(
490 LauncherItemController
* controller
) {
492 IDToItemControllerMap::iterator iter
= id_to_item_controller_map_
.find(id
);
493 CHECK(iter
!= id_to_item_controller_map_
.end());
494 controller
->set_shelf_id(id
);
495 iter
->second
= controller
;
496 // Existing controller is destroyed and replaced by registering again.
497 SetShelfItemDelegate(id
, controller
);
500 void ChromeLauncherController::CloseLauncherItem(ash::ShelfID id
) {
503 // Create a new shortcut controller.
504 IDToItemControllerMap::iterator iter
= id_to_item_controller_map_
.find(id
);
505 CHECK(iter
!= id_to_item_controller_map_
.end());
506 SetItemStatus(id
, ash::STATUS_CLOSED
);
507 std::string app_id
= iter
->second
->app_id();
508 iter
->second
= new AppShortcutLauncherItemController(app_id
, this);
509 iter
->second
->set_shelf_id(id
);
510 // Existing controller is destroyed and replaced by registering again.
511 SetShelfItemDelegate(id
, iter
->second
);
513 LauncherItemClosed(id
);
517 void ChromeLauncherController::Pin(ash::ShelfID id
) {
518 DCHECK(HasItemController(id
));
520 int index
= model_
->ItemIndexByID(id
);
523 ash::ShelfItem item
= model_
->items()[index
];
525 if (item
.type
== ash::TYPE_PLATFORM_APP
||
526 item
.type
== ash::TYPE_WINDOWED_APP
) {
527 item
.type
= ash::TYPE_APP_SHORTCUT
;
528 model_
->Set(index
, item
);
529 } else if (item
.type
!= ash::TYPE_APP_SHORTCUT
) {
534 PersistPinnedState();
537 void ChromeLauncherController::Unpin(ash::ShelfID id
) {
538 DCHECK(HasItemController(id
));
540 LauncherItemController
* controller
= id_to_item_controller_map_
[id
];
541 if (controller
->type() == LauncherItemController::TYPE_APP
||
542 controller
->locked()) {
543 UnpinRunningAppInternal(model_
->ItemIndexByID(id
));
545 LauncherItemClosed(id
);
548 PersistPinnedState();
551 bool ChromeLauncherController::IsPinned(ash::ShelfID id
) {
552 int index
= model_
->ItemIndexByID(id
);
555 ash::ShelfItemType type
= model_
->items()[index
].type
;
556 return (type
== ash::TYPE_APP_SHORTCUT
|| type
== ash::TYPE_BROWSER_SHORTCUT
);
559 void ChromeLauncherController::TogglePinned(ash::ShelfID id
) {
560 if (!HasItemController(id
))
561 return; // May happen if item closed with menu open.
569 bool ChromeLauncherController::IsPinnable(ash::ShelfID id
) const {
570 int index
= model_
->ItemIndexByID(id
);
574 ash::ShelfItemType type
= model_
->items()[index
].type
;
575 return ((type
== ash::TYPE_APP_SHORTCUT
||
576 type
== ash::TYPE_PLATFORM_APP
||
577 type
== ash::TYPE_WINDOWED_APP
) &&
581 void ChromeLauncherController::LockV1AppWithID(
582 const std::string
& app_id
) {
583 ash::ShelfID id
= GetShelfIDForAppID(app_id
);
584 if (!IsPinned(id
) && !IsWindowedAppInLauncher(app_id
)) {
585 CreateAppShortcutLauncherItemWithType(app_id
,
586 model_
->item_count(),
587 ash::TYPE_WINDOWED_APP
);
588 id
= GetShelfIDForAppID(app_id
);
591 id_to_item_controller_map_
[id
]->lock();
594 void ChromeLauncherController::UnlockV1AppWithID(const std::string
& app_id
) {
595 ash::ShelfID id
= GetShelfIDForAppID(app_id
);
596 CHECK(IsPinned(id
) || IsWindowedAppInLauncher(app_id
));
598 LauncherItemController
* controller
= id_to_item_controller_map_
[id
];
599 controller
->unlock();
600 if (!controller
->locked() && !IsPinned(id
))
601 CloseLauncherItem(id
);
604 void ChromeLauncherController::Launch(ash::ShelfID id
, int event_flags
) {
605 if (!HasItemController(id
))
606 return; // In case invoked from menu and item closed while menu up.
607 id_to_item_controller_map_
[id
]->Launch(ash::LAUNCH_FROM_UNKNOWN
, event_flags
);
610 void ChromeLauncherController::Close(ash::ShelfID id
) {
611 if (!HasItemController(id
))
612 return; // May happen if menu closed.
613 id_to_item_controller_map_
[id
]->Close();
616 bool ChromeLauncherController::IsOpen(ash::ShelfID id
) {
617 if (!HasItemController(id
))
619 return id_to_item_controller_map_
[id
]->IsOpen();
622 bool ChromeLauncherController::IsPlatformApp(ash::ShelfID id
) {
623 if (!HasItemController(id
))
626 std::string app_id
= GetAppIDForShelfID(id
);
627 const Extension
* extension
= GetExtensionForAppID(app_id
);
628 // An extension can be synced / updated at any time and therefore not be
630 return extension
? extension
->is_platform_app() : false;
633 void ChromeLauncherController::LaunchApp(const std::string
& app_id
,
634 ash::LaunchSource source
,
636 // |extension| could be NULL when it is being unloaded for updating.
637 const Extension
* extension
= GetExtensionForAppID(app_id
);
641 if (!extensions::util::IsAppLaunchableWithoutEnabling(app_id
, profile_
)) {
642 // Do nothing if there is already a running enable flow.
643 if (extension_enable_flow_
)
646 extension_enable_flow_
.reset(
647 new ExtensionEnableFlow(profile_
, app_id
, this));
648 extension_enable_flow_
->StartForNativeWindow(NULL
);
652 if (LaunchedInNativeDesktop(app_id
))
655 // The app will be created for the currently active profile.
656 AppLaunchParams
params(profile_
,
659 chrome::HOST_DESKTOP_TYPE_ASH
);
660 if (source
!= ash::LAUNCH_FROM_UNKNOWN
&&
661 app_id
== extension_misc::kWebStoreAppId
) {
662 // Get the corresponding source string.
663 std::string source_value
= GetSourceFromAppListSource(source
);
665 // Set an override URL to include the source.
666 GURL extension_url
= extensions::AppLaunchInfo::GetFullLaunchURL(extension
);
667 params
.override_url
= net::AppendQueryParameter(
668 extension_url
, extension_urls::kWebstoreSourceField
, source_value
);
671 OpenApplication(params
);
674 void ChromeLauncherController::ActivateApp(const std::string
& app_id
,
675 ash::LaunchSource source
,
677 // If there is an existing non-shortcut controller for this app, open it.
678 ash::ShelfID id
= GetShelfIDForAppID(app_id
);
680 LauncherItemController
* controller
= id_to_item_controller_map_
[id
];
681 controller
->Activate(source
);
685 // Create a temporary application launcher item and use it to see if there are
686 // running instances.
687 scoped_ptr
<AppShortcutLauncherItemController
> app_controller(
688 new AppShortcutLauncherItemController(app_id
, this));
689 if (!app_controller
->GetRunningApplications().empty())
690 app_controller
->Activate(source
);
692 LaunchApp(app_id
, source
, event_flags
);
695 extensions::LaunchType
ChromeLauncherController::GetLaunchType(
697 DCHECK(HasItemController(id
));
699 const Extension
* extension
= GetExtensionForAppID(
700 id_to_item_controller_map_
[id
]->app_id());
702 // An extension can be unloaded/updated/unavailable at any time.
704 return extensions::LAUNCH_TYPE_DEFAULT
;
706 return extensions::GetLaunchType(extensions::ExtensionPrefs::Get(profile_
),
710 ash::ShelfID
ChromeLauncherController::GetShelfIDForAppID(
711 const std::string
& app_id
) {
712 for (IDToItemControllerMap::const_iterator i
=
713 id_to_item_controller_map_
.begin();
714 i
!= id_to_item_controller_map_
.end(); ++i
) {
715 if (i
->second
->type() == LauncherItemController::TYPE_APP_PANEL
)
716 continue; // Don't include panels
717 if (i
->second
->app_id() == app_id
)
723 const std::string
& ChromeLauncherController::GetAppIDForShelfID(
725 CHECK(HasItemController(id
));
726 return id_to_item_controller_map_
[id
]->app_id();
729 void ChromeLauncherController::SetAppImage(const std::string
& id
,
730 const gfx::ImageSkia
& image
) {
731 // TODO: need to get this working for shortcuts.
732 for (IDToItemControllerMap::const_iterator i
=
733 id_to_item_controller_map_
.begin();
734 i
!= id_to_item_controller_map_
.end(); ++i
) {
735 LauncherItemController
* controller
= i
->second
;
736 if (controller
->app_id() != id
)
738 if (controller
->image_set_by_controller())
740 int index
= model_
->ItemIndexByID(i
->first
);
743 ash::ShelfItem item
= model_
->items()[index
];
745 model_
->Set(index
, item
);
746 // It's possible we're waiting on more than one item, so don't break.
750 void ChromeLauncherController::OnAutoHideBehaviorChanged(
751 aura::Window
* root_window
,
752 ash::ShelfAutoHideBehavior new_behavior
) {
753 SetShelfAutoHideBehaviorPrefs(new_behavior
, root_window
);
756 void ChromeLauncherController::SetLauncherItemImage(
757 ash::ShelfID shelf_id
,
758 const gfx::ImageSkia
& image
) {
759 int index
= model_
->ItemIndexByID(shelf_id
);
762 ash::ShelfItem item
= model_
->items()[index
];
764 model_
->Set(index
, item
);
767 bool ChromeLauncherController::CanPin() const {
768 const PrefService::Preference
* pref
=
769 profile_
->GetPrefs()->FindPreference(prefs::kPinnedLauncherApps
);
770 return pref
&& pref
->IsUserModifiable();
773 bool ChromeLauncherController::IsAppPinned(const std::string
& app_id
) {
774 for (IDToItemControllerMap::const_iterator i
=
775 id_to_item_controller_map_
.begin();
776 i
!= id_to_item_controller_map_
.end(); ++i
) {
777 if (IsPinned(i
->first
) && i
->second
->app_id() == app_id
)
783 bool ChromeLauncherController::IsWindowedAppInLauncher(
784 const std::string
& app_id
) {
785 int index
= model_
->ItemIndexByID(GetShelfIDForAppID(app_id
));
789 ash::ShelfItemType type
= model_
->items()[index
].type
;
790 return type
== ash::TYPE_WINDOWED_APP
;
793 void ChromeLauncherController::PinAppWithID(const std::string
& app_id
) {
795 DoPinAppWithID(app_id
);
800 void ChromeLauncherController::SetLaunchType(
802 extensions::LaunchType launch_type
) {
803 if (!HasItemController(id
))
806 extensions::SetLaunchType(profile_
->GetExtensionService(),
807 id_to_item_controller_map_
[id
]->app_id(),
811 void ChromeLauncherController::UnpinAppWithID(const std::string
& app_id
) {
813 DoUnpinAppWithID(app_id
);
818 bool ChromeLauncherController::IsLoggedInAsGuest() {
819 return profile_
->IsGuestSession();
822 void ChromeLauncherController::CreateNewWindow() {
823 // Use the currently active user.
824 chrome::NewEmptyWindow(profile_
, chrome::HOST_DESKTOP_TYPE_ASH
);
827 void ChromeLauncherController::CreateNewIncognitoWindow() {
828 // Use the currently active user.
829 chrome::NewEmptyWindow(profile_
->GetOffTheRecordProfile(),
830 chrome::HOST_DESKTOP_TYPE_ASH
);
833 void ChromeLauncherController::PersistPinnedState() {
834 if (ignore_persist_pinned_state_change_
)
836 // It is a coding error to call PersistPinnedState() if the pinned apps are
837 // not user-editable. The code should check earlier and not perform any
838 // modification actions that trigger persisting the state.
840 NOTREACHED() << "Can't pin but pinned state being updated";
843 // Mutating kPinnedLauncherApps is going to notify us and trigger us to
844 // process the change. We don't want that to happen so remove ourselves as a
846 pref_change_registrar_
.Remove(prefs::kPinnedLauncherApps
);
848 ListPrefUpdate
updater(profile_
->GetPrefs(), prefs::kPinnedLauncherApps
);
850 for (size_t i
= 0; i
< model_
->items().size(); ++i
) {
851 if (model_
->items()[i
].type
== ash::TYPE_APP_SHORTCUT
) {
852 ash::ShelfID id
= model_
->items()[i
].id
;
853 if (HasItemController(id
) && IsPinned(id
)) {
854 base::DictionaryValue
* app_value
= ash::CreateAppDict(
855 id_to_item_controller_map_
[id
]->app_id());
857 updater
->Append(app_value
);
859 } else if (model_
->items()[i
].type
== ash::TYPE_BROWSER_SHORTCUT
) {
860 PersistChromeItemIndex(i
);
861 } else if (model_
->items()[i
].type
== ash::TYPE_APP_LIST
) {
862 base::DictionaryValue
* app_value
= ash::CreateAppDict(
863 kAppShelfIdPlaceholder
);
865 updater
->Append(app_value
);
869 pref_change_registrar_
.Add(
870 prefs::kPinnedLauncherApps
,
871 base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref
,
872 base::Unretained(this)));
875 ash::ShelfModel
* ChromeLauncherController::model() {
879 Profile
* ChromeLauncherController::profile() {
883 ash::ShelfAutoHideBehavior
ChromeLauncherController::GetShelfAutoHideBehavior(
884 aura::Window
* root_window
) const {
885 // Don't show the shelf in app mode.
886 if (chrome::IsRunningInAppMode())
887 return ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN
;
889 // See comment in |kShelfAlignment| as to why we consider two prefs.
890 const std::string
behavior_value(
891 GetPrefForRootWindow(profile_
->GetPrefs(),
893 prefs::kShelfAutoHideBehaviorLocal
,
894 prefs::kShelfAutoHideBehavior
));
896 // Note: To maintain sync compatibility with old images of chrome/chromeos
897 // the set of values that may be encountered includes the now-extinct
898 // "Default" as well as "Never" and "Always", "Default" should now
899 // be treated as "Never" (http://crbug.com/146773).
900 if (behavior_value
== ash::kShelfAutoHideBehaviorAlways
)
901 return ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS
;
902 return ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER
;
905 bool ChromeLauncherController::CanUserModifyShelfAutoHideBehavior(
906 aura::Window
* root_window
) const {
907 return !ash::Shell::GetInstance()->IsMaximizeModeWindowManagerEnabled() &&
908 profile_
->GetPrefs()->FindPreference(
909 prefs::kShelfAutoHideBehaviorLocal
)->IsUserModifiable();
912 void ChromeLauncherController::ToggleShelfAutoHideBehavior(
913 aura::Window
* root_window
) {
914 ash::ShelfAutoHideBehavior behavior
= GetShelfAutoHideBehavior(root_window
) ==
915 ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS
?
916 ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER
:
917 ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS
;
918 SetShelfAutoHideBehaviorPrefs(behavior
, root_window
);
922 void ChromeLauncherController::UpdateAppState(content::WebContents
* contents
,
923 AppState app_state
) {
924 std::string app_id
= app_tab_helper_
->GetAppID(contents
);
926 // Check if the gMail app is loaded and it matches the given content.
927 // This special treatment is needed to address crbug.com/234268.
928 if (app_id
.empty() && ContentCanBeHandledByGmailApp(contents
))
929 app_id
= kGmailAppId
;
931 // Check the old |app_id| for a tab. If the contents has changed we need to
932 // remove it from the previous app.
933 if (web_contents_to_app_id_
.find(contents
) != web_contents_to_app_id_
.end()) {
934 std::string last_app_id
= web_contents_to_app_id_
[contents
];
935 if (last_app_id
!= app_id
) {
936 ash::ShelfID id
= GetShelfIDForAppID(last_app_id
);
938 // Since GetAppState() will use |web_contents_to_app_id_| we remove
939 // the connection before calling it.
940 web_contents_to_app_id_
.erase(contents
);
941 SetItemStatus(id
, GetAppState(last_app_id
));
946 if (app_state
== APP_STATE_REMOVED
)
947 web_contents_to_app_id_
.erase(contents
);
949 web_contents_to_app_id_
[contents
] = app_id
;
951 ash::ShelfID id
= GetShelfIDForAppID(app_id
);
953 SetItemStatus(id
, (app_state
== APP_STATE_WINDOW_ACTIVE
||
954 app_state
== APP_STATE_ACTIVE
) ? ash::STATUS_ACTIVE
:
955 GetAppState(app_id
));
959 ash::ShelfID
ChromeLauncherController::GetShelfIDForWebContents(
960 content::WebContents
* contents
) {
963 std::string app_id
= app_tab_helper_
->GetAppID(contents
);
965 if (app_id
.empty() && ContentCanBeHandledByGmailApp(contents
))
966 app_id
= kGmailAppId
;
968 ash::ShelfID id
= GetShelfIDForAppID(app_id
);
970 if (app_id
.empty() || !id
) {
971 int browser_index
= model_
->GetItemIndexForType(ash::TYPE_BROWSER_SHORTCUT
);
972 return model_
->items()[browser_index
].id
;
978 void ChromeLauncherController::SetRefocusURLPatternForTest(ash::ShelfID id
,
980 DCHECK(HasItemController(id
));
981 LauncherItemController
* controller
= id_to_item_controller_map_
[id
];
983 int index
= model_
->ItemIndexByID(id
);
985 NOTREACHED() << "Invalid launcher id";
989 ash::ShelfItemType type
= model_
->items()[index
].type
;
990 if (type
== ash::TYPE_APP_SHORTCUT
|| type
== ash::TYPE_WINDOWED_APP
) {
991 AppShortcutLauncherItemController
* app_controller
=
992 static_cast<AppShortcutLauncherItemController
*>(controller
);
993 app_controller
->set_refocus_url(url
);
995 NOTREACHED() << "Invalid launcher type";
999 const Extension
* ChromeLauncherController::GetExtensionForAppID(
1000 const std::string
& app_id
) const {
1001 // Some unit tests do not have a real extension.
1002 return (profile_
->GetExtensionService()) ?
1003 profile_
->GetExtensionService()->GetInstalledExtension(app_id
) : NULL
;
1006 void ChromeLauncherController::ActivateWindowOrMinimizeIfActive(
1007 ui::BaseWindow
* window
,
1008 bool allow_minimize
) {
1009 // In separated desktop mode we might have to teleport a window back to the
1011 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
1012 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED
) {
1013 aura::Window
* native_window
= window
->GetNativeWindow();
1014 const std::string
& current_user
=
1015 multi_user_util::GetUserIDFromProfile(profile());
1016 chrome::MultiUserWindowManager
* manager
=
1017 chrome::MultiUserWindowManager::GetInstance();
1018 if (!manager
->IsWindowOnDesktopOfUser(native_window
, current_user
)) {
1019 ash::MultiProfileUMA::RecordTeleportAction(
1020 ash::MultiProfileUMA::TELEPORT_WINDOW_RETURN_BY_LAUNCHER
);
1021 manager
->ShowWindowForUser(native_window
, current_user
);
1027 if (window
->IsActive() && allow_minimize
) {
1028 if (CommandLine::ForCurrentProcess()->HasSwitch(
1029 switches::kDisableMinimizeOnSecondLauncherItemClick
)) {
1030 AnimateWindow(window
->GetNativeWindow(),
1031 wm::WINDOW_ANIMATION_TYPE_BOUNCE
);
1041 void ChromeLauncherController::OnShelfCreated(ash::Shelf
* shelf
) {
1042 shelves_
.insert(shelf
);
1043 shelf
->shelf_widget()->shelf_layout_manager()->AddObserver(this);
1046 void ChromeLauncherController::OnShelfDestroyed(ash::Shelf
* shelf
) {
1047 shelves_
.erase(shelf
);
1048 // RemoveObserver is not called here, since by the time this method is called
1049 // Shelf is already in its destructor.
1052 void ChromeLauncherController::ShelfItemAdded(int index
) {
1053 // The app list launcher can get added to the shelf after we applied the
1054 // preferences. In that case the item might be at the wrong spot. As such we
1055 // call the function again.
1056 if (model_
->items()[index
].type
== ash::TYPE_APP_LIST
)
1057 UpdateAppLaunchersFromPref();
1060 void ChromeLauncherController::ShelfItemRemoved(int index
, ash::ShelfID id
) {
1063 void ChromeLauncherController::ShelfItemMoved(int start_index
,
1065 const ash::ShelfItem
& item
= model_
->items()[target_index
];
1066 // We remember the moved item position if it is either pinnable or
1067 // it is the app list with the alternate shelf layout.
1068 if ((HasItemController(item
.id
) && IsPinned(item
.id
)) ||
1069 item
.type
== ash::TYPE_APP_LIST
)
1070 PersistPinnedState();
1073 void ChromeLauncherController::ShelfItemChanged(
1075 const ash::ShelfItem
& old_item
) {
1078 void ChromeLauncherController::ShelfStatusChanged() {
1081 void ChromeLauncherController::ActiveUserChanged(
1082 const std::string
& user_email
) {
1083 // Store the order of running applications for the user which gets inactive.
1084 RememberUnpinnedRunningApplicationOrder();
1085 // Coming here the default profile is already switched. All profile specific
1086 // resources get released and the new profile gets attached instead.
1088 // When coming here, the active user has already be changed so that we can
1089 // set it as active.
1090 AttachProfile(ProfileManager::GetActiveUserProfile());
1091 // Update the V1 applications.
1092 browser_status_monitor_
->ActiveUserChanged(user_email
);
1093 // Switch the running applications to the new user.
1094 app_window_controller_
->ActiveUserChanged(user_email
);
1095 // Update the user specific shell properties from the new user profile.
1096 UpdateAppLaunchersFromPref();
1097 SetShelfAlignmentFromPrefs();
1098 SetShelfAutoHideBehaviorFromPrefs();
1099 SetShelfBehaviorsFromPrefs();
1100 // Restore the order of running, but unpinned applications for the activated
1102 RestoreUnpinnedRunningApplicationOrder(user_email
);
1103 // Inform the system tray of the change.
1104 ash::Shell::GetInstance()->system_tray_delegate()->ActiveUserWasChanged();
1107 void ChromeLauncherController::AdditionalUserAddedToSession(Profile
* profile
) {
1108 // Switch the running applications to the new user.
1109 app_window_controller_
->AdditionalUserAddedToSession(profile
);
1112 void ChromeLauncherController::Observe(
1114 const content::NotificationSource
& source
,
1115 const content::NotificationDetails
& details
) {
1117 case chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED
: {
1118 const Extension
* extension
=
1119 content::Details
<const Extension
>(details
).ptr();
1120 if (IsAppPinned(extension
->id())) {
1121 // Clear and re-fetch to ensure icon is up-to-date.
1122 app_icon_loader_
->ClearImage(extension
->id());
1123 app_icon_loader_
->FetchImage(extension
->id());
1126 UpdateAppLaunchersFromPref();
1129 case chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED
: {
1130 const content::Details
<UnloadedExtensionInfo
>& unload_info(details
);
1131 const Extension
* extension
= unload_info
->extension
;
1132 const std::string
& id
= extension
->id();
1133 // Since we might have windowed apps of this type which might have
1134 // outstanding locks which needs to be removed.
1135 if (GetShelfIDForAppID(id
) &&
1136 unload_info
->reason
== UnloadedExtensionInfo::REASON_UNINSTALL
) {
1137 CloseWindowedAppsFromRemovedExtension(id
);
1140 if (IsAppPinned(id
)) {
1141 if (unload_info
->reason
== UnloadedExtensionInfo::REASON_UNINSTALL
) {
1142 DoUnpinAppWithID(id
);
1143 app_icon_loader_
->ClearImage(id
);
1145 app_icon_loader_
->UpdateImage(id
);
1151 NOTREACHED() << "Unexpected notification type=" << type
;
1155 void ChromeLauncherController::OnShelfAlignmentChanged(
1156 aura::Window
* root_window
) {
1157 const char* pref_value
= NULL
;
1158 switch (ash::Shell::GetInstance()->GetShelfAlignment(root_window
)) {
1159 case ash::SHELF_ALIGNMENT_BOTTOM
:
1160 pref_value
= ash::kShelfAlignmentBottom
;
1162 case ash::SHELF_ALIGNMENT_LEFT
:
1163 pref_value
= ash::kShelfAlignmentLeft
;
1165 case ash::SHELF_ALIGNMENT_RIGHT
:
1166 pref_value
= ash::kShelfAlignmentRight
;
1168 case ash::SHELF_ALIGNMENT_TOP
:
1169 pref_value
= ash::kShelfAlignmentTop
;
1172 UpdatePerDisplayPref(
1173 profile_
->GetPrefs(), root_window
, prefs::kShelfAlignment
, pref_value
);
1175 if (root_window
== ash::Shell::GetPrimaryRootWindow()) {
1176 // See comment in |kShelfAlignment| about why we have two prefs here.
1177 profile_
->GetPrefs()->SetString(prefs::kShelfAlignmentLocal
, pref_value
);
1178 profile_
->GetPrefs()->SetString(prefs::kShelfAlignment
, pref_value
);
1182 void ChromeLauncherController::OnDisplayConfigurationChanged() {
1183 SetShelfBehaviorsFromPrefs();
1186 void ChromeLauncherController::OnIsSyncingChanged() {
1187 PrefServiceSyncable
* prefs
= PrefServiceSyncable::FromProfile(profile_
);
1188 MaybePropagatePrefToLocal(prefs
,
1189 prefs::kShelfAlignmentLocal
,
1190 prefs::kShelfAlignment
);
1191 MaybePropagatePrefToLocal(prefs
,
1192 prefs::kShelfAutoHideBehaviorLocal
,
1193 prefs::kShelfAutoHideBehavior
);
1196 void ChromeLauncherController::OnAppSyncUIStatusChanged() {
1197 if (app_sync_ui_state_
->status() == AppSyncUIState::STATUS_SYNCING
)
1198 model_
->SetStatus(ash::ShelfModel::STATUS_LOADING
);
1200 model_
->SetStatus(ash::ShelfModel::STATUS_NORMAL
);
1203 void ChromeLauncherController::ExtensionEnableFlowFinished() {
1204 LaunchApp(extension_enable_flow_
->extension_id(),
1205 ash::LAUNCH_FROM_UNKNOWN
,
1207 extension_enable_flow_
.reset();
1210 void ChromeLauncherController::ExtensionEnableFlowAborted(bool user_initiated
) {
1211 extension_enable_flow_
.reset();
1214 ChromeLauncherAppMenuItems
ChromeLauncherController::GetApplicationList(
1215 const ash::ShelfItem
& item
,
1217 // Make sure that there is a controller associated with the id and that the
1218 // extension itself is a valid application and not a panel.
1219 if (!HasItemController(item
.id
) ||
1220 !GetShelfIDForAppID(id_to_item_controller_map_
[item
.id
]->app_id()))
1221 return ChromeLauncherAppMenuItems().Pass();
1223 return id_to_item_controller_map_
[item
.id
]->GetApplicationList(event_flags
);
1226 std::vector
<content::WebContents
*>
1227 ChromeLauncherController::GetV1ApplicationsFromAppId(std::string app_id
) {
1228 ash::ShelfID id
= GetShelfIDForAppID(app_id
);
1230 // If there is no such an item pinned to the launcher, no menu gets created.
1232 LauncherItemController
* controller
= id_to_item_controller_map_
[id
];
1234 if (controller
->type() == LauncherItemController::TYPE_SHORTCUT
)
1235 return GetV1ApplicationsFromController(controller
);
1237 return std::vector
<content::WebContents
*>();
1240 void ChromeLauncherController::ActivateShellApp(const std::string
& app_id
,
1242 ash::ShelfID id
= GetShelfIDForAppID(app_id
);
1244 LauncherItemController
* controller
= id_to_item_controller_map_
[id
];
1245 if (controller
->type() == LauncherItemController::TYPE_APP
) {
1246 AppWindowLauncherItemController
* app_window_controller
=
1247 static_cast<AppWindowLauncherItemController
*>(controller
);
1248 app_window_controller
->ActivateIndexedApp(index
);
1253 bool ChromeLauncherController::IsWebContentHandledByApplication(
1254 content::WebContents
* web_contents
,
1255 const std::string
& app_id
) {
1256 if ((web_contents_to_app_id_
.find(web_contents
) !=
1257 web_contents_to_app_id_
.end()) &&
1258 (web_contents_to_app_id_
[web_contents
] == app_id
))
1260 return (app_id
== kGmailAppId
&& ContentCanBeHandledByGmailApp(web_contents
));
1263 bool ChromeLauncherController::ContentCanBeHandledByGmailApp(
1264 content::WebContents
* web_contents
) {
1265 ash::ShelfID id
= GetShelfIDForAppID(kGmailAppId
);
1267 const GURL url
= web_contents
->GetURL();
1268 // We need to extend the application matching for the gMail app beyond the
1269 // manifest file's specification. This is required because of the namespace
1270 // overlap with the offline app ("/mail/mu/").
1271 if (!MatchPattern(url
.path(), "/mail/mu/*") &&
1272 MatchPattern(url
.path(), "/mail/*") &&
1273 GetExtensionForAppID(kGmailAppId
) &&
1274 GetExtensionForAppID(kGmailAppId
)->OverlapsWithOrigin(url
))
1280 gfx::Image
ChromeLauncherController::GetAppListIcon(
1281 content::WebContents
* web_contents
) const {
1282 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
1283 if (IsIncognito(web_contents
))
1284 return rb
.GetImageNamed(IDR_ASH_SHELF_LIST_INCOGNITO_BROWSER
);
1285 FaviconTabHelper
* favicon_tab_helper
=
1286 FaviconTabHelper::FromWebContents(web_contents
);
1287 gfx::Image result
= favicon_tab_helper
->GetFavicon();
1288 if (result
.IsEmpty())
1289 return rb
.GetImageNamed(IDR_DEFAULT_FAVICON
);
1293 base::string16
ChromeLauncherController::GetAppListTitle(
1294 content::WebContents
* web_contents
) const {
1295 base::string16 title
= web_contents
->GetTitle();
1298 WebContentsToAppIDMap::const_iterator iter
=
1299 web_contents_to_app_id_
.find(web_contents
);
1300 if (iter
!= web_contents_to_app_id_
.end()) {
1301 std::string app_id
= iter
->second
;
1302 const extensions::Extension
* extension
= GetExtensionForAppID(app_id
);
1304 return base::UTF8ToUTF16(extension
->name());
1306 return l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE
);
1309 ash::ShelfID
ChromeLauncherController::CreateAppShortcutLauncherItem(
1310 const std::string
& app_id
,
1312 return CreateAppShortcutLauncherItemWithType(app_id
,
1314 ash::TYPE_APP_SHORTCUT
);
1317 void ChromeLauncherController::SetAppTabHelperForTest(AppTabHelper
* helper
) {
1318 app_tab_helper_
.reset(helper
);
1321 void ChromeLauncherController::SetAppIconLoaderForTest(
1322 extensions::AppIconLoader
* loader
) {
1323 app_icon_loader_
.reset(loader
);
1326 const std::string
& ChromeLauncherController::GetAppIdFromShelfIdForTest(
1328 return id_to_item_controller_map_
[id
]->app_id();
1331 void ChromeLauncherController::SetShelfItemDelegateManagerForTest(
1332 ash::ShelfItemDelegateManager
* manager
) {
1333 item_delegate_manager_
= manager
;
1336 void ChromeLauncherController::RememberUnpinnedRunningApplicationOrder() {
1337 RunningAppListIds list
;
1338 for (int i
= 0; i
< model_
->item_count(); i
++) {
1339 ash::ShelfItemType type
= model_
->items()[i
].type
;
1340 if (type
== ash::TYPE_WINDOWED_APP
|| type
== ash::TYPE_PLATFORM_APP
)
1341 list
.push_back(GetAppIDForShelfID(model_
->items()[i
].id
));
1343 last_used_running_application_order_
[
1344 multi_user_util::GetUserIDFromProfile(profile_
)] = list
;
1347 void ChromeLauncherController::RestoreUnpinnedRunningApplicationOrder(
1348 const std::string
& user_id
) {
1349 const RunningAppListIdMap::iterator app_id_list
=
1350 last_used_running_application_order_
.find(user_id
);
1351 if (app_id_list
== last_used_running_application_order_
.end())
1354 // Find the first insertion point for running applications.
1355 int running_index
= model_
->FirstRunningAppIndex();
1356 for (RunningAppListIds::iterator app_id
= app_id_list
->second
.begin();
1357 app_id
!= app_id_list
->second
.end(); ++app_id
) {
1358 ash::ShelfID shelf_id
= GetShelfIDForAppID(*app_id
);
1360 int app_index
= model_
->ItemIndexByID(shelf_id
);
1361 DCHECK_GE(app_index
, 0);
1362 ash::ShelfItemType type
= model_
->items()[app_index
].type
;
1363 if (type
== ash::TYPE_WINDOWED_APP
|| type
== ash::TYPE_PLATFORM_APP
) {
1364 if (running_index
!= app_index
)
1365 model_
->Move(running_index
, app_index
);
1372 ash::ShelfID
ChromeLauncherController::CreateAppShortcutLauncherItemWithType(
1373 const std::string
& app_id
,
1375 ash::ShelfItemType shelf_item_type
) {
1376 AppShortcutLauncherItemController
* controller
=
1377 new AppShortcutLauncherItemController(app_id
, this);
1378 ash::ShelfID shelf_id
= InsertAppLauncherItem(
1379 controller
, app_id
, ash::STATUS_CLOSED
, index
, shelf_item_type
);
1383 LauncherItemController
* ChromeLauncherController::GetLauncherItemController(
1384 const ash::ShelfID id
) {
1385 if (!HasItemController(id
))
1387 return id_to_item_controller_map_
[id
];
1390 bool ChromeLauncherController::IsBrowserFromActiveUser(Browser
* browser
) {
1391 // If running multi user mode with separate desktops, we have to check if the
1392 // browser is from the active user.
1393 if (chrome::MultiUserWindowManager::GetMultiProfileMode() !=
1394 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED
)
1396 return multi_user_util::IsProfileFromActiveUser(browser
->profile());
1399 void ChromeLauncherController::LauncherItemClosed(ash::ShelfID id
) {
1400 IDToItemControllerMap::iterator iter
= id_to_item_controller_map_
.find(id
);
1401 CHECK(iter
!= id_to_item_controller_map_
.end());
1402 CHECK(iter
->second
);
1403 app_icon_loader_
->ClearImage(iter
->second
->app_id());
1404 id_to_item_controller_map_
.erase(iter
);
1405 int index
= model_
->ItemIndexByID(id
);
1406 // A "browser proxy" is not known to the model and this removal does
1407 // therefore not need to be propagated to the model.
1409 model_
->RemoveItemAt(index
);
1412 void ChromeLauncherController::DoPinAppWithID(const std::string
& app_id
) {
1413 // If there is an item, do nothing and return.
1414 if (IsAppPinned(app_id
))
1417 ash::ShelfID shelf_id
= GetShelfIDForAppID(app_id
);
1419 // App item exists, pin it
1422 // Otherwise, create a shortcut item for it.
1423 CreateAppShortcutLauncherItem(app_id
, model_
->item_count());
1425 PersistPinnedState();
1429 void ChromeLauncherController::DoUnpinAppWithID(const std::string
& app_id
) {
1430 ash::ShelfID shelf_id
= GetShelfIDForAppID(app_id
);
1431 if (shelf_id
&& IsPinned(shelf_id
))
1435 int ChromeLauncherController::PinRunningAppInternal(int index
,
1436 ash::ShelfID shelf_id
) {
1437 int running_index
= model_
->ItemIndexByID(shelf_id
);
1438 ash::ShelfItem item
= model_
->items()[running_index
];
1439 DCHECK(item
.type
== ash::TYPE_WINDOWED_APP
||
1440 item
.type
== ash::TYPE_PLATFORM_APP
);
1441 item
.type
= ash::TYPE_APP_SHORTCUT
;
1442 model_
->Set(running_index
, item
);
1443 // The |ShelfModel|'s weight system might reposition the item to a
1444 // new index, so we get the index again.
1445 running_index
= model_
->ItemIndexByID(shelf_id
);
1446 if (running_index
< index
)
1448 if (running_index
!= index
)
1449 model_
->Move(running_index
, index
);
1453 void ChromeLauncherController::UnpinRunningAppInternal(int index
) {
1454 DCHECK_GE(index
, 0);
1455 ash::ShelfItem item
= model_
->items()[index
];
1456 DCHECK_EQ(item
.type
, ash::TYPE_APP_SHORTCUT
);
1457 item
.type
= ash::TYPE_WINDOWED_APP
;
1458 // A platform app and a windowed app are sharing TYPE_APP_SHORTCUT. As such
1459 // we have to check here what this was before it got a shortcut.
1460 if (HasItemController(item
.id
) &&
1461 id_to_item_controller_map_
[item
.id
]->type() ==
1462 LauncherItemController::TYPE_APP
)
1463 item
.type
= ash::TYPE_PLATFORM_APP
;
1464 model_
->Set(index
, item
);
1467 void ChromeLauncherController::UpdateAppLaunchersFromPref() {
1468 // There are various functions which will trigger a |PersistPinnedState| call
1469 // like a direct call to |DoPinAppWithID|, or an indirect call to the menu
1470 // model which will use weights to re-arrange the icons to new positions.
1471 // Since this function is meant to synchronize the "is state" with the
1472 // "sync state", it makes no sense to store any changes by this function back
1473 // into the pref state. Therefore we tell |persistPinnedState| to ignore any
1474 // invocations while we are running.
1475 base::AutoReset
<bool> auto_reset(&ignore_persist_pinned_state_change_
, true);
1476 std::vector
<std::string
> pinned_apps
= GetListOfPinnedAppsAndBrowser();
1479 int max_index
= model_
->item_count();
1481 // When one of the two special items cannot be moved (and we do not know where
1482 // yet), we remember the current location in one of these variables.
1483 int chrome_index
= -1;
1484 int app_list_index
= -1;
1486 // Walk the model and |pinned_apps| from the pref lockstep, adding and
1487 // removing items as necessary. NB: This code uses plain old indexing instead
1488 // of iterators because of model mutations as part of the loop.
1489 std::vector
<std::string
>::const_iterator
pref_app_id(pinned_apps
.begin());
1490 for (; index
< max_index
&& pref_app_id
!= pinned_apps
.end(); ++index
) {
1491 // Check if we have an item which we need to handle.
1492 if (*pref_app_id
== extension_misc::kChromeAppId
||
1493 *pref_app_id
== kAppShelfIdPlaceholder
||
1494 IsAppPinned(*pref_app_id
)) {
1495 for (; index
< max_index
; ++index
) {
1496 const ash::ShelfItem
& item(model_
->items()[index
]);
1497 bool is_app_list
= item
.type
== ash::TYPE_APP_LIST
;
1498 bool is_chrome
= item
.type
== ash::TYPE_BROWSER_SHORTCUT
;
1499 if (item
.type
!= ash::TYPE_APP_SHORTCUT
&& !is_app_list
&& !is_chrome
)
1501 IDToItemControllerMap::const_iterator entry
=
1502 id_to_item_controller_map_
.find(item
.id
);
1503 if ((kAppShelfIdPlaceholder
== *pref_app_id
&& is_app_list
) ||
1504 (extension_misc::kChromeAppId
== *pref_app_id
&& is_chrome
) ||
1505 (entry
!= id_to_item_controller_map_
.end() &&
1506 entry
->second
->app_id() == *pref_app_id
)) {
1507 // Check if an item needs to be moved here.
1508 MoveChromeOrApplistToFinalPosition(
1509 is_chrome
, is_app_list
, index
, &chrome_index
, &app_list_index
);
1513 if (is_chrome
|| is_app_list
) {
1514 // We cannot delete any of these shortcuts. As such we remember
1515 // their positions and move them later where they belong.
1517 chrome_index
= index
;
1519 app_list_index
= index
;
1520 // And skip the item - or exit the loop if end is reached (note that
1521 // in that case we will reduce the index again by one and this only
1522 // compensates for it).
1523 if (index
>= max_index
- 1)
1527 // Check if this is a platform or a windowed app.
1528 if (item
.type
== ash::TYPE_APP_SHORTCUT
&&
1529 (id_to_item_controller_map_
[item
.id
]->locked() ||
1530 id_to_item_controller_map_
[item
.id
]->type() ==
1531 LauncherItemController::TYPE_APP
)) {
1532 // Note: This will not change the amount of items (|max_index|).
1533 // Even changes to the actual |index| due to item weighting
1534 // changes should be fine.
1535 UnpinRunningAppInternal(index
);
1537 LauncherItemClosed(item
.id
);
1544 // If the item wasn't found, that means id_to_item_controller_map_
1546 DCHECK(index
<= max_index
);
1548 // Check if the item was already running but not yet pinned.
1549 ash::ShelfID shelf_id
= GetShelfIDForAppID(*pref_app_id
);
1551 // This app is running but not yet pinned. So pin and move it.
1552 index
= PinRunningAppInternal(index
, shelf_id
);
1554 // This app wasn't pinned before, insert a new entry.
1555 shelf_id
= CreateAppShortcutLauncherItem(*pref_app_id
, index
);
1557 index
= model_
->ItemIndexByID(shelf_id
);
1563 // Remove any trailing existing items.
1564 while (index
< model_
->item_count()) {
1565 const ash::ShelfItem
& item(model_
->items()[index
]);
1566 if (item
.type
== ash::TYPE_APP_SHORTCUT
) {
1567 if (id_to_item_controller_map_
[item
.id
]->locked() ||
1568 id_to_item_controller_map_
[item
.id
]->type() ==
1569 LauncherItemController::TYPE_APP
)
1570 UnpinRunningAppInternal(index
);
1572 LauncherItemClosed(item
.id
);
1574 if (item
.type
== ash::TYPE_BROWSER_SHORTCUT
)
1575 chrome_index
= index
;
1576 else if (item
.type
== ash::TYPE_APP_LIST
)
1577 app_list_index
= index
;
1582 // Append unprocessed items from the pref to the end of the model.
1583 for (; pref_app_id
!= pinned_apps
.end(); ++pref_app_id
) {
1584 // All items but the chrome and / or app list shortcut needs to be added.
1585 bool is_chrome
= *pref_app_id
== extension_misc::kChromeAppId
;
1586 bool is_app_list
= *pref_app_id
== kAppShelfIdPlaceholder
;
1587 // Coming here we know the next item which can be finalized, either the
1588 // chrome item or the app launcher. The final position is the end of the
1589 // list. The menu model will make sure that the item is grouped according
1590 // to its weight (which we do not know here).
1591 if (!is_chrome
&& !is_app_list
) {
1592 DoPinAppWithID(*pref_app_id
);
1593 int target_index
= FindInsertionPoint(false);
1594 ash::ShelfID id
= GetShelfIDForAppID(*pref_app_id
);
1595 int source_index
= model_
->ItemIndexByID(id
);
1596 if (source_index
!= target_index
)
1597 model_
->Move(source_index
, target_index
);
1599 // Needed for the old layout - the weight might force it to be lower in
1601 if (app_list_index
!= -1 && target_index
<= app_list_index
)
1604 int target_index
= FindInsertionPoint(is_app_list
);
1605 MoveChromeOrApplistToFinalPosition(
1606 is_chrome
, is_app_list
, target_index
, &chrome_index
, &app_list_index
);
1611 void ChromeLauncherController::SetShelfAutoHideBehaviorPrefs(
1612 ash::ShelfAutoHideBehavior behavior
,
1613 aura::Window
* root_window
) {
1614 const char* value
= NULL
;
1616 case ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS
:
1617 value
= ash::kShelfAutoHideBehaviorAlways
;
1619 case ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER
:
1620 value
= ash::kShelfAutoHideBehaviorNever
;
1622 case ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN
:
1623 // This one should not be a valid preference option for now. We only want
1624 // to completely hide it when we run in app mode - or while we temporarily
1625 // hide the shelf as part of an animation (e.g. the multi user change).
1629 UpdatePerDisplayPref(
1630 profile_
->GetPrefs(), root_window
, prefs::kShelfAutoHideBehavior
, value
);
1632 if (root_window
== ash::Shell::GetPrimaryRootWindow()) {
1633 // See comment in |kShelfAlignment| about why we have two prefs here.
1634 profile_
->GetPrefs()->SetString(prefs::kShelfAutoHideBehaviorLocal
, value
);
1635 profile_
->GetPrefs()->SetString(prefs::kShelfAutoHideBehavior
, value
);
1639 void ChromeLauncherController::SetShelfAutoHideBehaviorFromPrefs() {
1640 aura::Window::Windows root_windows
= ash::Shell::GetAllRootWindows();
1642 for (aura::Window::Windows::const_iterator iter
= root_windows
.begin();
1643 iter
!= root_windows
.end(); ++iter
) {
1644 ash::Shell::GetInstance()->SetShelfAutoHideBehavior(
1645 GetShelfAutoHideBehavior(*iter
), *iter
);
1649 void ChromeLauncherController::SetShelfAlignmentFromPrefs() {
1650 if (!ash::ShelfWidget::ShelfAlignmentAllowed())
1653 aura::Window::Windows root_windows
= ash::Shell::GetAllRootWindows();
1655 for (aura::Window::Windows::const_iterator iter
= root_windows
.begin();
1656 iter
!= root_windows
.end(); ++iter
) {
1657 // See comment in |kShelfAlignment| as to why we consider two prefs.
1658 const std::string
alignment_value(
1659 GetPrefForRootWindow(profile_
->GetPrefs(),
1661 prefs::kShelfAlignmentLocal
,
1662 prefs::kShelfAlignment
));
1663 ash::ShelfAlignment alignment
= ash::SHELF_ALIGNMENT_BOTTOM
;
1664 if (alignment_value
== ash::kShelfAlignmentLeft
)
1665 alignment
= ash::SHELF_ALIGNMENT_LEFT
;
1666 else if (alignment_value
== ash::kShelfAlignmentRight
)
1667 alignment
= ash::SHELF_ALIGNMENT_RIGHT
;
1668 else if (alignment_value
== ash::kShelfAlignmentTop
)
1669 alignment
= ash::SHELF_ALIGNMENT_TOP
;
1670 ash::Shell::GetInstance()->SetShelfAlignment(alignment
, *iter
);
1674 void ChromeLauncherController::SetShelfBehaviorsFromPrefs() {
1675 SetShelfAutoHideBehaviorFromPrefs();
1676 SetShelfAlignmentFromPrefs();
1679 ash::ShelfItemStatus
ChromeLauncherController::GetAppState(
1680 const::std::string
& app_id
) {
1681 ash::ShelfItemStatus status
= ash::STATUS_CLOSED
;
1682 for (WebContentsToAppIDMap::iterator it
= web_contents_to_app_id_
.begin();
1683 it
!= web_contents_to_app_id_
.end();
1685 if (it
->second
== app_id
) {
1686 Browser
* browser
= chrome::FindBrowserWithWebContents(it
->first
);
1687 // Usually there should never be an item in our |web_contents_to_app_id_|
1688 // list which got deleted already. However - in some situations e.g.
1689 // Browser::SwapTabContent there is temporarily no associated browser.
1692 if (browser
->window()->IsActive()) {
1693 return browser
->tab_strip_model()->GetActiveWebContents() == it
->first
?
1694 ash::STATUS_ACTIVE
: ash::STATUS_RUNNING
;
1696 status
= ash::STATUS_RUNNING
;
1703 ash::ShelfID
ChromeLauncherController::InsertAppLauncherItem(
1704 LauncherItemController
* controller
,
1705 const std::string
& app_id
,
1706 ash::ShelfItemStatus status
,
1708 ash::ShelfItemType shelf_item_type
) {
1709 ash::ShelfID id
= model_
->next_id();
1710 CHECK(!HasItemController(id
));
1712 id_to_item_controller_map_
[id
] = controller
;
1713 controller
->set_shelf_id(id
);
1715 ash::ShelfItem item
;
1716 item
.type
= shelf_item_type
;
1717 item
.image
= extensions::util::GetDefaultAppIcon();
1719 ash::ShelfItemStatus new_state
= GetAppState(app_id
);
1720 if (new_state
!= ash::STATUS_CLOSED
)
1723 item
.status
= status
;
1725 model_
->AddAt(index
, item
);
1727 app_icon_loader_
->FetchImage(app_id
);
1729 SetShelfItemDelegate(id
, controller
);
1734 bool ChromeLauncherController::HasItemController(ash::ShelfID id
) const {
1735 return id_to_item_controller_map_
.find(id
) !=
1736 id_to_item_controller_map_
.end();
1739 std::vector
<content::WebContents
*>
1740 ChromeLauncherController::GetV1ApplicationsFromController(
1741 LauncherItemController
* controller
) {
1742 DCHECK(controller
->type() == LauncherItemController::TYPE_SHORTCUT
);
1743 AppShortcutLauncherItemController
* app_controller
=
1744 static_cast<AppShortcutLauncherItemController
*>(controller
);
1745 return app_controller
->GetRunningApplications();
1748 BrowserShortcutLauncherItemController
*
1749 ChromeLauncherController::GetBrowserShortcutLauncherItemController() {
1750 for (IDToItemControllerMap::iterator i
= id_to_item_controller_map_
.begin();
1751 i
!= id_to_item_controller_map_
.end(); ++i
) {
1752 int index
= model_
->ItemIndexByID(i
->first
);
1753 const ash::ShelfItem
& item
= model_
->items()[index
];
1754 if (item
.type
== ash::TYPE_BROWSER_SHORTCUT
)
1755 return static_cast<BrowserShortcutLauncherItemController
*>(i
->second
);
1757 // Create a LauncherItemController for the Browser shortcut if it does not
1759 ash::ShelfID id
= CreateBrowserShortcutLauncherItem();
1760 DCHECK(id_to_item_controller_map_
[id
]);
1761 return static_cast<BrowserShortcutLauncherItemController
*>(
1762 id_to_item_controller_map_
[id
]);
1765 ash::ShelfID
ChromeLauncherController::CreateBrowserShortcutLauncherItem() {
1766 ash::ShelfItem browser_shortcut
;
1767 browser_shortcut
.type
= ash::TYPE_BROWSER_SHORTCUT
;
1768 ResourceBundle
& rb
= ResourceBundle::GetSharedInstance();
1769 browser_shortcut
.image
= *rb
.GetImageSkiaNamed(IDR_PRODUCT_LOGO_32
);
1770 ash::ShelfID id
= model_
->next_id();
1771 size_t index
= GetChromeIconIndexForCreation();
1772 model_
->AddAt(index
, browser_shortcut
);
1773 id_to_item_controller_map_
[id
] =
1774 new BrowserShortcutLauncherItemController(this);
1775 id_to_item_controller_map_
[id
]->set_shelf_id(id
);
1776 // ShelfItemDelegateManager owns BrowserShortcutLauncherItemController.
1777 SetShelfItemDelegate(id
, id_to_item_controller_map_
[id
]);
1781 void ChromeLauncherController::PersistChromeItemIndex(int index
) {
1782 profile_
->GetPrefs()->SetInteger(prefs::kShelfChromeIconIndex
, index
);
1785 int ChromeLauncherController::GetChromeIconIndexFromPref() const {
1786 size_t index
= profile_
->GetPrefs()->GetInteger(prefs::kShelfChromeIconIndex
);
1787 const base::ListValue
* pinned_apps_pref
=
1788 profile_
->GetPrefs()->GetList(prefs::kPinnedLauncherApps
);
1789 return std::max(static_cast<size_t>(0),
1790 std::min(pinned_apps_pref
->GetSize(), index
));
1793 void ChromeLauncherController::MoveChromeOrApplistToFinalPosition(
1798 int* app_list_index
) {
1799 if (is_chrome
&& *chrome_index
!= -1) {
1800 model_
->Move(*chrome_index
, target_index
);
1801 if (*app_list_index
!= -1 &&
1802 *chrome_index
< *app_list_index
&&
1803 target_index
> *app_list_index
)
1804 --(*app_list_index
);
1806 } else if (is_app_list
&& *app_list_index
!= -1) {
1807 model_
->Move(*app_list_index
, target_index
);
1808 if (*chrome_index
!= -1 &&
1809 *app_list_index
< *chrome_index
&&
1810 target_index
> *chrome_index
)
1812 *app_list_index
= -1;
1816 int ChromeLauncherController::FindInsertionPoint(bool is_app_list
) {
1817 // Keeping this change small to backport to M33&32 (see crbug.com/329597).
1818 // TODO(skuhne): With the removal of the legacy shelf layout we should remove
1819 // the ability to move the app list item since this was never used. We should
1820 // instead ask the ShelfModel::ValidateInsertionIndex or similir for an index.
1824 for (int i
= model_
->item_count() - 1; i
> 0; --i
) {
1825 ash::ShelfItemType type
= model_
->items()[i
].type
;
1826 if (type
== ash::TYPE_APP_SHORTCUT
||
1827 (is_app_list
&& type
== ash::TYPE_APP_LIST
) ||
1828 type
== ash::TYPE_BROWSER_SHORTCUT
) {
1835 int ChromeLauncherController::GetChromeIconIndexForCreation() {
1836 // We get the list of pinned apps as they currently would get pinned.
1837 // Within this list the chrome icon will be the correct location.
1838 std::vector
<std::string
> pinned_apps
= GetListOfPinnedAppsAndBrowser();
1840 std::vector
<std::string
>::iterator it
=
1841 std::find(pinned_apps
.begin(),
1843 std::string(extension_misc::kChromeAppId
));
1844 DCHECK(it
!= pinned_apps
.end());
1845 int index
= it
- pinned_apps
.begin();
1847 // We should do here a comparison between the is state and the "want to be"
1848 // state since some apps might be able to pin but are not yet. Instead - for
1849 // the time being we clamp against the amount of known items and wait for the
1850 // next |UpdateAppLaunchersFromPref()| call to correct it - it will come since
1851 // the pinning will be done then.
1852 return std::min(model_
->item_count(), index
);
1855 std::vector
<std::string
>
1856 ChromeLauncherController::GetListOfPinnedAppsAndBrowser() {
1857 // Adding the app list item to the list of items requires that the ID is not
1858 // a valid and known ID for the extension system. The ID was constructed that
1859 // way - but just to make sure...
1860 DCHECK(!app_tab_helper_
->IsValidIDForCurrentUser(kAppShelfIdPlaceholder
));
1862 std::vector
<std::string
> pinned_apps
;
1864 // Get the new incarnation of the list.
1865 const base::ListValue
* pinned_apps_pref
=
1866 profile_
->GetPrefs()->GetList(prefs::kPinnedLauncherApps
);
1868 // Keep track of the addition of the chrome and the app list icon.
1869 bool chrome_icon_added
= false;
1870 bool app_list_icon_added
= false;
1871 size_t chrome_icon_index
= GetChromeIconIndexFromPref();
1873 // See if the chrome string is already in the pinned list and remove it if
1875 base::Value
* chrome_app
= ash::CreateAppDict(extension_misc::kChromeAppId
);
1877 chrome_icon_added
= pinned_apps_pref
->Find(*chrome_app
) !=
1878 pinned_apps_pref
->end();
1882 for (size_t index
= 0; index
< pinned_apps_pref
->GetSize(); ++index
) {
1883 // We need to position the chrome icon relative to it's place in the pinned
1884 // preference list - even if an item of that list isn't shown yet.
1885 if (index
== chrome_icon_index
&& !chrome_icon_added
) {
1886 pinned_apps
.push_back(extension_misc::kChromeAppId
);
1887 chrome_icon_added
= true;
1889 const base::DictionaryValue
* app
= NULL
;
1891 if (pinned_apps_pref
->GetDictionary(index
, &app
) &&
1892 app
->GetString(ash::kPinnedAppsPrefAppIDPath
, &app_id
) &&
1893 (std::find(pinned_apps
.begin(), pinned_apps
.end(), app_id
) ==
1894 pinned_apps
.end())) {
1895 if (app_id
== extension_misc::kChromeAppId
) {
1896 chrome_icon_added
= true;
1897 pinned_apps
.push_back(extension_misc::kChromeAppId
);
1898 } else if (app_id
== kAppShelfIdPlaceholder
) {
1899 app_list_icon_added
= true;
1900 pinned_apps
.push_back(kAppShelfIdPlaceholder
);
1901 } else if (app_tab_helper_
->IsValidIDForCurrentUser(app_id
)) {
1902 // Note: In multi profile scenarios we only want to show pinnable apps
1903 // here which is correct. Running applications from the other users will
1904 // continue to run. So no need for multi profile modifications.
1905 pinned_apps
.push_back(app_id
);
1910 // If not added yet, the chrome item will be the last item in the list.
1911 if (!chrome_icon_added
)
1912 pinned_apps
.push_back(extension_misc::kChromeAppId
);
1914 // If not added yet, add the app list item either at the end or at the
1915 // beginning - depending on the shelf layout.
1916 if (!app_list_icon_added
) {
1917 pinned_apps
.insert(pinned_apps
.begin(), kAppShelfIdPlaceholder
);
1922 bool ChromeLauncherController::IsIncognito(
1923 const content::WebContents
* web_contents
) const {
1924 const Profile
* profile
=
1925 Profile::FromBrowserContext(web_contents
->GetBrowserContext());
1926 return profile
->IsOffTheRecord() && !profile
->IsGuestSession();
1929 void ChromeLauncherController::CloseWindowedAppsFromRemovedExtension(
1930 const std::string
& app_id
) {
1931 // This function cannot rely on the controller's enumeration functionality
1932 // since the extension has already be unloaded.
1933 const BrowserList
* ash_browser_list
=
1934 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH
);
1935 std::vector
<Browser
*> browser_to_close
;
1936 for (BrowserList::const_reverse_iterator
1937 it
= ash_browser_list
->begin_last_active();
1938 it
!= ash_browser_list
->end_last_active(); ++it
) {
1939 Browser
* browser
= *it
;
1940 if (!browser
->is_type_tabbed() &&
1941 browser
->is_type_popup() &&
1942 browser
->is_app() &&
1943 app_id
== web_app::GetExtensionIdFromApplicationName(
1944 browser
->app_name())) {
1945 browser_to_close
.push_back(browser
);
1948 while (!browser_to_close
.empty()) {
1949 TabStripModel
* tab_strip
= browser_to_close
.back()->tab_strip_model();
1950 tab_strip
->CloseWebContentsAt(0, TabStripModel::CLOSE_NONE
);
1951 browser_to_close
.pop_back();
1955 void ChromeLauncherController::SetShelfItemDelegate(
1957 ash::ShelfItemDelegate
* item_delegate
) {
1959 DCHECK(item_delegate
);
1960 DCHECK(item_delegate_manager_
);
1961 item_delegate_manager_
->SetShelfItemDelegate(
1962 id
, scoped_ptr
<ash::ShelfItemDelegate
>(item_delegate
).Pass());
1965 void ChromeLauncherController::AttachProfile(Profile
* profile
) {
1967 // Either add the profile to the list of known profiles and make it the active
1968 // one for some functions of AppTabHelper or create a new one.
1969 if (!app_tab_helper_
.get())
1970 app_tab_helper_
.reset(new LauncherAppTabHelper(profile_
));
1972 app_tab_helper_
->SetCurrentUser(profile_
);
1973 // TODO(skuhne): The AppIconLoaderImpl has the same problem. Each loaded
1974 // image is associated with a profile (it's loader requires the profile).
1975 // Since icon size changes are possible, the icon could be requested to be
1976 // reloaded. However - having it not multi profile aware would cause problems
1977 // if the icon cache gets deleted upon user switch.
1978 app_icon_loader_
.reset(new extensions::AppIconLoaderImpl(
1979 profile_
, extension_misc::EXTENSION_ICON_SMALL
, this));
1981 pref_change_registrar_
.Init(profile_
->GetPrefs());
1982 pref_change_registrar_
.Add(
1983 prefs::kPinnedLauncherApps
,
1984 base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref
,
1985 base::Unretained(this)));
1986 pref_change_registrar_
.Add(
1987 prefs::kShelfAlignmentLocal
,
1988 base::Bind(&ChromeLauncherController::SetShelfAlignmentFromPrefs
,
1989 base::Unretained(this)));
1990 pref_change_registrar_
.Add(
1991 prefs::kShelfAutoHideBehaviorLocal
,
1992 base::Bind(&ChromeLauncherController::
1993 SetShelfAutoHideBehaviorFromPrefs
,
1994 base::Unretained(this)));
1995 pref_change_registrar_
.Add(
1996 prefs::kShelfPreferences
,
1997 base::Bind(&ChromeLauncherController::SetShelfBehaviorsFromPrefs
,
1998 base::Unretained(this)));
2001 void ChromeLauncherController::ReleaseProfile() {
2002 if (app_sync_ui_state_
)
2003 app_sync_ui_state_
->RemoveObserver(this);
2005 PrefServiceSyncable::FromProfile(profile_
)->RemoveObserver(this);
2007 pref_change_registrar_
.RemoveAll();