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/wm/window_util.h"
20 #include "base/command_line.h"
21 #include "base/prefs/scoped_user_pref_update.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/utf_string_conversions.h"
24 #include "base/values.h"
25 #include "chrome/browser/app_mode/app_mode_utils.h"
26 #include "chrome/browser/chrome_notification_types.h"
27 #include "chrome/browser/defaults.h"
28 #include "chrome/browser/extensions/app_icon_loader_impl.h"
29 #include "chrome/browser/extensions/extension_service.h"
30 #include "chrome/browser/extensions/extension_system.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/browser_shortcut_launcher_item_controller.h"
42 #include "chrome/browser/ui/ash/launcher/browser_status_monitor.h"
43 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h"
44 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_browser.h"
45 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_tab.h"
46 #include "chrome/browser/ui/ash/launcher/chrome_launcher_types.h"
47 #include "chrome/browser/ui/ash/launcher/launcher_app_tab_helper.h"
48 #include "chrome/browser/ui/ash/launcher/launcher_item_controller.h"
49 #include "chrome/browser/ui/ash/launcher/shell_window_launcher_controller.h"
50 #include "chrome/browser/ui/ash/launcher/shell_window_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/extensions/manifest_handlers/icons_handler.h"
67 #include "chrome/common/pref_names.h"
68 #include "chrome/common/url_constants.h"
69 #include "content/public/browser/navigation_entry.h"
70 #include "content/public/browser/notification_registrar.h"
71 #include "content/public/browser/notification_service.h"
72 #include "content/public/browser/web_contents.h"
73 #include "extensions/common/extension.h"
74 #include "extensions/common/extension_resource.h"
75 #include "extensions/common/url_pattern.h"
76 #include "grit/ash_resources.h"
77 #include "grit/chromium_strings.h"
78 #include "grit/generated_resources.h"
79 #include "grit/theme_resources.h"
80 #include "grit/ui_resources.h"
81 #include "net/base/url_util.h"
82 #include "ui/aura/root_window.h"
83 #include "ui/aura/window.h"
84 #include "ui/base/l10n/l10n_util.h"
85 #include "ui/views/corewm/window_animations.h"
87 #if defined(OS_CHROMEOS)
88 #include "chrome/browser/browser_process.h"
89 #include "chrome/browser/chromeos/login/user_manager.h"
90 #include "chrome/browser/chromeos/login/wallpaper_manager.h"
91 #include "chrome/browser/ui/ash/chrome_shell_delegate.h"
92 #include "chrome/browser/ui/ash/launcher/multi_profile_browser_status_monitor.h"
93 #include "chrome/browser/ui/ash/launcher/multi_profile_shell_window_launcher_controller.h"
96 using extensions::Extension
;
97 using extensions::UnloadedExtensionInfo
;
98 using extension_misc::kGmailAppId
;
99 using content::WebContents
;
102 ChromeLauncherController
* ChromeLauncherController::instance_
= NULL
;
106 // This will be used as placeholder in the list of the pinned applciatons.
107 // Note that this is NOT a valid extension identifier so that pre M31 versions
109 const char kAppLauncherIdPlaceholder
[] = "AppLauncherIDPlaceholder--------";
111 std::string
GetPrefKeyForRootWindow(aura::Window
* root_window
) {
112 gfx::Display display
= gfx::Screen::GetScreenFor(
113 root_window
)->GetDisplayNearestWindow(root_window
);
114 DCHECK(display
.is_valid());
116 return base::Int64ToString(display
.id());
119 void UpdatePerDisplayPref(PrefService
* pref_service
,
120 aura::Window
* root_window
,
121 const char* pref_key
,
122 const std::string
& value
) {
123 std::string key
= GetPrefKeyForRootWindow(root_window
);
127 DictionaryPrefUpdate
update(pref_service
, prefs::kShelfPreferences
);
128 base::DictionaryValue
* shelf_prefs
= update
.Get();
129 base::DictionaryValue
* prefs
= NULL
;
130 if (!shelf_prefs
->GetDictionary(key
, &prefs
)) {
131 prefs
= new base::DictionaryValue();
132 shelf_prefs
->Set(key
, prefs
);
134 prefs
->SetStringWithoutPathExpansion(pref_key
, value
);
137 // Returns a pref value in |pref_service| for the display of |root_window|. The
138 // pref value is stored in |local_path| and |path|, but |pref_service| may have
139 // per-display preferences and the value can be specified by policy. Here is
141 // * A value managed by policy. This is a single value that applies to all
143 // * A user-set value for the specified display.
144 // * A user-set value in |local_path| or |path|, if no per-display settings are
145 // ever specified (see http://crbug.com/173719 for why). |local_path| is
146 // preferred. See comment in |kShelfAlignment| as to why we consider two
147 // prefs and why |local_path| is preferred.
148 // * A value recommended by policy. This is a single value that applies to all
150 // * The default value for |local_path| if the value is not recommended by
152 std::string
GetPrefForRootWindow(PrefService
* pref_service
,
153 aura::Window
* root_window
,
154 const char* local_path
,
156 const PrefService::Preference
* local_pref
=
157 pref_service
->FindPreference(local_path
);
158 const std::string
value(pref_service
->GetString(local_path
));
159 if (local_pref
->IsManaged())
162 std::string pref_key
= GetPrefKeyForRootWindow(root_window
);
163 bool has_per_display_prefs
= false;
164 if (!pref_key
.empty()) {
165 const base::DictionaryValue
* shelf_prefs
= pref_service
->GetDictionary(
166 prefs::kShelfPreferences
);
167 const base::DictionaryValue
* display_pref
= NULL
;
168 std::string per_display_value
;
169 if (shelf_prefs
->GetDictionary(pref_key
, &display_pref
) &&
170 display_pref
->GetString(path
, &per_display_value
))
171 return per_display_value
;
173 // If the pref for the specified display is not found, scan the whole prefs
174 // and check if the prefs for other display is already specified.
175 std::string unused_value
;
176 for (base::DictionaryValue::Iterator
iter(*shelf_prefs
);
177 !iter
.IsAtEnd(); iter
.Advance()) {
178 const base::DictionaryValue
* display_pref
= NULL
;
179 if (iter
.value().GetAsDictionary(&display_pref
) &&
180 display_pref
->GetString(path
, &unused_value
)) {
181 has_per_display_prefs
= true;
187 if (local_pref
->IsRecommended() || !has_per_display_prefs
)
190 const base::Value
* default_value
=
191 pref_service
->GetDefaultPrefValue(local_path
);
192 std::string default_string
;
193 default_value
->GetAsString(&default_string
);
194 return default_string
;
197 // If prefs have synced and no user-set value exists at |local_path|, the value
198 // from |synced_path| is copied to |local_path|.
199 void MaybePropagatePrefToLocal(PrefServiceSyncable
* pref_service
,
200 const char* local_path
,
201 const char* synced_path
) {
202 if (!pref_service
->FindPreference(local_path
)->HasUserSetting() &&
203 pref_service
->IsSyncing()) {
204 // First time the user is using this machine, propagate from remote to
206 pref_service
->SetString(local_path
, pref_service
->GetString(synced_path
));
210 std::string
GetSourceFromAppListSource(ash::LaunchSource source
) {
212 case ash::LAUNCH_FROM_APP_LIST
:
213 return std::string(extension_urls::kLaunchSourceAppList
);
214 case ash::LAUNCH_FROM_APP_LIST_SEARCH
:
215 return std::string(extension_urls::kLaunchSourceAppListSearch
);
216 default: return std::string();
222 #if defined(OS_CHROMEOS)
223 // A class to get events from ChromeOS when a user gets changed or added.
224 class ChromeLauncherControllerUserSwitchObserverChromeOS
225 : public ChromeLauncherControllerUserSwitchObserver
,
226 public chromeos::UserManager::UserSessionStateObserver
,
227 content::NotificationObserver
{
229 ChromeLauncherControllerUserSwitchObserverChromeOS(
230 ChromeLauncherController
* controller
)
231 : controller_(controller
) {
232 DCHECK(chromeos::UserManager::IsInitialized());
233 chromeos::UserManager::Get()->AddSessionStateObserver(this);
234 // A UserAddedToSession notification can be sent before a profile is loaded.
235 // Since our observers require that we have already a profile, we might have
236 // to postpone the notification until the ProfileManager lets us know that
237 // the profile for that newly added user was added to the ProfileManager.
238 registrar_
.Add(this, chrome::NOTIFICATION_PROFILE_ADDED
,
239 content::NotificationService::AllSources());
241 virtual ~ChromeLauncherControllerUserSwitchObserverChromeOS() {
242 chromeos::UserManager::Get()->RemoveSessionStateObserver(this);
245 // chromeos::UserManager::UserSessionStateObserver overrides:
246 virtual void ActiveUserChanged(const chromeos::User
* active_user
) OVERRIDE
;
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::ActiveUserChanged(
273 const chromeos::User
* active_user
) {
274 const std::string
& user_email
= active_user
->email();
275 // Forward the OS specific event to the ChromeLauncherController.
276 controller_
->ActiveUserChanged(user_email
);
277 // TODO(skuhne): At the moment the login screen does the wallpaper management
278 // and wallpapers are not synchronized across multiple desktops.
279 if (chromeos::WallpaperManager::Get())
280 chromeos::WallpaperManager::Get()->SetUserWallpaper(user_email
);
283 void ChromeLauncherControllerUserSwitchObserverChromeOS::UserAddedToSession(
284 const chromeos::User
* active_user
) {
285 Profile
* profile
= multi_user_util::GetProfileFromUserID(
286 active_user
->email());
287 // If we do not have a profile yet, we postpone forwarding the notification
288 // until it is loaded.
290 added_user_ids_waiting_for_profiles_
.insert(active_user
->email());
295 void ChromeLauncherControllerUserSwitchObserverChromeOS::Observe(
297 const content::NotificationSource
& source
,
298 const content::NotificationDetails
& details
) {
299 if (type
== chrome::NOTIFICATION_PROFILE_ADDED
&&
300 !added_user_ids_waiting_for_profiles_
.empty()) {
301 // Check if the profile is from a user which was on the waiting list.
302 Profile
* profile
= content::Source
<Profile
>(source
).ptr();
303 std::string user_id
= multi_user_util::GetUserIDFromProfile(profile
);
304 std::set
<std::string
>::iterator it
= std::find(
305 added_user_ids_waiting_for_profiles_
.begin(),
306 added_user_ids_waiting_for_profiles_
.end(),
308 if (it
!= added_user_ids_waiting_for_profiles_
.end()) {
309 added_user_ids_waiting_for_profiles_
.erase(it
);
310 AddUser(profile
->GetOriginalProfile());
315 void ChromeLauncherControllerUserSwitchObserverChromeOS::AddUser(
317 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
318 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED
)
319 chrome::MultiUserWindowManager::GetInstance()->AddUser(profile
);
320 controller_
->AdditionalUserAddedToSession(profile
->GetOriginalProfile());
324 ChromeLauncherController::ChromeLauncherController(Profile
* profile
,
325 ash::ShelfModel
* model
)
327 item_delegate_manager_(NULL
),
329 app_sync_ui_state_(NULL
),
330 ignore_persist_pinned_state_change_(false) {
332 // If no profile was passed, we take the currently active profile and use it
333 // as the owner of the current desktop.
334 // Use the original profile as on chromeos we may get a temporary off the
335 // record profile, unless in guest session (where off the record profile is
337 Profile
* active_profile
= ProfileManager::GetActiveUserProfile();
338 profile_
= active_profile
->IsGuestSession() ? active_profile
:
339 active_profile
->GetOriginalProfile();
341 app_sync_ui_state_
= AppSyncUIState::Get(profile_
);
342 if (app_sync_ui_state_
)
343 app_sync_ui_state_
->AddObserver(this);
346 // All profile relevant settings get bound to the current profile.
347 AttachProfile(profile_
);
348 model_
->AddObserver(this);
350 // In multi profile mode we might have a window manager. We try to create it
351 // here. If the instantiation fails, the manager is not needed.
352 chrome::MultiUserWindowManager::CreateInstance();
354 #if defined(OS_CHROMEOS)
355 // On Chrome OS using multi profile we want to switch the content of the shelf
356 // with a user change. Note that for unit tests the instance can be NULL.
357 if (chrome::MultiUserWindowManager::GetMultiProfileMode() !=
358 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_OFF
) {
359 user_switch_observer_
.reset(
360 new ChromeLauncherControllerUserSwitchObserverChromeOS(this));
363 // Create our v1/v2 application / browser monitors which will inform the
364 // launcher of status changes.
365 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
366 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED
) {
367 // If running in separated destkop mode, we create the multi profile version
368 // of status monitor.
369 browser_status_monitor_
.reset(new MultiProfileBrowserStatusMonitor(this));
370 shell_window_controller_
.reset(
371 new MultiProfileShellWindowLauncherController(this));
373 // Create our v1/v2 application / browser monitors which will inform the
374 // launcher of status changes.
375 browser_status_monitor_
.reset(new BrowserStatusMonitor(this));
376 shell_window_controller_
.reset(new ShellWindowLauncherController(this));
379 // Create our v1/v2 application / browser monitors which will inform the
380 // launcher of status changes.
381 browser_status_monitor_
.reset(new BrowserStatusMonitor(this));
382 shell_window_controller_
.reset(new ShellWindowLauncherController(this));
385 // Right now ash::Shell isn't created for tests.
386 // TODO(mukai): Allows it to observe display change and write tests.
387 if (ash::Shell::HasInstance()) {
388 ash::Shell::GetInstance()->display_controller()->AddObserver(this);
389 item_delegate_manager_
=
390 ash::Shell::GetInstance()->shelf_item_delegate_manager();
393 notification_registrar_
.Add(this,
394 chrome::NOTIFICATION_EXTENSION_LOADED
,
395 content::Source
<Profile
>(profile_
));
396 notification_registrar_
.Add(this,
397 chrome::NOTIFICATION_EXTENSION_UNLOADED
,
398 content::Source
<Profile
>(profile_
));
401 ChromeLauncherController::~ChromeLauncherController() {
402 // Reset the BrowserStatusMonitor as it has a weak pointer to this.
403 browser_status_monitor_
.reset();
405 // Reset the shell window controller here since it has a weak pointer to this.
406 shell_window_controller_
.reset();
408 for (std::set
<ash::Shelf
*>::iterator iter
= shelves_
.begin();
409 iter
!= shelves_
.end();
411 (*iter
)->shelf_widget()->shelf_layout_manager()->RemoveObserver(this);
413 model_
->RemoveObserver(this);
414 if (ash::Shell::HasInstance())
415 ash::Shell::GetInstance()->display_controller()->RemoveObserver(this);
416 for (IDToItemControllerMap::iterator i
= id_to_item_controller_map_
.begin();
417 i
!= id_to_item_controller_map_
.end(); ++i
) {
418 int index
= model_
->ItemIndexByID(i
->first
);
419 // A "browser proxy" is not known to the model and this removal does
420 // therefore not need to be propagated to the model.
422 model_
->items()[index
].type
!= ash::TYPE_BROWSER_SHORTCUT
)
423 model_
->RemoveItemAt(index
);
426 if (ash::Shell::HasInstance())
427 ash::Shell::GetInstance()->RemoveShellObserver(this);
429 // Release all profile dependent resources.
431 if (instance_
== this)
434 // Get rid of the multi user window manager instance.
435 chrome::MultiUserWindowManager::DeleteInstance();
439 ChromeLauncherController
* ChromeLauncherController::CreateInstance(
441 ash::ShelfModel
* model
) {
442 // We do not check here for re-creation of the ChromeLauncherController since
443 // it appears that it might be intentional that the ChromeLauncherController
444 // can be re-created.
445 instance_
= new ChromeLauncherController(profile
, model
);
449 void ChromeLauncherController::Init() {
450 CreateBrowserShortcutLauncherItem();
451 UpdateAppLaunchersFromPref();
453 // TODO(sky): update unit test so that this test isn't necessary.
454 if (ash::Shell::HasInstance()) {
455 SetShelfAutoHideBehaviorFromPrefs();
456 SetShelfAlignmentFromPrefs();
457 PrefServiceSyncable
* prefs
= PrefServiceSyncable::FromProfile(profile_
);
458 if (!prefs
->FindPreference(prefs::kShelfAlignmentLocal
)->HasUserSetting() ||
459 !prefs
->FindPreference(prefs::kShelfAutoHideBehaviorLocal
)->
461 // This causes OnIsSyncingChanged to be called when the value of
462 // PrefService::IsSyncing() changes.
463 prefs
->AddObserver(this);
465 ash::Shell::GetInstance()->AddShellObserver(this);
469 ash::LauncherID
ChromeLauncherController::CreateAppLauncherItem(
470 LauncherItemController
* controller
,
471 const std::string
& app_id
,
472 ash::LauncherItemStatus status
) {
475 // Panels are inserted on the left so as not to push all existing panels over.
476 if (controller
->GetLauncherItemType() != ash::TYPE_APP_PANEL
)
477 index
= model_
->item_count();
478 return InsertAppLauncherItem(controller
,
482 controller
->GetLauncherItemType());
485 void ChromeLauncherController::SetItemStatus(
487 ash::LauncherItemStatus status
) {
488 int index
= model_
->ItemIndexByID(id
);
489 ash::LauncherItemStatus old_status
= model_
->items()[index
].status
;
490 // Since ordinary browser windows are not registered, we might get a negative
492 if (index
>= 0 && old_status
!= status
) {
493 ash::LauncherItem item
= model_
->items()[index
];
494 item
.status
= status
;
495 model_
->Set(index
, item
);
499 void ChromeLauncherController::SetItemController(
501 LauncherItemController
* controller
) {
503 IDToItemControllerMap::iterator iter
= id_to_item_controller_map_
.find(id
);
504 CHECK(iter
!= id_to_item_controller_map_
.end());
505 controller
->set_launcher_id(id
);
506 iter
->second
= controller
;
507 // Existing controller is destroyed and replaced by registering again.
508 SetShelfItemDelegate(id
, controller
);
511 void ChromeLauncherController::CloseLauncherItem(ash::LauncherID id
) {
514 // Create a new shortcut controller.
515 IDToItemControllerMap::iterator iter
= id_to_item_controller_map_
.find(id
);
516 CHECK(iter
!= id_to_item_controller_map_
.end());
517 SetItemStatus(id
, ash::STATUS_CLOSED
);
518 std::string app_id
= iter
->second
->app_id();
519 iter
->second
= new AppShortcutLauncherItemController(app_id
, this);
520 iter
->second
->set_launcher_id(id
);
521 // Existing controller is destroyed and replaced by registering again.
522 SetShelfItemDelegate(id
, iter
->second
);
524 LauncherItemClosed(id
);
528 void ChromeLauncherController::Pin(ash::LauncherID id
) {
529 DCHECK(HasItemController(id
));
531 int index
= model_
->ItemIndexByID(id
);
534 ash::LauncherItem item
= model_
->items()[index
];
536 if (item
.type
== ash::TYPE_PLATFORM_APP
||
537 item
.type
== ash::TYPE_WINDOWED_APP
) {
538 item
.type
= ash::TYPE_APP_SHORTCUT
;
539 model_
->Set(index
, item
);
540 } else if (item
.type
!= ash::TYPE_APP_SHORTCUT
) {
545 PersistPinnedState();
548 void ChromeLauncherController::Unpin(ash::LauncherID id
) {
549 DCHECK(HasItemController(id
));
551 LauncherItemController
* controller
= id_to_item_controller_map_
[id
];
552 if (controller
->type() == LauncherItemController::TYPE_APP
||
553 controller
->locked()) {
554 UnpinRunningAppInternal(model_
->ItemIndexByID(id
));
556 LauncherItemClosed(id
);
559 PersistPinnedState();
562 bool ChromeLauncherController::IsPinned(ash::LauncherID id
) {
563 int index
= model_
->ItemIndexByID(id
);
566 ash::LauncherItemType type
= model_
->items()[index
].type
;
567 return (type
== ash::TYPE_APP_SHORTCUT
|| type
== ash::TYPE_BROWSER_SHORTCUT
);
570 void ChromeLauncherController::TogglePinned(ash::LauncherID id
) {
571 if (!HasItemController(id
))
572 return; // May happen if item closed with menu open.
580 bool ChromeLauncherController::IsPinnable(ash::LauncherID id
) const {
581 int index
= model_
->ItemIndexByID(id
);
585 ash::LauncherItemType type
= model_
->items()[index
].type
;
586 return ((type
== ash::TYPE_APP_SHORTCUT
||
587 type
== ash::TYPE_PLATFORM_APP
||
588 type
== ash::TYPE_WINDOWED_APP
) &&
592 void ChromeLauncherController::LockV1AppWithID(
593 const std::string
& app_id
) {
594 ash::LauncherID id
= GetLauncherIDForAppID(app_id
);
595 if (!IsPinned(id
) && !IsWindowedAppInLauncher(app_id
)) {
596 CreateAppShortcutLauncherItemWithType(app_id
,
597 model_
->item_count(),
598 ash::TYPE_WINDOWED_APP
);
599 id
= GetLauncherIDForAppID(app_id
);
602 id_to_item_controller_map_
[id
]->lock();
605 void ChromeLauncherController::UnlockV1AppWithID(
606 const std::string
& app_id
) {
607 ash::LauncherID id
= GetLauncherIDForAppID(app_id
);
608 CHECK(IsPinned(id
) || IsWindowedAppInLauncher(app_id
));
610 LauncherItemController
* controller
= id_to_item_controller_map_
[id
];
611 controller
->unlock();
612 if (!controller
->locked() && !IsPinned(id
))
613 CloseLauncherItem(id
);
616 void ChromeLauncherController::Launch(ash::LauncherID id
,
618 if (!HasItemController(id
))
619 return; // In case invoked from menu and item closed while menu up.
620 id_to_item_controller_map_
[id
]->Launch(ash::LAUNCH_FROM_UNKNOWN
, event_flags
);
623 void ChromeLauncherController::Close(ash::LauncherID id
) {
624 if (!HasItemController(id
))
625 return; // May happen if menu closed.
626 id_to_item_controller_map_
[id
]->Close();
629 bool ChromeLauncherController::IsOpen(ash::LauncherID id
) {
630 if (!HasItemController(id
))
632 return id_to_item_controller_map_
[id
]->IsOpen();
635 bool ChromeLauncherController::IsPlatformApp(ash::LauncherID id
) {
636 if (!HasItemController(id
))
639 std::string app_id
= GetAppIDForLauncherID(id
);
640 const Extension
* extension
= GetExtensionForAppID(app_id
);
641 // An extension can be synced / updated at any time and therefore not be
643 return extension
? extension
->is_platform_app() : false;
646 void ChromeLauncherController::LaunchApp(const std::string
& app_id
,
647 ash::LaunchSource source
,
649 // |extension| could be NULL when it is being unloaded for updating.
650 const Extension
* extension
= GetExtensionForAppID(app_id
);
654 const ExtensionService
* service
=
655 extensions::ExtensionSystem::Get(profile_
)->extension_service();
656 if (!extension_util::IsAppLaunchableWithoutEnabling(app_id
, service
)) {
657 // Do nothing if there is already a running enable flow.
658 if (extension_enable_flow_
)
661 extension_enable_flow_
.reset(
662 new ExtensionEnableFlow(profile_
, app_id
, this));
663 extension_enable_flow_
->StartForNativeWindow(NULL
);
667 if (LaunchedInNativeDesktop(app_id
))
670 // The app will be created for the currently active profile.
671 AppLaunchParams
params(profile_
,
674 chrome::HOST_DESKTOP_TYPE_ASH
);
675 if (source
!= ash::LAUNCH_FROM_UNKNOWN
&&
676 app_id
== extension_misc::kWebStoreAppId
) {
677 // Get the corresponding source string.
678 std::string source_value
= GetSourceFromAppListSource(source
);
680 // Set an override URL to include the source.
681 GURL extension_url
= extensions::AppLaunchInfo::GetFullLaunchURL(extension
);
682 params
.override_url
= net::AppendQueryParameter(
683 extension_url
, extension_urls::kWebstoreSourceField
, source_value
);
686 OpenApplication(params
);
689 void ChromeLauncherController::ActivateApp(const std::string
& app_id
,
690 ash::LaunchSource source
,
692 // If there is an existing non-shortcut controller for this app, open it.
693 ash::LauncherID id
= GetLauncherIDForAppID(app_id
);
695 LauncherItemController
* controller
= id_to_item_controller_map_
[id
];
696 controller
->Activate(source
);
700 // Create a temporary application launcher item and use it to see if there are
701 // running instances.
702 scoped_ptr
<AppShortcutLauncherItemController
> app_controller(
703 new AppShortcutLauncherItemController(app_id
, this));
704 if (!app_controller
->GetRunningApplications().empty())
705 app_controller
->Activate(source
);
707 LaunchApp(app_id
, source
, event_flags
);
710 extensions::LaunchType
ChromeLauncherController::GetLaunchType(
711 ash::LauncherID id
) {
712 DCHECK(HasItemController(id
));
714 const Extension
* extension
= GetExtensionForAppID(
715 id_to_item_controller_map_
[id
]->app_id());
717 // An extension can be unloaded/updated/unavailable at any time.
719 return extensions::LAUNCH_TYPE_DEFAULT
;
721 return extensions::GetLaunchType(
722 profile_
->GetExtensionService()->extension_prefs(),
726 ash::LauncherID
ChromeLauncherController::GetLauncherIDForAppID(
727 const std::string
& app_id
) {
728 for (IDToItemControllerMap::const_iterator i
=
729 id_to_item_controller_map_
.begin();
730 i
!= id_to_item_controller_map_
.end(); ++i
) {
731 if (i
->second
->type() == LauncherItemController::TYPE_APP_PANEL
)
732 continue; // Don't include panels
733 if (i
->second
->app_id() == app_id
)
739 const std::string
& ChromeLauncherController::GetAppIDForLauncherID(
740 ash::LauncherID id
) {
741 CHECK(HasItemController(id
));
742 return id_to_item_controller_map_
[id
]->app_id();
745 void ChromeLauncherController::SetAppImage(const std::string
& id
,
746 const gfx::ImageSkia
& image
) {
747 // TODO: need to get this working for shortcuts.
748 for (IDToItemControllerMap::const_iterator i
=
749 id_to_item_controller_map_
.begin();
750 i
!= id_to_item_controller_map_
.end(); ++i
) {
751 LauncherItemController
* controller
= i
->second
;
752 if (controller
->app_id() != id
)
754 if (controller
->image_set_by_controller())
756 int index
= model_
->ItemIndexByID(i
->first
);
759 ash::LauncherItem item
= model_
->items()[index
];
761 model_
->Set(index
, item
);
762 // It's possible we're waiting on more than one item, so don't break.
766 void ChromeLauncherController::OnAutoHideBehaviorChanged(
767 aura::Window
* root_window
,
768 ash::ShelfAutoHideBehavior new_behavior
) {
769 SetShelfAutoHideBehaviorPrefs(new_behavior
, root_window
);
772 void ChromeLauncherController::SetLauncherItemImage(
773 ash::LauncherID launcher_id
,
774 const gfx::ImageSkia
& image
) {
775 int index
= model_
->ItemIndexByID(launcher_id
);
778 ash::LauncherItem item
= model_
->items()[index
];
780 model_
->Set(index
, item
);
783 bool ChromeLauncherController::CanPin() const {
784 const PrefService::Preference
* pref
=
785 profile_
->GetPrefs()->FindPreference(prefs::kPinnedLauncherApps
);
786 return pref
&& pref
->IsUserModifiable();
789 bool ChromeLauncherController::IsAppPinned(const std::string
& app_id
) {
790 for (IDToItemControllerMap::const_iterator i
=
791 id_to_item_controller_map_
.begin();
792 i
!= id_to_item_controller_map_
.end(); ++i
) {
793 if (IsPinned(i
->first
) && i
->second
->app_id() == app_id
)
799 bool ChromeLauncherController::IsWindowedAppInLauncher(
800 const std::string
& app_id
) {
801 int index
= model_
->ItemIndexByID(GetLauncherIDForAppID(app_id
));
805 ash::LauncherItemType type
= model_
->items()[index
].type
;
806 return type
== ash::TYPE_WINDOWED_APP
;
809 void ChromeLauncherController::PinAppWithID(const std::string
& app_id
) {
811 DoPinAppWithID(app_id
);
816 void ChromeLauncherController::SetLaunchType(
818 extensions::LaunchType launch_type
) {
819 if (!HasItemController(id
))
822 extensions::SetLaunchType(profile_
->GetExtensionService(),
823 id_to_item_controller_map_
[id
]->app_id(),
827 void ChromeLauncherController::UnpinAppWithID(const std::string
& app_id
) {
829 DoUnpinAppWithID(app_id
);
834 bool ChromeLauncherController::IsLoggedInAsGuest() {
835 return profile_
->IsGuestSession();
838 void ChromeLauncherController::CreateNewWindow() {
839 // Use the currently active user.
840 chrome::NewEmptyWindow(profile_
, chrome::HOST_DESKTOP_TYPE_ASH
);
843 void ChromeLauncherController::CreateNewIncognitoWindow() {
844 // Use the currently active user.
845 chrome::NewEmptyWindow(profile_
->GetOffTheRecordProfile(),
846 chrome::HOST_DESKTOP_TYPE_ASH
);
849 void ChromeLauncherController::PersistPinnedState() {
850 if (ignore_persist_pinned_state_change_
)
852 // It is a coding error to call PersistPinnedState() if the pinned apps are
853 // not user-editable. The code should check earlier and not perform any
854 // modification actions that trigger persisting the state.
856 NOTREACHED() << "Can't pin but pinned state being updated";
859 // Mutating kPinnedLauncherApps is going to notify us and trigger us to
860 // process the change. We don't want that to happen so remove ourselves as a
862 pref_change_registrar_
.Remove(prefs::kPinnedLauncherApps
);
864 ListPrefUpdate
updater(profile_
->GetPrefs(), prefs::kPinnedLauncherApps
);
866 for (size_t i
= 0; i
< model_
->items().size(); ++i
) {
867 if (model_
->items()[i
].type
== ash::TYPE_APP_SHORTCUT
) {
868 ash::LauncherID id
= model_
->items()[i
].id
;
869 if (HasItemController(id
) && IsPinned(id
)) {
870 base::DictionaryValue
* app_value
= ash::CreateAppDict(
871 id_to_item_controller_map_
[id
]->app_id());
873 updater
->Append(app_value
);
875 } else if (model_
->items()[i
].type
== ash::TYPE_BROWSER_SHORTCUT
) {
876 PersistChromeItemIndex(i
);
877 } else if (model_
->items()[i
].type
== ash::TYPE_APP_LIST
) {
878 base::DictionaryValue
* app_value
= ash::CreateAppDict(
879 kAppLauncherIdPlaceholder
);
881 updater
->Append(app_value
);
885 pref_change_registrar_
.Add(
886 prefs::kPinnedLauncherApps
,
887 base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref
,
888 base::Unretained(this)));
891 ash::ShelfModel
* ChromeLauncherController::model() {
895 Profile
* ChromeLauncherController::profile() {
899 ash::ShelfAutoHideBehavior
ChromeLauncherController::GetShelfAutoHideBehavior(
900 aura::Window
* root_window
) const {
901 // Don't show the shelf in app mode.
902 if (chrome::IsRunningInAppMode())
903 return ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN
;
906 // Autohide functionality temporarily disabled for windows due to
907 // issue crbug.com/292864.
908 // TODO(shrikant): Remove this once the issue gets resolved.
909 return ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER
;
912 // See comment in |kShelfAlignment| as to why we consider two prefs.
913 const std::string
behavior_value(
914 GetPrefForRootWindow(profile_
->GetPrefs(),
916 prefs::kShelfAutoHideBehaviorLocal
,
917 prefs::kShelfAutoHideBehavior
));
919 // Note: To maintain sync compatibility with old images of chrome/chromeos
920 // the set of values that may be encountered includes the now-extinct
921 // "Default" as well as "Never" and "Always", "Default" should now
922 // be treated as "Never" (http://crbug.com/146773).
923 if (behavior_value
== ash::kShelfAutoHideBehaviorAlways
)
924 return ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS
;
925 return ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER
;
928 bool ChromeLauncherController::CanUserModifyShelfAutoHideBehavior(
929 aura::Window
* root_window
) const {
931 // Autohide functionality temporarily disabled for windows due to
932 // issue crbug.com/292864.
933 // TODO(shrikant): Remove this once the issue gets resolved.
936 return profile_
->GetPrefs()->
937 FindPreference(prefs::kShelfAutoHideBehaviorLocal
)->IsUserModifiable();
940 void ChromeLauncherController::ToggleShelfAutoHideBehavior(
941 aura::Window
* root_window
) {
942 ash::ShelfAutoHideBehavior behavior
= GetShelfAutoHideBehavior(root_window
) ==
943 ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS
?
944 ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER
:
945 ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS
;
946 SetShelfAutoHideBehaviorPrefs(behavior
, root_window
);
950 void ChromeLauncherController::RemoveTabFromRunningApp(
952 const std::string
& app_id
) {
953 web_contents_to_app_id_
.erase(tab
);
954 // BrowserShortcutLauncherItemController::UpdateBrowserItemState() will update
955 // the state when no application is associated with the tab.
959 AppIDToWebContentsListMap::iterator i_app_id
=
960 app_id_to_web_contents_list_
.find(app_id
);
961 if (i_app_id
!= app_id_to_web_contents_list_
.end()) {
962 WebContentsList
* tab_list
= &i_app_id
->second
;
963 tab_list
->remove(tab
);
964 ash::LauncherItemStatus status
= ash::STATUS_RUNNING
;
965 if (tab_list
->empty()) {
966 app_id_to_web_contents_list_
.erase(i_app_id
);
967 status
= ash::STATUS_CLOSED
;
969 ash::LauncherID id
= GetLauncherIDForAppID(app_id
);
971 SetItemStatus(id
, status
);
975 void ChromeLauncherController::UpdateAppState(content::WebContents
* contents
,
976 AppState app_state
) {
977 std::string app_id
= app_tab_helper_
->GetAppID(contents
);
979 // Check if the gMail app is loaded and it matches the given content.
980 // This special treatment is needed to address crbug.com/234268.
981 if (app_id
.empty() && ContentCanBeHandledByGmailApp(contents
))
982 app_id
= kGmailAppId
;
984 // Check the old |app_id| for a tab. If the contents has changed we need to
985 // remove it from the previous app.
986 if (web_contents_to_app_id_
.find(contents
) != web_contents_to_app_id_
.end()) {
987 std::string last_app_id
= web_contents_to_app_id_
[contents
];
988 if (last_app_id
!= app_id
)
989 RemoveTabFromRunningApp(contents
, last_app_id
);
992 web_contents_to_app_id_
[contents
] = app_id
;
994 if (app_state
== APP_STATE_REMOVED
) {
995 // The tab has gone away.
996 RemoveTabFromRunningApp(contents
, app_id
);
997 } else if (!app_id
.empty()) {
998 WebContentsList
& tab_list(app_id_to_web_contents_list_
[app_id
]);
999 WebContentsList::const_iterator i_tab
=
1000 std::find(tab_list
.begin(), tab_list
.end(), contents
);
1002 if (i_tab
== tab_list
.end())
1003 tab_list
.push_back(contents
);
1005 if (app_state
== APP_STATE_INACTIVE
|| app_state
== APP_STATE_ACTIVE
) {
1006 if (i_tab
!= tab_list
.begin()) {
1007 // Going to running state, but wasn't the front tab, indicating that a
1008 // new tab has already become active.
1013 if (app_state
== APP_STATE_ACTIVE
|| app_state
== APP_STATE_WINDOW_ACTIVE
) {
1014 tab_list
.remove(contents
);
1015 tab_list
.push_front(contents
);
1018 ash::LauncherID id
= GetLauncherIDForAppID(app_id
);
1020 // If the window is active, mark the app as active.
1021 SetItemStatus(id
, app_state
== APP_STATE_WINDOW_ACTIVE
?
1022 ash::STATUS_ACTIVE
: ash::STATUS_RUNNING
);
1027 ash::LauncherID
ChromeLauncherController::GetLauncherIDForWebContents(
1028 content::WebContents
* contents
) {
1031 std::string app_id
= app_tab_helper_
->GetAppID(contents
);
1033 if (app_id
.empty() && ContentCanBeHandledByGmailApp(contents
))
1034 app_id
= kGmailAppId
;
1036 ash::LauncherID id
= GetLauncherIDForAppID(app_id
);
1038 if (app_id
.empty() || !id
) {
1039 int browser_index
= model_
->GetItemIndexForType(ash::TYPE_BROWSER_SHORTCUT
);
1040 return model_
->items()[browser_index
].id
;
1046 void ChromeLauncherController::SetRefocusURLPatternForTest(ash::LauncherID id
,
1048 DCHECK(HasItemController(id
));
1049 LauncherItemController
* controller
= id_to_item_controller_map_
[id
];
1051 int index
= model_
->ItemIndexByID(id
);
1053 NOTREACHED() << "Invalid launcher id";
1057 ash::LauncherItemType type
= model_
->items()[index
].type
;
1058 if (type
== ash::TYPE_APP_SHORTCUT
|| type
== ash::TYPE_WINDOWED_APP
) {
1059 AppShortcutLauncherItemController
* app_controller
=
1060 static_cast<AppShortcutLauncherItemController
*>(controller
);
1061 app_controller
->set_refocus_url(url
);
1063 NOTREACHED() << "Invalid launcher type";
1067 const Extension
* ChromeLauncherController::GetExtensionForAppID(
1068 const std::string
& app_id
) const {
1069 // Some unit tests do not have a real extension.
1070 return (profile_
->GetExtensionService()) ?
1071 profile_
->GetExtensionService()->GetInstalledExtension(app_id
) : NULL
;
1074 void ChromeLauncherController::ActivateWindowOrMinimizeIfActive(
1075 ui::BaseWindow
* window
,
1076 bool allow_minimize
) {
1077 // In separated desktop mode we might have to teleport a window back to the
1079 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
1080 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED
) {
1081 aura::Window
* native_window
= window
->GetNativeWindow();
1082 const std::string
& current_user
=
1083 multi_user_util::GetUserIDFromProfile(profile());
1084 chrome::MultiUserWindowManager
* manager
=
1085 chrome::MultiUserWindowManager::GetInstance();
1086 if (!manager
->IsWindowOnDesktopOfUser(native_window
, current_user
)) {
1087 ash::MultiProfileUMA::RecordTeleportAction(
1088 ash::MultiProfileUMA::TELEPORT_WINDOW_RETURN_BY_LAUNCHER
);
1089 manager
->ShowWindowForUser(native_window
, current_user
);
1095 if (window
->IsActive() && allow_minimize
) {
1096 if (CommandLine::ForCurrentProcess()->HasSwitch(
1097 switches::kDisableMinimizeOnSecondLauncherItemClick
)) {
1098 AnimateWindow(window
->GetNativeWindow(),
1099 views::corewm::WINDOW_ANIMATION_TYPE_BOUNCE
);
1109 void ChromeLauncherController::OnShelfCreated(ash::Shelf
* shelf
) {
1110 shelves_
.insert(shelf
);
1111 shelf
->shelf_widget()->shelf_layout_manager()->AddObserver(this);
1114 void ChromeLauncherController::OnShelfDestroyed(ash::Shelf
* shelf
) {
1115 shelves_
.erase(shelf
);
1116 // RemoveObserver is not called here, since by the time this method is called
1117 // Shelf is already in its destructor.
1120 void ChromeLauncherController::ShelfItemAdded(int index
) {
1121 // The app list launcher can get added to the shelf after we applied the
1122 // preferences. In that case the item might be at the wrong spot. As such we
1123 // call the function again.
1124 if (model_
->items()[index
].type
== ash::TYPE_APP_LIST
&&
1125 ash::switches::UseAlternateShelfLayout())
1126 UpdateAppLaunchersFromPref();
1129 void ChromeLauncherController::ShelfItemRemoved(int index
, ash::LauncherID id
) {
1132 void ChromeLauncherController::ShelfItemMoved(int start_index
,
1134 const ash::LauncherItem
& item
= model_
->items()[target_index
];
1135 // We remember the moved item position if it is either pinnable or
1136 // it is the app list with the alternate shelf layout.
1137 if ((HasItemController(item
.id
) && IsPinned(item
.id
)) ||
1138 (ash::switches::UseAlternateShelfLayout() &&
1139 item
.type
== ash::TYPE_APP_LIST
))
1140 PersistPinnedState();
1143 void ChromeLauncherController::ShelfItemChanged(
1145 const ash::LauncherItem
& old_item
) {
1148 void ChromeLauncherController::ShelfStatusChanged() {
1151 void ChromeLauncherController::ActiveUserChanged(
1152 const std::string
& user_email
) {
1153 // Store the order of running applications for the user which gets inactive.
1154 RememberUnpinnedRunningApplicationOrder();
1155 // Coming here the default profile is already switched. All profile specific
1156 // resources get released and the new profile gets attached instead.
1158 // When coming here, the active user has already be changed so that we can
1159 // set it as active.
1160 AttachProfile(ProfileManager::GetActiveUserProfile());
1161 // Update the V1 applications.
1162 browser_status_monitor_
->ActiveUserChanged(user_email
);
1163 // Switch the running applications to the new user.
1164 shell_window_controller_
->ActiveUserChanged(user_email
);
1165 // Update the user specific shell properties from the new user profile.
1166 UpdateAppLaunchersFromPref();
1167 SetShelfAlignmentFromPrefs();
1168 SetShelfAutoHideBehaviorFromPrefs();
1169 SetShelfBehaviorsFromPrefs();
1170 // Restore the order of running, but unpinned applications for the activated
1172 RestoreUnpinnedRunningApplicationOrder(user_email
);
1175 void ChromeLauncherController::AdditionalUserAddedToSession(Profile
* profile
) {
1176 // Switch the running applications to the new user.
1177 shell_window_controller_
->AdditionalUserAddedToSession(profile
);
1180 void ChromeLauncherController::Observe(
1182 const content::NotificationSource
& source
,
1183 const content::NotificationDetails
& details
) {
1185 case chrome::NOTIFICATION_EXTENSION_LOADED
: {
1186 const Extension
* extension
=
1187 content::Details
<const Extension
>(details
).ptr();
1188 if (IsAppPinned(extension
->id())) {
1189 // Clear and re-fetch to ensure icon is up-to-date.
1190 app_icon_loader_
->ClearImage(extension
->id());
1191 app_icon_loader_
->FetchImage(extension
->id());
1194 UpdateAppLaunchersFromPref();
1197 case chrome::NOTIFICATION_EXTENSION_UNLOADED
: {
1198 const content::Details
<UnloadedExtensionInfo
>& unload_info(details
);
1199 const Extension
* extension
= unload_info
->extension
;
1200 const std::string
& id
= extension
->id();
1201 // Since we might have windowed apps of this type which might have
1202 // outstanding locks which needs to be removed.
1203 if (GetLauncherIDForAppID(id
) &&
1204 unload_info
->reason
== UnloadedExtensionInfo::REASON_UNINSTALL
) {
1205 CloseWindowedAppsFromRemovedExtension(id
);
1208 if (IsAppPinned(id
)) {
1209 if (unload_info
->reason
== UnloadedExtensionInfo::REASON_UNINSTALL
) {
1210 DoUnpinAppWithID(id
);
1211 app_icon_loader_
->ClearImage(id
);
1213 app_icon_loader_
->UpdateImage(id
);
1219 NOTREACHED() << "Unexpected notification type=" << type
;
1223 void ChromeLauncherController::OnShelfAlignmentChanged(
1224 aura::Window
* root_window
) {
1225 const char* pref_value
= NULL
;
1226 switch (ash::Shell::GetInstance()->GetShelfAlignment(root_window
)) {
1227 case ash::SHELF_ALIGNMENT_BOTTOM
:
1228 pref_value
= ash::kShelfAlignmentBottom
;
1230 case ash::SHELF_ALIGNMENT_LEFT
:
1231 pref_value
= ash::kShelfAlignmentLeft
;
1233 case ash::SHELF_ALIGNMENT_RIGHT
:
1234 pref_value
= ash::kShelfAlignmentRight
;
1236 case ash::SHELF_ALIGNMENT_TOP
:
1237 pref_value
= ash::kShelfAlignmentTop
;
1240 UpdatePerDisplayPref(
1241 profile_
->GetPrefs(), root_window
, prefs::kShelfAlignment
, pref_value
);
1243 if (root_window
== ash::Shell::GetPrimaryRootWindow()) {
1244 // See comment in |kShelfAlignment| about why we have two prefs here.
1245 profile_
->GetPrefs()->SetString(prefs::kShelfAlignmentLocal
, pref_value
);
1246 profile_
->GetPrefs()->SetString(prefs::kShelfAlignment
, pref_value
);
1250 void ChromeLauncherController::OnDisplayConfigurationChanging() {
1253 void ChromeLauncherController::OnDisplayConfigurationChanged() {
1254 SetShelfBehaviorsFromPrefs();
1257 void ChromeLauncherController::OnIsSyncingChanged() {
1258 PrefServiceSyncable
* prefs
= PrefServiceSyncable::FromProfile(profile_
);
1259 MaybePropagatePrefToLocal(prefs
,
1260 prefs::kShelfAlignmentLocal
,
1261 prefs::kShelfAlignment
);
1262 MaybePropagatePrefToLocal(prefs
,
1263 prefs::kShelfAutoHideBehaviorLocal
,
1264 prefs::kShelfAutoHideBehavior
);
1267 void ChromeLauncherController::OnAppSyncUIStatusChanged() {
1268 if (app_sync_ui_state_
->status() == AppSyncUIState::STATUS_SYNCING
)
1269 model_
->SetStatus(ash::ShelfModel::STATUS_LOADING
);
1271 model_
->SetStatus(ash::ShelfModel::STATUS_NORMAL
);
1274 void ChromeLauncherController::ExtensionEnableFlowFinished() {
1275 LaunchApp(extension_enable_flow_
->extension_id(),
1276 ash::LAUNCH_FROM_UNKNOWN
,
1278 extension_enable_flow_
.reset();
1281 void ChromeLauncherController::ExtensionEnableFlowAborted(bool user_initiated
) {
1282 extension_enable_flow_
.reset();
1285 ChromeLauncherAppMenuItems
ChromeLauncherController::GetApplicationList(
1286 const ash::LauncherItem
& item
,
1288 // Make sure that there is a controller associated with the id and that the
1289 // extension itself is a valid application and not a panel.
1290 if (!HasItemController(item
.id
) ||
1291 !GetLauncherIDForAppID(id_to_item_controller_map_
[item
.id
]->app_id()))
1292 return ChromeLauncherAppMenuItems().Pass();
1294 return id_to_item_controller_map_
[item
.id
]->GetApplicationList(event_flags
);
1297 std::vector
<content::WebContents
*>
1298 ChromeLauncherController::GetV1ApplicationsFromAppId(std::string app_id
) {
1299 ash::LauncherID id
= GetLauncherIDForAppID(app_id
);
1301 // If there is no such an item pinned to the launcher, no menu gets created.
1303 LauncherItemController
* controller
= id_to_item_controller_map_
[id
];
1305 if (controller
->type() == LauncherItemController::TYPE_SHORTCUT
)
1306 return GetV1ApplicationsFromController(controller
);
1308 return std::vector
<content::WebContents
*>();
1311 void ChromeLauncherController::ActivateShellApp(const std::string
& app_id
,
1313 ash::LauncherID id
= GetLauncherIDForAppID(app_id
);
1315 LauncherItemController
* controller
= id_to_item_controller_map_
[id
];
1316 if (controller
->type() == LauncherItemController::TYPE_APP
) {
1317 ShellWindowLauncherItemController
* shell_window_controller
=
1318 static_cast<ShellWindowLauncherItemController
*>(controller
);
1319 shell_window_controller
->ActivateIndexedApp(index
);
1324 bool ChromeLauncherController::IsWebContentHandledByApplication(
1325 content::WebContents
* web_contents
,
1326 const std::string
& app_id
) {
1327 if ((web_contents_to_app_id_
.find(web_contents
) !=
1328 web_contents_to_app_id_
.end()) &&
1329 (web_contents_to_app_id_
[web_contents
] == app_id
))
1331 return (app_id
== kGmailAppId
&& ContentCanBeHandledByGmailApp(web_contents
));
1334 bool ChromeLauncherController::ContentCanBeHandledByGmailApp(
1335 content::WebContents
* web_contents
) {
1336 ash::LauncherID id
= GetLauncherIDForAppID(kGmailAppId
);
1338 const GURL url
= web_contents
->GetURL();
1339 // We need to extend the application matching for the gMail app beyond the
1340 // manifest file's specification. This is required because of the namespace
1341 // overlap with the offline app ("/mail/mu/").
1342 if (!MatchPattern(url
.path(), "/mail/mu/*") &&
1343 MatchPattern(url
.path(), "/mail/*") &&
1344 GetExtensionForAppID(kGmailAppId
) &&
1345 GetExtensionForAppID(kGmailAppId
)->OverlapsWithOrigin(url
))
1351 gfx::Image
ChromeLauncherController::GetAppListIcon(
1352 content::WebContents
* web_contents
) const {
1353 ResourceBundle
& rb
= ResourceBundle::GetSharedInstance();
1354 if (IsIncognito(web_contents
))
1355 return rb
.GetImageNamed(IDR_AURA_LAUNCHER_LIST_INCOGNITO_BROWSER
);
1356 FaviconTabHelper
* favicon_tab_helper
=
1357 FaviconTabHelper::FromWebContents(web_contents
);
1358 gfx::Image result
= favicon_tab_helper
->GetFavicon();
1359 if (result
.IsEmpty())
1360 return rb
.GetImageNamed(IDR_DEFAULT_FAVICON
);
1364 base::string16
ChromeLauncherController::GetAppListTitle(
1365 content::WebContents
* web_contents
) const {
1366 base::string16 title
= web_contents
->GetTitle();
1369 WebContentsToAppIDMap::const_iterator iter
=
1370 web_contents_to_app_id_
.find(web_contents
);
1371 if (iter
!= web_contents_to_app_id_
.end()) {
1372 std::string app_id
= iter
->second
;
1373 const extensions::Extension
* extension
= GetExtensionForAppID(app_id
);
1375 return base::UTF8ToUTF16(extension
->name());
1377 return l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE
);
1380 ash::LauncherID
ChromeLauncherController::CreateAppShortcutLauncherItem(
1381 const std::string
& app_id
,
1383 return CreateAppShortcutLauncherItemWithType(app_id
,
1385 ash::TYPE_APP_SHORTCUT
);
1388 void ChromeLauncherController::SetAppTabHelperForTest(AppTabHelper
* helper
) {
1389 app_tab_helper_
.reset(helper
);
1392 void ChromeLauncherController::SetAppIconLoaderForTest(
1393 extensions::AppIconLoader
* loader
) {
1394 app_icon_loader_
.reset(loader
);
1397 const std::string
& ChromeLauncherController::GetAppIdFromLauncherIdForTest(
1398 ash::LauncherID id
) {
1399 return id_to_item_controller_map_
[id
]->app_id();
1402 void ChromeLauncherController::SetShelfItemDelegateManagerForTest(
1403 ash::ShelfItemDelegateManager
* manager
) {
1404 item_delegate_manager_
= manager
;
1407 void ChromeLauncherController::RememberUnpinnedRunningApplicationOrder() {
1408 RunningAppListIds list
;
1409 for (int i
= 0; i
< model_
->item_count(); i
++) {
1410 ash::LauncherItemType type
= model_
->items()[i
].type
;
1411 if (type
== ash::TYPE_WINDOWED_APP
|| type
== ash::TYPE_PLATFORM_APP
)
1412 list
.push_back(GetAppIDForLauncherID(model_
->items()[i
].id
));
1414 last_used_running_application_order_
[
1415 multi_user_util::GetUserIDFromProfile(profile_
)] = list
;
1418 void ChromeLauncherController::RestoreUnpinnedRunningApplicationOrder(
1419 const std::string
& user_id
) {
1420 const RunningAppListIdMap::iterator app_id_list
=
1421 last_used_running_application_order_
.find(user_id
);
1422 if (app_id_list
== last_used_running_application_order_
.end())
1425 // Find the first insertion point for running applications.
1426 int running_index
= model_
->FirstRunningAppIndex();
1427 for (RunningAppListIds::iterator app_id
= app_id_list
->second
.begin();
1428 app_id
!= app_id_list
->second
.end(); ++app_id
) {
1429 ash::LauncherID launcher_id
= GetLauncherIDForAppID(*app_id
);
1431 int app_index
= model_
->ItemIndexByID(launcher_id
);
1432 DCHECK_GE(app_index
, 0);
1433 ash::LauncherItemType type
= model_
->items()[app_index
].type
;
1434 if (type
== ash::TYPE_WINDOWED_APP
|| type
== ash::TYPE_PLATFORM_APP
) {
1435 if (running_index
!= app_index
)
1436 model_
->Move(running_index
, app_index
);
1443 ash::LauncherID
ChromeLauncherController::CreateAppShortcutLauncherItemWithType(
1444 const std::string
& app_id
,
1446 ash::LauncherItemType launcher_item_type
) {
1447 AppShortcutLauncherItemController
* controller
=
1448 new AppShortcutLauncherItemController(app_id
, this);
1449 ash::LauncherID launcher_id
= InsertAppLauncherItem(
1450 controller
, app_id
, ash::STATUS_CLOSED
, index
, launcher_item_type
);
1454 LauncherItemController
* ChromeLauncherController::GetLauncherItemController(
1455 const ash::LauncherID id
) {
1456 if (!HasItemController(id
))
1458 return id_to_item_controller_map_
[id
];
1461 bool ChromeLauncherController::IsBrowserFromActiveUser(Browser
* browser
) {
1462 // If running multi user mode with separate desktops, we have to check if the
1463 // browser is from the active user.
1464 if (chrome::MultiUserWindowManager::GetMultiProfileMode() !=
1465 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED
)
1467 return multi_user_util::IsProfileFromActiveUser(browser
->profile());
1470 void ChromeLauncherController::LauncherItemClosed(ash::LauncherID id
) {
1471 IDToItemControllerMap::iterator iter
= id_to_item_controller_map_
.find(id
);
1472 CHECK(iter
!= id_to_item_controller_map_
.end());
1473 CHECK(iter
->second
);
1474 app_icon_loader_
->ClearImage(iter
->second
->app_id());
1475 id_to_item_controller_map_
.erase(iter
);
1476 int index
= model_
->ItemIndexByID(id
);
1477 // A "browser proxy" is not known to the model and this removal does
1478 // therefore not need to be propagated to the model.
1480 model_
->RemoveItemAt(index
);
1483 void ChromeLauncherController::DoPinAppWithID(const std::string
& app_id
) {
1484 // If there is an item, do nothing and return.
1485 if (IsAppPinned(app_id
))
1488 ash::LauncherID launcher_id
= GetLauncherIDForAppID(app_id
);
1490 // App item exists, pin it
1493 // Otherwise, create a shortcut item for it.
1494 CreateAppShortcutLauncherItem(app_id
, model_
->item_count());
1496 PersistPinnedState();
1500 void ChromeLauncherController::DoUnpinAppWithID(const std::string
& app_id
) {
1501 ash::LauncherID launcher_id
= GetLauncherIDForAppID(app_id
);
1502 if (launcher_id
&& IsPinned(launcher_id
))
1506 int ChromeLauncherController::PinRunningAppInternal(
1508 ash::LauncherID launcher_id
) {
1509 int running_index
= model_
->ItemIndexByID(launcher_id
);
1510 ash::LauncherItem item
= model_
->items()[running_index
];
1511 DCHECK(item
.type
== ash::TYPE_WINDOWED_APP
||
1512 item
.type
== ash::TYPE_PLATFORM_APP
);
1513 item
.type
= ash::TYPE_APP_SHORTCUT
;
1514 model_
->Set(running_index
, item
);
1515 // The |ShelfModel|'s weight system might reposition the item to a
1516 // new index, so we get the index again.
1517 running_index
= model_
->ItemIndexByID(launcher_id
);
1518 if (running_index
< index
)
1520 if (running_index
!= index
)
1521 model_
->Move(running_index
, index
);
1525 void ChromeLauncherController::UnpinRunningAppInternal(int index
) {
1526 DCHECK_GE(index
, 0);
1527 ash::LauncherItem item
= model_
->items()[index
];
1528 DCHECK_EQ(item
.type
, ash::TYPE_APP_SHORTCUT
);
1529 item
.type
= ash::TYPE_WINDOWED_APP
;
1530 // A platform app and a windowed app are sharing TYPE_APP_SHORTCUT. As such
1531 // we have to check here what this was before it got a shortcut.
1532 if (HasItemController(item
.id
) &&
1533 id_to_item_controller_map_
[item
.id
]->type() ==
1534 LauncherItemController::TYPE_APP
)
1535 item
.type
= ash::TYPE_PLATFORM_APP
;
1536 model_
->Set(index
, item
);
1539 void ChromeLauncherController::UpdateAppLaunchersFromPref() {
1540 // There are various functions which will trigger a |PersistPinnedState| call
1541 // like a direct call to |DoPinAppWithID|, or an indirect call to the menu
1542 // model which will use weights to re-arrange the icons to new positions.
1543 // Since this function is meant to synchronize the "is state" with the
1544 // "sync state", it makes no sense to store any changes by this function back
1545 // into the pref state. Therefore we tell |persistPinnedState| to ignore any
1546 // invocations while we are running.
1547 base::AutoReset
<bool> auto_reset(&ignore_persist_pinned_state_change_
, true);
1548 std::vector
<std::string
> pinned_apps
= GetListOfPinnedAppsAndBrowser();
1551 int max_index
= model_
->item_count();
1553 // When one of the two special items cannot be moved (and we do not know where
1554 // yet), we remember the current location in one of these variables.
1555 int chrome_index
= -1;
1556 int app_list_index
= -1;
1558 // Walk the model and |pinned_apps| from the pref lockstep, adding and
1559 // removing items as necessary. NB: This code uses plain old indexing instead
1560 // of iterators because of model mutations as part of the loop.
1561 std::vector
<std::string
>::const_iterator
pref_app_id(pinned_apps
.begin());
1562 for (; index
< max_index
&& pref_app_id
!= pinned_apps
.end(); ++index
) {
1563 // Check if we have an item which we need to handle.
1564 if (*pref_app_id
== extension_misc::kChromeAppId
||
1565 *pref_app_id
== kAppLauncherIdPlaceholder
||
1566 IsAppPinned(*pref_app_id
)) {
1567 for (; index
< max_index
; ++index
) {
1568 const ash::LauncherItem
& item(model_
->items()[index
]);
1569 bool is_app_list
= item
.type
== ash::TYPE_APP_LIST
;
1570 bool is_chrome
= item
.type
== ash::TYPE_BROWSER_SHORTCUT
;
1571 if (item
.type
!= ash::TYPE_APP_SHORTCUT
&& !is_app_list
&& !is_chrome
)
1573 IDToItemControllerMap::const_iterator entry
=
1574 id_to_item_controller_map_
.find(item
.id
);
1575 if ((kAppLauncherIdPlaceholder
== *pref_app_id
&& is_app_list
) ||
1576 (extension_misc::kChromeAppId
== *pref_app_id
&& is_chrome
) ||
1577 (entry
!= id_to_item_controller_map_
.end() &&
1578 entry
->second
->app_id() == *pref_app_id
)) {
1579 // Check if an item needs to be moved here.
1580 MoveChromeOrApplistToFinalPosition(
1581 is_chrome
, is_app_list
, index
, &chrome_index
, &app_list_index
);
1585 if (is_chrome
|| is_app_list
) {
1586 // We cannot delete any of these shortcuts. As such we remember
1587 // their positions and move them later where they belong.
1589 chrome_index
= index
;
1591 app_list_index
= index
;
1592 // And skip the item - or exit the loop if end is reached (note that
1593 // in that case we will reduce the index again by one and this only
1594 // compensates for it).
1595 if (index
>= max_index
- 1)
1599 // Check if this is a platform or a windowed app.
1600 if (item
.type
== ash::TYPE_APP_SHORTCUT
&&
1601 (id_to_item_controller_map_
[item
.id
]->locked() ||
1602 id_to_item_controller_map_
[item
.id
]->type() ==
1603 LauncherItemController::TYPE_APP
)) {
1604 // Note: This will not change the amount of items (|max_index|).
1605 // Even changes to the actual |index| due to item weighting
1606 // changes should be fine.
1607 UnpinRunningAppInternal(index
);
1609 LauncherItemClosed(item
.id
);
1616 // If the item wasn't found, that means id_to_item_controller_map_
1618 DCHECK(index
<= max_index
);
1620 // Check if the item was already running but not yet pinned.
1621 ash::LauncherID launcher_id
= GetLauncherIDForAppID(*pref_app_id
);
1623 // This app is running but not yet pinned. So pin and move it.
1624 index
= PinRunningAppInternal(index
, launcher_id
);
1626 // This app wasn't pinned before, insert a new entry.
1627 launcher_id
= CreateAppShortcutLauncherItem(*pref_app_id
, index
);
1629 index
= model_
->ItemIndexByID(launcher_id
);
1635 // Remove any trailing existing items.
1636 while (index
< model_
->item_count()) {
1637 const ash::LauncherItem
& item(model_
->items()[index
]);
1638 if (item
.type
== ash::TYPE_APP_SHORTCUT
) {
1639 if (id_to_item_controller_map_
[item
.id
]->locked() ||
1640 id_to_item_controller_map_
[item
.id
]->type() ==
1641 LauncherItemController::TYPE_APP
)
1642 UnpinRunningAppInternal(index
);
1644 LauncherItemClosed(item
.id
);
1646 if (item
.type
== ash::TYPE_BROWSER_SHORTCUT
)
1647 chrome_index
= index
;
1648 else if (item
.type
== ash::TYPE_APP_LIST
)
1649 app_list_index
= index
;
1654 // Append unprocessed items from the pref to the end of the model.
1655 for (; pref_app_id
!= pinned_apps
.end(); ++pref_app_id
) {
1656 // All items but the chrome and / or app list shortcut needs to be added.
1657 bool is_chrome
= *pref_app_id
== extension_misc::kChromeAppId
;
1658 bool is_app_list
= *pref_app_id
== kAppLauncherIdPlaceholder
;
1659 // Coming here we know the next item which can be finalized, either the
1660 // chrome item or the app launcher. The final position is the end of the
1661 // list. The menu model will make sure that the item is grouped according
1662 // to its weight (which we do not know here).
1663 if (!is_chrome
&& !is_app_list
) {
1664 DoPinAppWithID(*pref_app_id
);
1665 int target_index
= FindInsertionPoint(false);
1666 ash::LauncherID id
= GetLauncherIDForAppID(*pref_app_id
);
1667 int source_index
= model_
->ItemIndexByID(id
);
1668 if (source_index
!= target_index
)
1669 model_
->Move(source_index
, target_index
);
1671 // Needed for the old layout - the weight might force it to be lower in
1673 if (app_list_index
!= -1 && target_index
<= app_list_index
)
1676 int target_index
= FindInsertionPoint(is_app_list
);
1677 MoveChromeOrApplistToFinalPosition(
1678 is_chrome
, is_app_list
, target_index
, &chrome_index
, &app_list_index
);
1683 void ChromeLauncherController::SetShelfAutoHideBehaviorPrefs(
1684 ash::ShelfAutoHideBehavior behavior
,
1685 aura::Window
* root_window
) {
1686 const char* value
= NULL
;
1688 case ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS
:
1689 value
= ash::kShelfAutoHideBehaviorAlways
;
1691 case ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER
:
1692 value
= ash::kShelfAutoHideBehaviorNever
;
1694 case ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN
:
1695 // This one should not be a valid preference option for now. We only want
1696 // to completely hide it when we run app mode.
1701 UpdatePerDisplayPref(
1702 profile_
->GetPrefs(), root_window
, prefs::kShelfAutoHideBehavior
, value
);
1704 if (root_window
== ash::Shell::GetPrimaryRootWindow()) {
1705 // See comment in |kShelfAlignment| about why we have two prefs here.
1706 profile_
->GetPrefs()->SetString(prefs::kShelfAutoHideBehaviorLocal
, value
);
1707 profile_
->GetPrefs()->SetString(prefs::kShelfAutoHideBehavior
, value
);
1711 void ChromeLauncherController::SetShelfAutoHideBehaviorFromPrefs() {
1712 aura::Window::Windows root_windows
= ash::Shell::GetAllRootWindows();
1714 for (aura::Window::Windows::const_iterator iter
= root_windows
.begin();
1715 iter
!= root_windows
.end(); ++iter
) {
1716 ash::Shell::GetInstance()->SetShelfAutoHideBehavior(
1717 GetShelfAutoHideBehavior(*iter
), *iter
);
1721 void ChromeLauncherController::SetShelfAlignmentFromPrefs() {
1722 if (!ash::ShelfWidget::ShelfAlignmentAllowed())
1725 aura::Window::Windows root_windows
= ash::Shell::GetAllRootWindows();
1727 for (aura::Window::Windows::const_iterator iter
= root_windows
.begin();
1728 iter
!= root_windows
.end(); ++iter
) {
1729 // See comment in |kShelfAlignment| as to why we consider two prefs.
1730 const std::string
alignment_value(
1731 GetPrefForRootWindow(profile_
->GetPrefs(),
1733 prefs::kShelfAlignmentLocal
,
1734 prefs::kShelfAlignment
));
1735 ash::ShelfAlignment alignment
= ash::SHELF_ALIGNMENT_BOTTOM
;
1736 if (alignment_value
== ash::kShelfAlignmentLeft
)
1737 alignment
= ash::SHELF_ALIGNMENT_LEFT
;
1738 else if (alignment_value
== ash::kShelfAlignmentRight
)
1739 alignment
= ash::SHELF_ALIGNMENT_RIGHT
;
1740 else if (alignment_value
== ash::kShelfAlignmentTop
)
1741 alignment
= ash::SHELF_ALIGNMENT_TOP
;
1742 ash::Shell::GetInstance()->SetShelfAlignment(alignment
, *iter
);
1746 void ChromeLauncherController::SetShelfBehaviorsFromPrefs() {
1747 SetShelfAutoHideBehaviorFromPrefs();
1748 SetShelfAlignmentFromPrefs();
1751 WebContents
* ChromeLauncherController::GetLastActiveWebContents(
1752 const std::string
& app_id
) {
1753 AppIDToWebContentsListMap::const_iterator i
=
1754 app_id_to_web_contents_list_
.find(app_id
);
1755 if (i
== app_id_to_web_contents_list_
.end())
1757 DCHECK_GT(i
->second
.size(), 0u);
1758 return *i
->second
.begin();
1761 ash::LauncherID
ChromeLauncherController::InsertAppLauncherItem(
1762 LauncherItemController
* controller
,
1763 const std::string
& app_id
,
1764 ash::LauncherItemStatus status
,
1766 ash::LauncherItemType launcher_item_type
) {
1767 ash::LauncherID id
= model_
->next_id();
1768 CHECK(!HasItemController(id
));
1770 id_to_item_controller_map_
[id
] = controller
;
1771 controller
->set_launcher_id(id
);
1773 ash::LauncherItem item
;
1774 item
.type
= launcher_item_type
;
1775 item
.image
= extensions::IconsInfo::GetDefaultAppIcon();
1777 WebContents
* active_tab
= GetLastActiveWebContents(app_id
);
1779 Browser
* browser
= chrome::FindBrowserWithWebContents(active_tab
);
1781 if (browser
->window()->IsActive())
1782 status
= ash::STATUS_ACTIVE
;
1784 status
= ash::STATUS_RUNNING
;
1786 item
.status
= status
;
1788 model_
->AddAt(index
, item
);
1790 app_icon_loader_
->FetchImage(app_id
);
1792 SetShelfItemDelegate(id
, controller
);
1797 bool ChromeLauncherController::HasItemController(ash::LauncherID id
) const {
1798 return id_to_item_controller_map_
.find(id
) !=
1799 id_to_item_controller_map_
.end();
1802 std::vector
<content::WebContents
*>
1803 ChromeLauncherController::GetV1ApplicationsFromController(
1804 LauncherItemController
* controller
) {
1805 DCHECK(controller
->type() == LauncherItemController::TYPE_SHORTCUT
);
1806 AppShortcutLauncherItemController
* app_controller
=
1807 static_cast<AppShortcutLauncherItemController
*>(controller
);
1808 return app_controller
->GetRunningApplications();
1811 BrowserShortcutLauncherItemController
*
1812 ChromeLauncherController::GetBrowserShortcutLauncherItemController() {
1813 for (IDToItemControllerMap::iterator i
= id_to_item_controller_map_
.begin();
1814 i
!= id_to_item_controller_map_
.end(); ++i
) {
1815 int index
= model_
->ItemIndexByID(i
->first
);
1816 const ash::LauncherItem
& item
= model_
->items()[index
];
1817 if (item
.type
== ash::TYPE_BROWSER_SHORTCUT
)
1818 return static_cast<BrowserShortcutLauncherItemController
*>(i
->second
);
1820 // Create a LauncherItemController for the Browser shortcut if it does not
1822 ash::LauncherID id
= CreateBrowserShortcutLauncherItem();
1823 DCHECK(id_to_item_controller_map_
[id
]);
1824 return static_cast<BrowserShortcutLauncherItemController
*>(
1825 id_to_item_controller_map_
[id
]);
1828 ash::LauncherID
ChromeLauncherController::CreateBrowserShortcutLauncherItem() {
1829 ash::LauncherItem browser_shortcut
;
1830 browser_shortcut
.type
= ash::TYPE_BROWSER_SHORTCUT
;
1831 ResourceBundle
& rb
= ResourceBundle::GetSharedInstance();
1832 browser_shortcut
.image
= *rb
.GetImageSkiaNamed(IDR_PRODUCT_LOGO_32
);
1833 ash::LauncherID id
= model_
->next_id();
1834 size_t index
= GetChromeIconIndexForCreation();
1835 model_
->AddAt(index
, browser_shortcut
);
1836 id_to_item_controller_map_
[id
] =
1837 new BrowserShortcutLauncherItemController(this);
1838 id_to_item_controller_map_
[id
]->set_launcher_id(id
);
1839 // ShelfItemDelegateManager owns BrowserShortcutLauncherItemController.
1840 SetShelfItemDelegate(id
, id_to_item_controller_map_
[id
]);
1844 void ChromeLauncherController::PersistChromeItemIndex(int index
) {
1845 profile_
->GetPrefs()->SetInteger(prefs::kShelfChromeIconIndex
, index
);
1848 int ChromeLauncherController::GetChromeIconIndexFromPref() const {
1849 size_t index
= profile_
->GetPrefs()->GetInteger(prefs::kShelfChromeIconIndex
);
1850 const base::ListValue
* pinned_apps_pref
=
1851 profile_
->GetPrefs()->GetList(prefs::kPinnedLauncherApps
);
1852 return std::max(static_cast<size_t>(0),
1853 std::min(pinned_apps_pref
->GetSize(), index
));
1856 void ChromeLauncherController::MoveChromeOrApplistToFinalPosition(
1861 int* app_list_index
) {
1862 if (is_chrome
&& *chrome_index
!= -1) {
1863 model_
->Move(*chrome_index
, target_index
);
1864 if (*app_list_index
!= -1 &&
1865 *chrome_index
< *app_list_index
&&
1866 target_index
> *app_list_index
)
1867 --(*app_list_index
);
1869 } else if (is_app_list
&& *app_list_index
!= -1) {
1870 model_
->Move(*app_list_index
, target_index
);
1871 if (*chrome_index
!= -1 &&
1872 *app_list_index
< *chrome_index
&&
1873 target_index
> *chrome_index
)
1875 *app_list_index
= -1;
1879 int ChromeLauncherController::FindInsertionPoint(bool is_app_list
) {
1880 bool alternate
= ash::switches::UseAlternateShelfLayout();
1881 // Keeping this change small to backport to M33&32 (see crbug.com/329597).
1882 // TODO(skuhne): With the removal of the legacy shelf layout we should remove
1883 // the ability to move the app list item since this was never used. We should
1884 // instead ask the ShelfModel::ValidateInsertionIndex or similir for an index.
1885 if (is_app_list
&& alternate
)
1888 for (int i
= model_
->item_count() - 1; i
> 0; --i
) {
1889 ash::LauncherItemType type
= model_
->items()[i
].type
;
1890 if (type
== ash::TYPE_APP_SHORTCUT
||
1891 ((is_app_list
|| alternate
) && type
== ash::TYPE_APP_LIST
) ||
1892 type
== ash::TYPE_BROWSER_SHORTCUT
||
1893 type
== ash::TYPE_WINDOWED_APP
)
1899 int ChromeLauncherController::GetChromeIconIndexForCreation() {
1900 // We get the list of pinned apps as they currently would get pinned.
1901 // Within this list the chrome icon will be the correct location.
1902 std::vector
<std::string
> pinned_apps
= GetListOfPinnedAppsAndBrowser();
1904 std::vector
<std::string
>::iterator it
=
1905 std::find(pinned_apps
.begin(),
1907 std::string(extension_misc::kChromeAppId
));
1908 DCHECK(it
!= pinned_apps
.end());
1909 int index
= it
- pinned_apps
.begin();
1911 // We should do here a comparison between the is state and the "want to be"
1912 // state since some apps might be able to pin but are not yet. Instead - for
1913 // the time being we clamp against the amount of known items and wait for the
1914 // next |UpdateAppLaunchersFromPref()| call to correct it - it will come since
1915 // the pinning will be done then.
1916 return std::min(model_
->item_count(), index
);
1919 std::vector
<std::string
>
1920 ChromeLauncherController::GetListOfPinnedAppsAndBrowser() {
1921 // Adding the app list item to the list of items requires that the ID is not
1922 // a valid and known ID for the extension system. The ID was constructed that
1923 // way - but just to make sure...
1924 DCHECK(!app_tab_helper_
->IsValidIDForCurrentUser(kAppLauncherIdPlaceholder
));
1926 std::vector
<std::string
> pinned_apps
;
1928 // Get the new incarnation of the list.
1929 const base::ListValue
* pinned_apps_pref
=
1930 profile_
->GetPrefs()->GetList(prefs::kPinnedLauncherApps
);
1932 // Keep track of the addition of the chrome and the app list icon.
1933 bool chrome_icon_added
= false;
1934 bool app_list_icon_added
= false;
1935 size_t chrome_icon_index
= GetChromeIconIndexFromPref();
1937 // See if the chrome string is already in the pinned list and remove it if
1939 base::Value
* chrome_app
= ash::CreateAppDict(extension_misc::kChromeAppId
);
1941 chrome_icon_added
= pinned_apps_pref
->Find(*chrome_app
) !=
1942 pinned_apps_pref
->end();
1946 for (size_t index
= 0; index
< pinned_apps_pref
->GetSize(); ++index
) {
1947 // We need to position the chrome icon relative to it's place in the pinned
1948 // preference list - even if an item of that list isn't shown yet.
1949 if (index
== chrome_icon_index
&& !chrome_icon_added
) {
1950 pinned_apps
.push_back(extension_misc::kChromeAppId
);
1951 chrome_icon_added
= true;
1953 const base::DictionaryValue
* app
= NULL
;
1955 if (pinned_apps_pref
->GetDictionary(index
, &app
) &&
1956 app
->GetString(ash::kPinnedAppsPrefAppIDPath
, &app_id
) &&
1957 (std::find(pinned_apps
.begin(), pinned_apps
.end(), app_id
) ==
1958 pinned_apps
.end())) {
1959 if (app_id
== extension_misc::kChromeAppId
) {
1960 chrome_icon_added
= true;
1961 pinned_apps
.push_back(extension_misc::kChromeAppId
);
1962 } else if (app_id
== kAppLauncherIdPlaceholder
) {
1963 app_list_icon_added
= true;
1964 pinned_apps
.push_back(kAppLauncherIdPlaceholder
);
1965 } else if (app_tab_helper_
->IsValidIDForCurrentUser(app_id
)) {
1966 // Note: In multi profile scenarios we only want to show pinnable apps
1967 // here which is correct. Running applications from the other users will
1968 // continue to run. So no need for multi profile modifications.
1969 pinned_apps
.push_back(app_id
);
1974 // If not added yet, the chrome item will be the last item in the list.
1975 if (!chrome_icon_added
)
1976 pinned_apps
.push_back(extension_misc::kChromeAppId
);
1978 // If not added yet, add the app list item either at the end or at the
1979 // beginning - depending on the shelf layout.
1980 if (!app_list_icon_added
) {
1981 if (ash::switches::UseAlternateShelfLayout())
1982 pinned_apps
.insert(pinned_apps
.begin(), kAppLauncherIdPlaceholder
);
1984 pinned_apps
.push_back(kAppLauncherIdPlaceholder
);
1989 bool ChromeLauncherController::IsIncognito(
1990 const content::WebContents
* web_contents
) const {
1991 const Profile
* profile
=
1992 Profile::FromBrowserContext(web_contents
->GetBrowserContext());
1993 return profile
->IsOffTheRecord() && !profile
->IsGuestSession();
1996 void ChromeLauncherController::CloseWindowedAppsFromRemovedExtension(
1997 const std::string
& app_id
) {
1998 // This function cannot rely on the controller's enumeration functionality
1999 // since the extension has already be unloaded.
2000 const BrowserList
* ash_browser_list
=
2001 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH
);
2002 std::vector
<Browser
*> browser_to_close
;
2003 for (BrowserList::const_reverse_iterator
2004 it
= ash_browser_list
->begin_last_active();
2005 it
!= ash_browser_list
->end_last_active(); ++it
) {
2006 Browser
* browser
= *it
;
2007 if (!browser
->is_type_tabbed() &&
2008 browser
->is_type_popup() &&
2009 browser
->is_app() &&
2010 app_id
== web_app::GetExtensionIdFromApplicationName(
2011 browser
->app_name())) {
2012 browser_to_close
.push_back(browser
);
2015 while (!browser_to_close
.empty()) {
2016 TabStripModel
* tab_strip
= browser_to_close
.back()->tab_strip_model();
2017 tab_strip
->CloseWebContentsAt(0, TabStripModel::CLOSE_NONE
);
2018 browser_to_close
.pop_back();
2022 void ChromeLauncherController::SetShelfItemDelegate(
2024 ash::ShelfItemDelegate
* item_delegate
) {
2026 DCHECK(item_delegate
);
2027 DCHECK(item_delegate_manager_
);
2028 item_delegate_manager_
->SetShelfItemDelegate(
2029 id
, scoped_ptr
<ash::ShelfItemDelegate
>(item_delegate
).Pass());
2032 void ChromeLauncherController::AttachProfile(Profile
* profile
) {
2034 // Either add the profile to the list of known profiles and make it the active
2035 // one for some functions of AppTabHelper or create a new one.
2036 if (!app_tab_helper_
.get())
2037 app_tab_helper_
.reset(new LauncherAppTabHelper(profile_
));
2039 app_tab_helper_
->SetCurrentUser(profile_
);
2040 // TODO(skuhne): The AppIconLoaderImpl has the same problem. Each loaded
2041 // image is associated with a profile (it's loader requires the profile).
2042 // Since icon size changes are possible, the icon could be requested to be
2043 // reloaded. However - having it not multi profile aware would cause problems
2044 // if the icon cache gets deleted upon user switch.
2045 app_icon_loader_
.reset(new extensions::AppIconLoaderImpl(
2046 profile_
, extension_misc::EXTENSION_ICON_SMALL
, this));
2048 pref_change_registrar_
.Init(profile_
->GetPrefs());
2049 pref_change_registrar_
.Add(
2050 prefs::kPinnedLauncherApps
,
2051 base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref
,
2052 base::Unretained(this)));
2053 pref_change_registrar_
.Add(
2054 prefs::kShelfAlignmentLocal
,
2055 base::Bind(&ChromeLauncherController::SetShelfAlignmentFromPrefs
,
2056 base::Unretained(this)));
2057 pref_change_registrar_
.Add(
2058 prefs::kShelfAutoHideBehaviorLocal
,
2059 base::Bind(&ChromeLauncherController::
2060 SetShelfAutoHideBehaviorFromPrefs
,
2061 base::Unretained(this)));
2062 pref_change_registrar_
.Add(
2063 prefs::kShelfPreferences
,
2064 base::Bind(&ChromeLauncherController::SetShelfBehaviorsFromPrefs
,
2065 base::Unretained(this)));
2068 void ChromeLauncherController::ReleaseProfile() {
2069 if (app_sync_ui_state_
)
2070 app_sync_ui_state_
->RemoveObserver(this);
2072 PrefServiceSyncable::FromProfile(profile_
)->RemoveObserver(this);
2074 pref_change_registrar_
.RemoveAll();