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/pattern.h"
24 #include "base/strings/string_number_conversions.h"
25 #include "base/strings/string_util.h"
26 #include "base/strings/utf_string_conversions.h"
27 #include "base/values.h"
28 #include "chrome/browser/app_mode/app_mode_utils.h"
29 #include "chrome/browser/chrome_notification_types.h"
30 #include "chrome/browser/defaults.h"
31 #include "chrome/browser/extensions/app_icon_loader_impl.h"
32 #include "chrome/browser/extensions/extension_util.h"
33 #include "chrome/browser/extensions/launch_util.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/app_launch_params.h"
60 #include "chrome/browser/ui/extensions/application_launch.h"
61 #include "chrome/browser/ui/extensions/extension_enable_flow.h"
62 #include "chrome/browser/ui/host_desktop.h"
63 #include "chrome/browser/ui/tabs/tab_strip_model.h"
64 #include "chrome/browser/web_applications/web_app.h"
65 #include "chrome/common/chrome_switches.h"
66 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
67 #include "chrome/common/pref_names.h"
68 #include "chrome/common/url_constants.h"
69 #include "chrome/grit/generated_resources.h"
70 #include "components/favicon/content/content_favicon_driver.h"
71 #include "content/public/browser/navigation_entry.h"
72 #include "content/public/browser/web_contents.h"
73 #include "extensions/browser/extension_prefs.h"
74 #include "extensions/browser/extension_registry.h"
75 #include "extensions/browser/extension_system.h"
76 #include "extensions/browser/extension_util.h"
77 #include "extensions/common/constants.h"
78 #include "extensions/common/extension.h"
79 #include "extensions/common/extension_resource.h"
80 #include "extensions/common/manifest_handlers/icons_handler.h"
81 #include "extensions/common/url_pattern.h"
82 #include "grit/ash_resources.h"
83 #include "grit/theme_resources.h"
84 #include "net/base/url_util.h"
85 #include "ui/aura/window.h"
86 #include "ui/aura/window_event_dispatcher.h"
87 #include "ui/base/l10n/l10n_util.h"
88 #include "ui/base/window_open_disposition.h"
89 #include "ui/keyboard/keyboard_util.h"
90 #include "ui/resources/grit/ui_resources.h"
91 #include "ui/wm/core/window_animations.h"
93 #if defined(OS_CHROMEOS)
94 #include "chrome/browser/browser_process.h"
95 #include "chrome/browser/ui/ash/chrome_shell_delegate.h"
96 #include "chrome/browser/ui/ash/launcher/multi_profile_app_window_launcher_controller.h"
97 #include "chrome/browser/ui/ash/launcher/multi_profile_browser_status_monitor.h"
98 #include "components/user_manager/user_manager.h"
101 using extensions::Extension
;
102 using extensions::UnloadedExtensionInfo
;
103 using extension_misc::kGmailAppId
;
104 using content::WebContents
;
107 ChromeLauncherController
* ChromeLauncherController::instance_
= NULL
;
111 // This will be used as placeholder in the list of the pinned applciatons.
112 // Note that this is NOT a valid extension identifier so that pre M31 versions
114 const char kAppShelfIdPlaceholder
[] = "AppShelfIDPlaceholder--------";
116 std::string
GetPrefKeyForRootWindow(aura::Window
* root_window
) {
117 gfx::Display display
= gfx::Screen::GetScreenFor(
118 root_window
)->GetDisplayNearestWindow(root_window
);
119 DCHECK(display
.is_valid());
121 return base::Int64ToString(display
.id());
124 void UpdatePerDisplayPref(PrefService
* pref_service
,
125 aura::Window
* root_window
,
126 const char* pref_key
,
127 const std::string
& value
) {
128 std::string key
= GetPrefKeyForRootWindow(root_window
);
132 DictionaryPrefUpdate
update(pref_service
, prefs::kShelfPreferences
);
133 base::DictionaryValue
* shelf_prefs
= update
.Get();
134 base::DictionaryValue
* prefs
= NULL
;
135 if (!shelf_prefs
->GetDictionary(key
, &prefs
)) {
136 prefs
= new base::DictionaryValue();
137 shelf_prefs
->Set(key
, prefs
);
139 prefs
->SetStringWithoutPathExpansion(pref_key
, value
);
142 // Returns a pref value in |pref_service| for the display of |root_window|. The
143 // pref value is stored in |local_path| and |path|, but |pref_service| may have
144 // per-display preferences and the value can be specified by policy. Here is
146 // * A value managed by policy. This is a single value that applies to all
148 // * A user-set value for the specified display.
149 // * A user-set value in |local_path| or |path|, if no per-display settings are
150 // ever specified (see http://crbug.com/173719 for why). |local_path| is
151 // preferred. See comment in |kShelfAlignment| as to why we consider two
152 // prefs and why |local_path| is preferred.
153 // * A value recommended by policy. This is a single value that applies to all
155 // * The default value for |local_path| if the value is not recommended by
157 std::string
GetPrefForRootWindow(PrefService
* pref_service
,
158 aura::Window
* root_window
,
159 const char* local_path
,
161 const PrefService::Preference
* local_pref
=
162 pref_service
->FindPreference(local_path
);
163 const std::string
value(pref_service
->GetString(local_path
));
164 if (local_pref
->IsManaged())
167 std::string pref_key
= GetPrefKeyForRootWindow(root_window
);
168 bool has_per_display_prefs
= false;
169 if (!pref_key
.empty()) {
170 const base::DictionaryValue
* shelf_prefs
= pref_service
->GetDictionary(
171 prefs::kShelfPreferences
);
172 const base::DictionaryValue
* display_pref
= NULL
;
173 std::string per_display_value
;
174 if (shelf_prefs
->GetDictionary(pref_key
, &display_pref
) &&
175 display_pref
->GetString(path
, &per_display_value
))
176 return per_display_value
;
178 // If the pref for the specified display is not found, scan the whole prefs
179 // and check if the prefs for other display is already specified.
180 std::string unused_value
;
181 for (base::DictionaryValue::Iterator
iter(*shelf_prefs
);
182 !iter
.IsAtEnd(); iter
.Advance()) {
183 const base::DictionaryValue
* display_pref
= NULL
;
184 if (iter
.value().GetAsDictionary(&display_pref
) &&
185 display_pref
->GetString(path
, &unused_value
)) {
186 has_per_display_prefs
= true;
192 if (local_pref
->IsRecommended() || !has_per_display_prefs
)
195 const base::Value
* default_value
=
196 pref_service
->GetDefaultPrefValue(local_path
);
197 std::string default_string
;
198 default_value
->GetAsString(&default_string
);
199 return default_string
;
202 // Gets the shelf auto hide behavior from prefs for a root window.
203 ash::ShelfAutoHideBehavior
GetShelfAutoHideBehaviorFromPrefs(
205 aura::Window
* root_window
) {
208 // Don't show the shelf in app mode.
209 if (chrome::IsRunningInAppMode())
210 return ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN
;
212 // See comment in |kShelfAlignment| as to why we consider two prefs.
213 const std::string
behavior_value(
214 GetPrefForRootWindow(profile
->GetPrefs(),
216 prefs::kShelfAutoHideBehaviorLocal
,
217 prefs::kShelfAutoHideBehavior
));
219 // Note: To maintain sync compatibility with old images of chrome/chromeos
220 // the set of values that may be encountered includes the now-extinct
221 // "Default" as well as "Never" and "Always", "Default" should now
222 // be treated as "Never" (http://crbug.com/146773).
223 if (behavior_value
== ash::kShelfAutoHideBehaviorAlways
)
224 return ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS
;
225 return ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER
;
228 // Gets the shelf alignment from prefs for a root window.
229 ash::ShelfAlignment
GetShelfAlignmentFromPrefs(Profile
* profile
,
230 aura::Window
* root_window
) {
233 // See comment in |kShelfAlignment| as to why we consider two prefs.
234 const std::string
alignment_value(
235 GetPrefForRootWindow(profile
->GetPrefs(),
237 prefs::kShelfAlignmentLocal
,
238 prefs::kShelfAlignment
));
239 if (alignment_value
== ash::kShelfAlignmentLeft
)
240 return ash::SHELF_ALIGNMENT_LEFT
;
241 else if (alignment_value
== ash::kShelfAlignmentRight
)
242 return ash::SHELF_ALIGNMENT_RIGHT
;
243 else if (alignment_value
== ash::kShelfAlignmentTop
)
244 return ash::SHELF_ALIGNMENT_TOP
;
245 return ash::SHELF_ALIGNMENT_BOTTOM
;
248 // If prefs have synced and no user-set value exists at |local_path|, the value
249 // from |synced_path| is copied to |local_path|.
250 void MaybePropagatePrefToLocal(PrefServiceSyncable
* pref_service
,
251 const char* local_path
,
252 const char* synced_path
) {
253 if (!pref_service
->FindPreference(local_path
)->HasUserSetting() &&
254 pref_service
->IsSyncing()) {
255 // First time the user is using this machine, propagate from remote to
257 pref_service
->SetString(local_path
, pref_service
->GetString(synced_path
));
261 std::string
GetSourceFromAppListSource(ash::LaunchSource source
) {
263 case ash::LAUNCH_FROM_APP_LIST
:
264 return std::string(extension_urls::kLaunchSourceAppList
);
265 case ash::LAUNCH_FROM_APP_LIST_SEARCH
:
266 return std::string(extension_urls::kLaunchSourceAppListSearch
);
267 default: return std::string();
273 #if defined(OS_CHROMEOS)
274 // A class to get events from ChromeOS when a user gets changed or added.
275 class ChromeLauncherControllerUserSwitchObserver
276 : public user_manager::UserManager::UserSessionStateObserver
{
278 ChromeLauncherControllerUserSwitchObserver(
279 ChromeLauncherController
* controller
)
280 : controller_(controller
) {
281 DCHECK(user_manager::UserManager::IsInitialized());
282 user_manager::UserManager::Get()->AddSessionStateObserver(this);
284 ~ChromeLauncherControllerUserSwitchObserver() override
{
285 user_manager::UserManager::Get()->RemoveSessionStateObserver(this);
288 // user_manager::UserManager::UserSessionStateObserver overrides:
289 void UserAddedToSession(const user_manager::User
* added_user
) override
;
291 // ChromeLauncherControllerUserSwitchObserver:
292 void OnUserProfileReadyToSwitch(Profile
* profile
);
295 // Add a user to the session.
296 void AddUser(Profile
* profile
);
298 // The owning ChromeLauncherController.
299 ChromeLauncherController
* controller_
;
301 // Users which were just added to the system, but which profiles were not yet
303 std::set
<std::string
> added_user_ids_waiting_for_profiles_
;
305 DISALLOW_COPY_AND_ASSIGN(ChromeLauncherControllerUserSwitchObserver
);
308 void ChromeLauncherControllerUserSwitchObserver::UserAddedToSession(
309 const user_manager::User
* active_user
) {
310 Profile
* profile
= multi_user_util::GetProfileFromUserID(
311 active_user
->email());
312 // If we do not have a profile yet, we postpone forwarding the notification
313 // until it is loaded.
315 added_user_ids_waiting_for_profiles_
.insert(active_user
->email());
320 void ChromeLauncherControllerUserSwitchObserver::OnUserProfileReadyToSwitch(
322 if (!added_user_ids_waiting_for_profiles_
.empty()) {
323 // Check if the profile is from a user which was on the waiting list.
324 std::string user_id
= multi_user_util::GetUserIDFromProfile(profile
);
325 std::set
<std::string
>::iterator it
= std::find(
326 added_user_ids_waiting_for_profiles_
.begin(),
327 added_user_ids_waiting_for_profiles_
.end(),
329 if (it
!= added_user_ids_waiting_for_profiles_
.end()) {
330 added_user_ids_waiting_for_profiles_
.erase(it
);
331 AddUser(profile
->GetOriginalProfile());
336 void ChromeLauncherControllerUserSwitchObserver::AddUser(Profile
* profile
) {
337 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
338 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED
)
339 chrome::MultiUserWindowManager::GetInstance()->AddUser(profile
);
340 controller_
->AdditionalUserAddedToSession(profile
->GetOriginalProfile());
344 ChromeLauncherController::ChromeLauncherController(Profile
* profile
,
345 ash::ShelfModel
* model
)
347 item_delegate_manager_(NULL
),
349 app_sync_ui_state_(NULL
),
350 ignore_persist_pinned_state_change_(false) {
352 // If no profile was passed, we take the currently active profile and use it
353 // as the owner of the current desktop.
354 // Use the original profile as on chromeos we may get a temporary off the
355 // record profile, unless in guest session (where off the record profile is
357 profile_
= ProfileManager::GetActiveUserProfile();
358 if (!profile_
->IsGuestSession() && !profile_
->IsSystemProfile())
359 profile_
= profile_
->GetOriginalProfile();
361 app_sync_ui_state_
= AppSyncUIState::Get(profile_
);
362 if (app_sync_ui_state_
)
363 app_sync_ui_state_
->AddObserver(this);
366 // All profile relevant settings get bound to the current profile.
367 AttachProfile(profile_
);
368 model_
->AddObserver(this);
370 // In multi profile mode we might have a window manager. We try to create it
371 // here. If the instantiation fails, the manager is not needed.
372 chrome::MultiUserWindowManager::CreateInstance();
374 #if defined(OS_CHROMEOS)
375 // On Chrome OS using multi profile we want to switch the content of the shelf
376 // with a user change. Note that for unit tests the instance can be NULL.
377 if (chrome::MultiUserWindowManager::GetMultiProfileMode() !=
378 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_OFF
) {
379 user_switch_observer_
.reset(
380 new ChromeLauncherControllerUserSwitchObserver(this));
383 // Create our v1/v2 application / browser monitors which will inform the
384 // launcher of status changes.
385 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
386 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED
) {
387 // If running in separated destkop mode, we create the multi profile version
388 // of status monitor.
389 browser_status_monitor_
.reset(new MultiProfileBrowserStatusMonitor(this));
390 app_window_controller_
.reset(
391 new MultiProfileAppWindowLauncherController(this));
393 // Create our v1/v2 application / browser monitors which will inform the
394 // launcher of status changes.
395 browser_status_monitor_
.reset(new BrowserStatusMonitor(this));
396 app_window_controller_
.reset(new AppWindowLauncherController(this));
399 // Create our v1/v2 application / browser monitors which will inform the
400 // launcher of status changes.
401 browser_status_monitor_
.reset(new BrowserStatusMonitor(this));
402 app_window_controller_
.reset(new AppWindowLauncherController(this));
405 // Right now ash::Shell isn't created for tests.
406 // TODO(mukai): Allows it to observe display change and write tests.
407 if (ash::Shell::HasInstance()) {
408 ash::Shell::GetInstance()->display_controller()->AddObserver(this);
409 // If it got already set, we remove the observer first again and swap the
410 // ItemDelegateManager.
411 if (item_delegate_manager_
)
412 item_delegate_manager_
->RemoveObserver(this);
413 item_delegate_manager_
=
414 ash::Shell::GetInstance()->shelf_item_delegate_manager();
415 item_delegate_manager_
->AddObserver(this);
419 ChromeLauncherController::~ChromeLauncherController() {
420 if (item_delegate_manager_
)
421 item_delegate_manager_
->RemoveObserver(this);
423 // Reset the BrowserStatusMonitor as it has a weak pointer to this.
424 browser_status_monitor_
.reset();
426 // Reset the app window controller here since it has a weak pointer to this.
427 app_window_controller_
.reset();
429 for (std::set
<ash::Shelf
*>::iterator iter
= shelves_
.begin();
430 iter
!= shelves_
.end();
432 (*iter
)->shelf_widget()->shelf_layout_manager()->RemoveObserver(this);
434 model_
->RemoveObserver(this);
435 if (ash::Shell::HasInstance())
436 ash::Shell::GetInstance()->display_controller()->RemoveObserver(this);
437 for (IDToItemControllerMap::iterator i
= id_to_item_controller_map_
.begin();
438 i
!= id_to_item_controller_map_
.end(); ++i
) {
439 int index
= model_
->ItemIndexByID(i
->first
);
440 // A "browser proxy" is not known to the model and this removal does
441 // therefore not need to be propagated to the model.
443 model_
->items()[index
].type
!= ash::TYPE_BROWSER_SHORTCUT
)
444 model_
->RemoveItemAt(index
);
447 if (ash::Shell::HasInstance())
448 ash::Shell::GetInstance()->RemoveShellObserver(this);
450 // Release all profile dependent resources.
452 if (instance_
== this)
455 // Get rid of the multi user window manager instance.
456 chrome::MultiUserWindowManager::DeleteInstance();
460 ChromeLauncherController
* ChromeLauncherController::CreateInstance(
462 ash::ShelfModel
* model
) {
463 // We do not check here for re-creation of the ChromeLauncherController since
464 // it appears that it might be intentional that the ChromeLauncherController
465 // can be re-created.
466 instance_
= new ChromeLauncherController(profile
, model
);
470 void ChromeLauncherController::Init() {
471 CreateBrowserShortcutLauncherItem();
472 UpdateAppLaunchersFromPref();
474 // TODO(sky): update unit test so that this test isn't necessary.
475 if (ash::Shell::HasInstance()) {
476 SetShelfAutoHideBehaviorFromPrefs();
477 SetShelfAlignmentFromPrefs();
478 #if defined(OS_CHROMEOS)
479 SetVirtualKeyboardBehaviorFromPrefs();
480 #endif // defined(OS_CHROMEOS)
481 PrefServiceSyncable
* prefs
= PrefServiceSyncable::FromProfile(profile_
);
482 if (!prefs
->FindPreference(prefs::kShelfAlignmentLocal
)->HasUserSetting() ||
483 !prefs
->FindPreference(prefs::kShelfAutoHideBehaviorLocal
)->
485 // This causes OnIsSyncingChanged to be called when the value of
486 // PrefService::IsSyncing() changes.
487 prefs
->AddObserver(this);
489 ash::Shell::GetInstance()->AddShellObserver(this);
493 ash::ShelfID
ChromeLauncherController::CreateAppLauncherItem(
494 LauncherItemController
* controller
,
495 const std::string
& app_id
,
496 ash::ShelfItemStatus status
) {
499 // Panels are inserted on the left so as not to push all existing panels over.
500 if (controller
->GetShelfItemType() != ash::TYPE_APP_PANEL
)
501 index
= model_
->item_count();
502 return InsertAppLauncherItem(controller
,
506 controller
->GetShelfItemType());
509 void ChromeLauncherController::SetItemStatus(ash::ShelfID id
,
510 ash::ShelfItemStatus status
) {
511 int index
= model_
->ItemIndexByID(id
);
512 ash::ShelfItemStatus old_status
= model_
->items()[index
].status
;
513 // Since ordinary browser windows are not registered, we might get a negative
515 if (index
>= 0 && old_status
!= status
) {
516 ash::ShelfItem item
= model_
->items()[index
];
517 item
.status
= status
;
518 model_
->Set(index
, item
);
522 void ChromeLauncherController::SetItemController(
524 LauncherItemController
* controller
) {
526 IDToItemControllerMap::iterator iter
= id_to_item_controller_map_
.find(id
);
527 CHECK(iter
!= id_to_item_controller_map_
.end());
528 controller
->set_shelf_id(id
);
529 iter
->second
= controller
;
530 // Existing controller is destroyed and replaced by registering again.
531 SetShelfItemDelegate(id
, controller
);
534 void ChromeLauncherController::CloseLauncherItem(ash::ShelfID id
) {
537 // Create a new shortcut controller.
538 IDToItemControllerMap::iterator iter
= id_to_item_controller_map_
.find(id
);
539 CHECK(iter
!= id_to_item_controller_map_
.end());
540 SetItemStatus(id
, ash::STATUS_CLOSED
);
541 std::string app_id
= iter
->second
->app_id();
542 iter
->second
= new AppShortcutLauncherItemController(app_id
, this);
543 iter
->second
->set_shelf_id(id
);
544 // Existing controller is destroyed and replaced by registering again.
545 SetShelfItemDelegate(id
, iter
->second
);
547 LauncherItemClosed(id
);
551 void ChromeLauncherController::Pin(ash::ShelfID id
) {
552 DCHECK(HasShelfIDToAppIDMapping(id
));
554 int index
= model_
->ItemIndexByID(id
);
557 ash::ShelfItem item
= model_
->items()[index
];
559 if (item
.type
== ash::TYPE_PLATFORM_APP
||
560 item
.type
== ash::TYPE_WINDOWED_APP
) {
561 item
.type
= ash::TYPE_APP_SHORTCUT
;
562 model_
->Set(index
, item
);
563 } else if (item
.type
!= ash::TYPE_APP_SHORTCUT
) {
568 PersistPinnedState();
571 void ChromeLauncherController::Unpin(ash::ShelfID id
) {
572 LauncherItemController
* controller
= GetLauncherItemController(id
);
575 if (controller
->type() == LauncherItemController::TYPE_APP
||
576 controller
->locked()) {
577 UnpinRunningAppInternal(model_
->ItemIndexByID(id
));
579 LauncherItemClosed(id
);
582 PersistPinnedState();
585 bool ChromeLauncherController::IsPinned(ash::ShelfID id
) {
586 int index
= model_
->ItemIndexByID(id
);
589 ash::ShelfItemType type
= model_
->items()[index
].type
;
590 return (type
== ash::TYPE_APP_SHORTCUT
|| type
== ash::TYPE_BROWSER_SHORTCUT
);
593 void ChromeLauncherController::TogglePinned(ash::ShelfID id
) {
594 if (!HasShelfIDToAppIDMapping(id
))
595 return; // May happen if item closed with menu open.
603 bool ChromeLauncherController::IsPinnable(ash::ShelfID id
) const {
604 int index
= model_
->ItemIndexByID(id
);
608 ash::ShelfItemType type
= model_
->items()[index
].type
;
609 return ((type
== ash::TYPE_APP_SHORTCUT
||
610 type
== ash::TYPE_PLATFORM_APP
||
611 type
== ash::TYPE_WINDOWED_APP
) &&
615 void ChromeLauncherController::Install(ash::ShelfID id
) {
616 LauncherItemController
* controller
= GetLauncherItemController(id
);
620 std::string app_id
= GetAppIDForShelfID(id
);
621 if (extensions::util::IsExtensionInstalledPermanently(app_id
, profile_
))
624 if (controller
->type() == LauncherItemController::TYPE_APP
) {
625 AppWindowLauncherItemController
* app_window_controller
=
626 static_cast<AppWindowLauncherItemController
*>(controller
);
627 app_window_controller
->InstallApp();
631 bool ChromeLauncherController::CanInstall(ash::ShelfID id
) {
632 int index
= model_
->ItemIndexByID(id
);
636 ash::ShelfItemType type
= model_
->items()[index
].type
;
637 if (type
!= ash::TYPE_PLATFORM_APP
)
640 return extensions::util::IsEphemeralApp(GetAppIDForShelfID(id
), profile_
);
643 void ChromeLauncherController::LockV1AppWithID(const std::string
& app_id
) {
644 ash::ShelfID id
= GetShelfIDForAppID(app_id
);
645 if (!IsPinned(id
) && !IsWindowedAppInLauncher(app_id
)) {
646 CreateAppShortcutLauncherItemWithType(app_id
,
647 model_
->item_count(),
648 ash::TYPE_WINDOWED_APP
);
649 id
= GetShelfIDForAppID(app_id
);
652 id_to_item_controller_map_
[id
]->lock();
655 void ChromeLauncherController::UnlockV1AppWithID(const std::string
& app_id
) {
656 ash::ShelfID id
= GetShelfIDForAppID(app_id
);
658 CHECK(IsPinned(id
) || IsWindowedAppInLauncher(app_id
));
659 LauncherItemController
* controller
= id_to_item_controller_map_
[id
];
660 controller
->unlock();
661 if (!controller
->locked() && !IsPinned(id
))
662 CloseLauncherItem(id
);
665 void ChromeLauncherController::Launch(ash::ShelfID id
, int event_flags
) {
666 LauncherItemController
* controller
= GetLauncherItemController(id
);
668 return; // In case invoked from menu and item closed while menu up.
669 controller
->Launch(ash::LAUNCH_FROM_UNKNOWN
, event_flags
);
672 void ChromeLauncherController::Close(ash::ShelfID id
) {
673 LauncherItemController
* controller
= GetLauncherItemController(id
);
675 return; // May happen if menu closed.
679 bool ChromeLauncherController::IsOpen(ash::ShelfID id
) {
680 LauncherItemController
* controller
= GetLauncherItemController(id
);
683 return controller
->IsOpen();
686 bool ChromeLauncherController::IsPlatformApp(ash::ShelfID id
) {
687 if (!HasShelfIDToAppIDMapping(id
))
690 std::string app_id
= GetAppIDForShelfID(id
);
691 const Extension
* extension
= GetExtensionForAppID(app_id
);
692 // An extension can be synced / updated at any time and therefore not be
694 return extension
? extension
->is_platform_app() : false;
697 void ChromeLauncherController::LaunchApp(const std::string
& app_id
,
698 ash::LaunchSource source
,
700 // |extension| could be NULL when it is being unloaded for updating.
701 const Extension
* extension
= GetExtensionForAppID(app_id
);
705 if (!extensions::util::IsAppLaunchableWithoutEnabling(app_id
, profile_
)) {
706 // Do nothing if there is already a running enable flow.
707 if (extension_enable_flow_
)
710 extension_enable_flow_
.reset(
711 new ExtensionEnableFlow(profile_
, app_id
, this));
712 extension_enable_flow_
->StartForNativeWindow(NULL
);
717 if (LaunchedInNativeDesktop(app_id
))
721 // The app will be created for the currently active profile.
722 AppLaunchParams
params(
723 profile_
, extension
, ui::DispositionFromEventFlags(event_flags
),
724 chrome::HOST_DESKTOP_TYPE_ASH
, extensions::SOURCE_APP_LAUNCHER
);
725 if (source
!= ash::LAUNCH_FROM_UNKNOWN
&&
726 app_id
== extensions::kWebStoreAppId
) {
727 // Get the corresponding source string.
728 std::string source_value
= GetSourceFromAppListSource(source
);
730 // Set an override URL to include the source.
731 GURL extension_url
= extensions::AppLaunchInfo::GetFullLaunchURL(extension
);
732 params
.override_url
= net::AppendQueryParameter(
733 extension_url
, extension_urls::kWebstoreSourceField
, source_value
);
736 OpenApplication(params
);
739 void ChromeLauncherController::ActivateApp(const std::string
& app_id
,
740 ash::LaunchSource source
,
742 // If there is an existing non-shortcut controller for this app, open it.
743 ash::ShelfID id
= GetShelfIDForAppID(app_id
);
745 LauncherItemController
* controller
= GetLauncherItemController(id
);
746 controller
->Activate(source
);
750 // Create a temporary application launcher item and use it to see if there are
751 // running instances.
752 scoped_ptr
<AppShortcutLauncherItemController
> app_controller(
753 new AppShortcutLauncherItemController(app_id
, this));
754 if (!app_controller
->GetRunningApplications().empty())
755 app_controller
->Activate(source
);
757 LaunchApp(app_id
, source
, event_flags
);
760 extensions::LaunchType
ChromeLauncherController::GetLaunchType(
762 const Extension
* extension
= GetExtensionForAppID(GetAppIDForShelfID(id
));
764 // An extension can be unloaded/updated/unavailable at any time.
766 return extensions::LAUNCH_TYPE_DEFAULT
;
768 return extensions::GetLaunchType(extensions::ExtensionPrefs::Get(profile_
),
772 ash::ShelfID
ChromeLauncherController::GetShelfIDForAppID(
773 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 (i
->second
->type() == LauncherItemController::TYPE_APP_PANEL
)
778 continue; // Don't include panels
779 if (i
->second
->app_id() == app_id
)
785 bool ChromeLauncherController::HasShelfIDToAppIDMapping(ash::ShelfID id
) const {
786 return id_to_item_controller_map_
.find(id
) !=
787 id_to_item_controller_map_
.end();
790 const std::string
& ChromeLauncherController::GetAppIDForShelfID(
792 LauncherItemController
* controller
= GetLauncherItemController(id
);
794 return controller
->app_id();
797 void ChromeLauncherController::SetAppImage(const std::string
& id
,
798 const gfx::ImageSkia
& image
) {
799 // TODO: need to get this working for shortcuts.
800 for (IDToItemControllerMap::const_iterator i
=
801 id_to_item_controller_map_
.begin();
802 i
!= id_to_item_controller_map_
.end(); ++i
) {
803 LauncherItemController
* controller
= i
->second
;
804 if (controller
->app_id() != id
)
806 if (controller
->image_set_by_controller())
808 int index
= model_
->ItemIndexByID(i
->first
);
811 ash::ShelfItem item
= model_
->items()[index
];
813 model_
->Set(index
, item
);
814 // It's possible we're waiting on more than one item, so don't break.
818 void ChromeLauncherController::OnAutoHideBehaviorChanged(
819 aura::Window
* root_window
,
820 ash::ShelfAutoHideBehavior new_behavior
) {
821 SetShelfAutoHideBehaviorPrefs(new_behavior
, root_window
);
824 void ChromeLauncherController::SetLauncherItemImage(
825 ash::ShelfID shelf_id
,
826 const gfx::ImageSkia
& image
) {
827 int index
= model_
->ItemIndexByID(shelf_id
);
830 ash::ShelfItem item
= model_
->items()[index
];
832 model_
->Set(index
, item
);
835 bool ChromeLauncherController::CanPin() const {
836 const PrefService::Preference
* pref
=
837 profile_
->GetPrefs()->FindPreference(prefs::kPinnedLauncherApps
);
838 return pref
&& pref
->IsUserModifiable();
841 bool ChromeLauncherController::IsAppPinned(const std::string
& app_id
) {
842 for (IDToItemControllerMap::const_iterator i
=
843 id_to_item_controller_map_
.begin();
844 i
!= id_to_item_controller_map_
.end(); ++i
) {
845 if (IsPinned(i
->first
) && i
->second
->app_id() == app_id
)
851 bool ChromeLauncherController::IsWindowedAppInLauncher(
852 const std::string
& app_id
) {
853 int index
= model_
->ItemIndexByID(GetShelfIDForAppID(app_id
));
857 ash::ShelfItemType type
= model_
->items()[index
].type
;
858 return type
== ash::TYPE_WINDOWED_APP
;
861 void ChromeLauncherController::PinAppWithID(const std::string
& app_id
) {
863 DoPinAppWithID(app_id
);
868 void ChromeLauncherController::SetLaunchType(
870 extensions::LaunchType launch_type
) {
871 LauncherItemController
* controller
= GetLauncherItemController(id
);
875 extensions::SetLaunchType(profile_
, controller
->app_id(), launch_type
);
878 void ChromeLauncherController::UnpinAppWithID(const std::string
& app_id
) {
880 DoUnpinAppWithID(app_id
);
885 void ChromeLauncherController::OnSetShelfItemDelegate(
887 ash::ShelfItemDelegate
* item_delegate
) {
888 // TODO(skuhne): This fixes crbug.com/429870, but it does not answer why we
889 // get into this state in the first place.
890 IDToItemControllerMap::iterator iter
= id_to_item_controller_map_
.find(id
);
891 if (iter
== id_to_item_controller_map_
.end() || item_delegate
== iter
->second
)
893 LOG(ERROR
) << "Unexpected change of shelf item id: " << id
;
894 id_to_item_controller_map_
.erase(iter
);
897 bool ChromeLauncherController::IsLoggedInAsGuest() {
898 return profile_
->IsGuestSession();
901 void ChromeLauncherController::CreateNewWindow() {
902 // Use the currently active user.
903 chrome::NewEmptyWindow(profile_
, chrome::HOST_DESKTOP_TYPE_ASH
);
906 void ChromeLauncherController::CreateNewIncognitoWindow() {
907 // Use the currently active user.
908 chrome::NewEmptyWindow(profile_
->GetOffTheRecordProfile(),
909 chrome::HOST_DESKTOP_TYPE_ASH
);
912 void ChromeLauncherController::PersistPinnedState() {
913 if (ignore_persist_pinned_state_change_
)
915 // It is a coding error to call PersistPinnedState() if the pinned apps are
916 // not user-editable. The code should check earlier and not perform any
917 // modification actions that trigger persisting the state.
919 NOTREACHED() << "Can't pin but pinned state being updated";
922 // Mutating kPinnedLauncherApps is going to notify us and trigger us to
923 // process the change. We don't want that to happen so remove ourselves as a
925 pref_change_registrar_
.Remove(prefs::kPinnedLauncherApps
);
927 ListPrefUpdate
updater(profile_
->GetPrefs(), prefs::kPinnedLauncherApps
);
929 for (size_t i
= 0; i
< model_
->items().size(); ++i
) {
930 if (model_
->items()[i
].type
== ash::TYPE_APP_SHORTCUT
) {
931 ash::ShelfID id
= model_
->items()[i
].id
;
932 LauncherItemController
* controller
= GetLauncherItemController(id
);
933 if (controller
&& IsPinned(id
)) {
934 base::DictionaryValue
* app_value
= ash::CreateAppDict(
935 controller
->app_id());
937 updater
->Append(app_value
);
939 } else if (model_
->items()[i
].type
== ash::TYPE_BROWSER_SHORTCUT
) {
940 PersistChromeItemIndex(i
);
941 } else if (model_
->items()[i
].type
== ash::TYPE_APP_LIST
) {
942 base::DictionaryValue
* app_value
= ash::CreateAppDict(
943 kAppShelfIdPlaceholder
);
945 updater
->Append(app_value
);
949 pref_change_registrar_
.Add(
950 prefs::kPinnedLauncherApps
,
951 base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref
,
952 base::Unretained(this)));
955 ash::ShelfModel
* ChromeLauncherController::model() {
959 Profile
* ChromeLauncherController::profile() {
963 ash::ShelfAutoHideBehavior
ChromeLauncherController::GetShelfAutoHideBehavior(
964 aura::Window
* root_window
) const {
965 return GetShelfAutoHideBehaviorFromPrefs(profile_
, root_window
);
968 bool ChromeLauncherController::CanUserModifyShelfAutoHideBehavior(
969 aura::Window
* root_window
) const {
971 // Disable shelf auto-hide behavior on screen sides in Metro mode.
972 if (ash::Shell::GetInstance()->GetShelfAlignment(root_window
) !=
973 ash::SHELF_ALIGNMENT_BOTTOM
) {
977 return profile_
->GetPrefs()->
978 FindPreference(prefs::kShelfAutoHideBehaviorLocal
)->IsUserModifiable();
981 void ChromeLauncherController::ToggleShelfAutoHideBehavior(
982 aura::Window
* root_window
) {
983 ash::ShelfAutoHideBehavior behavior
= GetShelfAutoHideBehavior(root_window
) ==
984 ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS
?
985 ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER
:
986 ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS
;
987 SetShelfAutoHideBehaviorPrefs(behavior
, root_window
);
991 void ChromeLauncherController::UpdateAppState(content::WebContents
* contents
,
992 AppState app_state
) {
993 std::string app_id
= app_tab_helper_
->GetAppID(contents
);
995 // Check if the gMail app is loaded and it matches the given content.
996 // This special treatment is needed to address crbug.com/234268.
997 if (app_id
.empty() && ContentCanBeHandledByGmailApp(contents
))
998 app_id
= kGmailAppId
;
1000 // Check the old |app_id| for a tab. If the contents has changed we need to
1001 // remove it from the previous app.
1002 if (web_contents_to_app_id_
.find(contents
) != web_contents_to_app_id_
.end()) {
1003 std::string last_app_id
= web_contents_to_app_id_
[contents
];
1004 if (last_app_id
!= app_id
) {
1005 ash::ShelfID id
= GetShelfIDForAppID(last_app_id
);
1007 // Since GetAppState() will use |web_contents_to_app_id_| we remove
1008 // the connection before calling it.
1009 web_contents_to_app_id_
.erase(contents
);
1010 SetItemStatus(id
, GetAppState(last_app_id
));
1015 if (app_state
== APP_STATE_REMOVED
)
1016 web_contents_to_app_id_
.erase(contents
);
1018 web_contents_to_app_id_
[contents
] = app_id
;
1020 ash::ShelfID id
= GetShelfIDForAppID(app_id
);
1022 SetItemStatus(id
, (app_state
== APP_STATE_WINDOW_ACTIVE
||
1023 app_state
== APP_STATE_ACTIVE
) ? ash::STATUS_ACTIVE
:
1024 GetAppState(app_id
));
1028 ash::ShelfID
ChromeLauncherController::GetShelfIDForWebContents(
1029 content::WebContents
* contents
) {
1032 std::string app_id
= app_tab_helper_
->GetAppID(contents
);
1034 if (app_id
.empty() && ContentCanBeHandledByGmailApp(contents
))
1035 app_id
= kGmailAppId
;
1037 ash::ShelfID id
= GetShelfIDForAppID(app_id
);
1039 if (app_id
.empty() || !id
) {
1040 int browser_index
= model_
->GetItemIndexForType(ash::TYPE_BROWSER_SHORTCUT
);
1041 return model_
->items()[browser_index
].id
;
1047 void ChromeLauncherController::SetRefocusURLPatternForTest(ash::ShelfID id
,
1049 LauncherItemController
* controller
= GetLauncherItemController(id
);
1052 int index
= model_
->ItemIndexByID(id
);
1054 NOTREACHED() << "Invalid launcher id";
1058 ash::ShelfItemType type
= model_
->items()[index
].type
;
1059 if (type
== ash::TYPE_APP_SHORTCUT
|| type
== ash::TYPE_WINDOWED_APP
) {
1060 AppShortcutLauncherItemController
* app_controller
=
1061 static_cast<AppShortcutLauncherItemController
*>(controller
);
1062 app_controller
->set_refocus_url(url
);
1064 NOTREACHED() << "Invalid launcher type";
1068 const Extension
* ChromeLauncherController::GetExtensionForAppID(
1069 const std::string
& app_id
) const {
1070 return extensions::ExtensionRegistry::Get(profile_
)->GetExtensionById(
1071 app_id
, extensions::ExtensionRegistry::EVERYTHING
);
1074 ash::ShelfItemDelegate::PerformedAction
1075 ChromeLauncherController::ActivateWindowOrMinimizeIfActive(
1076 ui::BaseWindow
* window
,
1077 bool allow_minimize
) {
1078 // In separated desktop mode we might have to teleport a window back to the
1080 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
1081 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED
) {
1082 aura::Window
* native_window
= window
->GetNativeWindow();
1083 const std::string
& current_user
=
1084 multi_user_util::GetUserIDFromProfile(profile());
1085 chrome::MultiUserWindowManager
* manager
=
1086 chrome::MultiUserWindowManager::GetInstance();
1087 if (!manager
->IsWindowOnDesktopOfUser(native_window
, current_user
)) {
1088 ash::MultiProfileUMA::RecordTeleportAction(
1089 ash::MultiProfileUMA::TELEPORT_WINDOW_RETURN_BY_LAUNCHER
);
1090 manager
->ShowWindowForUser(native_window
, current_user
);
1092 return ash::ShelfItemDelegate::kExistingWindowActivated
;
1096 if (window
->IsActive() && allow_minimize
) {
1097 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1098 switches::kDisableMinimizeOnSecondLauncherItemClick
)) {
1099 AnimateWindow(window
->GetNativeWindow(),
1100 wm::WINDOW_ANIMATION_TYPE_BOUNCE
);
1103 return ash::ShelfItemDelegate::kExistingWindowMinimized
;
1108 return ash::ShelfItemDelegate::kExistingWindowActivated
;
1110 return ash::ShelfItemDelegate::kNoAction
;
1113 void ChromeLauncherController::OnShelfCreated(ash::Shelf
* shelf
) {
1114 shelves_
.insert(shelf
);
1115 shelf
->shelf_widget()->shelf_layout_manager()->AddObserver(this);
1118 void ChromeLauncherController::OnShelfDestroyed(ash::Shelf
* shelf
) {
1119 shelves_
.erase(shelf
);
1120 // RemoveObserver is not called here, since by the time this method is called
1121 // Shelf is already in its destructor.
1124 void ChromeLauncherController::ShelfItemAdded(int index
) {
1125 // The app list launcher can get added to the shelf after we applied the
1126 // preferences. In that case the item might be at the wrong spot. As such we
1127 // call the function again.
1128 if (model_
->items()[index
].type
== ash::TYPE_APP_LIST
)
1129 UpdateAppLaunchersFromPref();
1132 void ChromeLauncherController::ShelfItemRemoved(int index
, ash::ShelfID id
) {
1133 // TODO(skuhne): This fixes crbug.com/429870, but it does not answer why we
1134 // get into this state in the first place.
1135 IDToItemControllerMap::iterator iter
= id_to_item_controller_map_
.find(id
);
1136 if (iter
== id_to_item_controller_map_
.end())
1139 LOG(ERROR
) << "Unexpected change of shelf item id: " << id
;
1141 id_to_item_controller_map_
.erase(iter
);
1144 void ChromeLauncherController::ShelfItemMoved(int start_index
,
1146 const ash::ShelfItem
& item
= model_
->items()[target_index
];
1147 // We remember the moved item position if it is either pinnable or
1148 // it is the app list with the alternate shelf layout.
1149 if ((HasShelfIDToAppIDMapping(item
.id
) && IsPinned(item
.id
)) ||
1150 item
.type
== ash::TYPE_APP_LIST
)
1151 PersistPinnedState();
1154 void ChromeLauncherController::ShelfItemChanged(
1156 const ash::ShelfItem
& old_item
) {
1159 void ChromeLauncherController::ShelfStatusChanged() {
1162 void ChromeLauncherController::ActiveUserChanged(
1163 const std::string
& user_email
) {
1164 // Store the order of running applications for the user which gets inactive.
1165 RememberUnpinnedRunningApplicationOrder();
1166 // Coming here the default profile is already switched. All profile specific
1167 // resources get released and the new profile gets attached instead.
1169 // When coming here, the active user has already be changed so that we can
1170 // set it as active.
1171 AttachProfile(ProfileManager::GetActiveUserProfile());
1172 // Update the V1 applications.
1173 browser_status_monitor_
->ActiveUserChanged(user_email
);
1174 // Switch the running applications to the new user.
1175 app_window_controller_
->ActiveUserChanged(user_email
);
1176 // Update the user specific shell properties from the new user profile.
1177 UpdateAppLaunchersFromPref();
1178 SetShelfAlignmentFromPrefs();
1179 SetShelfAutoHideBehaviorFromPrefs();
1180 SetShelfBehaviorsFromPrefs();
1181 #if defined(OS_CHROMEOS)
1182 SetVirtualKeyboardBehaviorFromPrefs();
1183 #endif // defined(OS_CHROMEOS)
1184 // Restore the order of running, but unpinned applications for the activated
1186 RestoreUnpinnedRunningApplicationOrder(user_email
);
1187 // Inform the system tray of the change.
1188 ash::Shell::GetInstance()->system_tray_delegate()->ActiveUserWasChanged();
1189 // Force on-screen keyboard to reset.
1190 if (keyboard::IsKeyboardEnabled())
1191 ash::Shell::GetInstance()->CreateKeyboard();
1194 void ChromeLauncherController::AdditionalUserAddedToSession(Profile
* profile
) {
1195 // Switch the running applications to the new user.
1196 app_window_controller_
->AdditionalUserAddedToSession(profile
);
1199 void ChromeLauncherController::OnExtensionLoaded(
1200 content::BrowserContext
* browser_context
,
1201 const Extension
* extension
) {
1202 if (IsAppPinned(extension
->id())) {
1203 // Clear and re-fetch to ensure icon is up-to-date.
1204 app_icon_loader_
->ClearImage(extension
->id());
1205 app_icon_loader_
->FetchImage(extension
->id());
1208 UpdateAppLaunchersFromPref();
1211 void ChromeLauncherController::OnExtensionUnloaded(
1212 content::BrowserContext
* browser_context
,
1213 const Extension
* extension
,
1214 UnloadedExtensionInfo::Reason reason
) {
1215 const std::string
& id
= extension
->id();
1216 const Profile
* profile
= Profile::FromBrowserContext(browser_context
);
1218 // Since we might have windowed apps of this type which might have
1219 // outstanding locks which needs to be removed.
1220 if (GetShelfIDForAppID(id
) &&
1221 reason
== UnloadedExtensionInfo::REASON_UNINSTALL
) {
1222 CloseWindowedAppsFromRemovedExtension(id
, profile
);
1225 if (IsAppPinned(id
)) {
1226 if (reason
== UnloadedExtensionInfo::REASON_UNINSTALL
) {
1227 if (profile
== profile_
) {
1228 DoUnpinAppWithID(id
);
1230 app_icon_loader_
->ClearImage(id
);
1232 app_icon_loader_
->UpdateImage(id
);
1237 void ChromeLauncherController::OnShelfAlignmentChanged(
1238 aura::Window
* root_window
) {
1239 const char* pref_value
= NULL
;
1240 switch (ash::Shell::GetInstance()->GetShelfAlignment(root_window
)) {
1241 case ash::SHELF_ALIGNMENT_BOTTOM
:
1242 pref_value
= ash::kShelfAlignmentBottom
;
1244 case ash::SHELF_ALIGNMENT_LEFT
:
1245 pref_value
= ash::kShelfAlignmentLeft
;
1247 case ash::SHELF_ALIGNMENT_RIGHT
:
1248 pref_value
= ash::kShelfAlignmentRight
;
1250 case ash::SHELF_ALIGNMENT_TOP
:
1251 pref_value
= ash::kShelfAlignmentTop
;
1254 UpdatePerDisplayPref(
1255 profile_
->GetPrefs(), root_window
, prefs::kShelfAlignment
, pref_value
);
1257 if (root_window
== ash::Shell::GetPrimaryRootWindow()) {
1258 // See comment in |kShelfAlignment| about why we have two prefs here.
1259 profile_
->GetPrefs()->SetString(prefs::kShelfAlignmentLocal
, pref_value
);
1260 profile_
->GetPrefs()->SetString(prefs::kShelfAlignment
, pref_value
);
1264 void ChromeLauncherController::OnDisplayConfigurationChanged() {
1265 SetShelfBehaviorsFromPrefs();
1268 void ChromeLauncherController::OnIsSyncingChanged() {
1269 PrefServiceSyncable
* prefs
= PrefServiceSyncable::FromProfile(profile_
);
1270 MaybePropagatePrefToLocal(prefs
,
1271 prefs::kShelfAlignmentLocal
,
1272 prefs::kShelfAlignment
);
1273 MaybePropagatePrefToLocal(prefs
,
1274 prefs::kShelfAutoHideBehaviorLocal
,
1275 prefs::kShelfAutoHideBehavior
);
1278 void ChromeLauncherController::OnAppSyncUIStatusChanged() {
1279 if (app_sync_ui_state_
->status() == AppSyncUIState::STATUS_SYNCING
)
1280 model_
->SetStatus(ash::ShelfModel::STATUS_LOADING
);
1282 model_
->SetStatus(ash::ShelfModel::STATUS_NORMAL
);
1285 void ChromeLauncherController::ExtensionEnableFlowFinished() {
1286 LaunchApp(extension_enable_flow_
->extension_id(),
1287 ash::LAUNCH_FROM_UNKNOWN
,
1289 extension_enable_flow_
.reset();
1292 void ChromeLauncherController::ExtensionEnableFlowAborted(bool user_initiated
) {
1293 extension_enable_flow_
.reset();
1296 ChromeLauncherAppMenuItems
ChromeLauncherController::GetApplicationList(
1297 const ash::ShelfItem
& item
,
1299 // Make sure that there is a controller associated with the id and that the
1300 // extension itself is a valid application and not a panel.
1301 LauncherItemController
* controller
= GetLauncherItemController(item
.id
);
1302 if (!controller
|| !GetShelfIDForAppID(controller
->app_id()))
1303 return ChromeLauncherAppMenuItems().Pass();
1305 return controller
->GetApplicationList(event_flags
);
1308 std::vector
<content::WebContents
*>
1309 ChromeLauncherController::GetV1ApplicationsFromAppId(std::string app_id
) {
1310 ash::ShelfID id
= GetShelfIDForAppID(app_id
);
1312 // If there is no such an item pinned to the launcher, no menu gets created.
1314 LauncherItemController
* controller
= GetLauncherItemController(id
);
1316 if (controller
->type() == LauncherItemController::TYPE_SHORTCUT
)
1317 return GetV1ApplicationsFromController(controller
);
1319 return std::vector
<content::WebContents
*>();
1322 void ChromeLauncherController::ActivateShellApp(const std::string
& app_id
,
1324 ash::ShelfID id
= GetShelfIDForAppID(app_id
);
1326 LauncherItemController
* controller
= GetLauncherItemController(id
);
1327 if (controller
&& controller
->type() == LauncherItemController::TYPE_APP
) {
1328 AppWindowLauncherItemController
* app_window_controller
=
1329 static_cast<AppWindowLauncherItemController
*>(controller
);
1330 app_window_controller
->ActivateIndexedApp(index
);
1335 bool ChromeLauncherController::IsWebContentHandledByApplication(
1336 content::WebContents
* web_contents
,
1337 const std::string
& app_id
) {
1338 if ((web_contents_to_app_id_
.find(web_contents
) !=
1339 web_contents_to_app_id_
.end()) &&
1340 (web_contents_to_app_id_
[web_contents
] == app_id
))
1342 return (app_id
== kGmailAppId
&& ContentCanBeHandledByGmailApp(web_contents
));
1345 bool ChromeLauncherController::ContentCanBeHandledByGmailApp(
1346 content::WebContents
* web_contents
) {
1347 ash::ShelfID id
= GetShelfIDForAppID(kGmailAppId
);
1349 const GURL url
= web_contents
->GetURL();
1350 // We need to extend the application matching for the gMail app beyond the
1351 // manifest file's specification. This is required because of the namespace
1352 // overlap with the offline app ("/mail/mu/").
1353 if (!base::MatchPattern(url
.path(), "/mail/mu/*") &&
1354 base::MatchPattern(url
.path(), "/mail/*") &&
1355 GetExtensionForAppID(kGmailAppId
) &&
1356 GetExtensionForAppID(kGmailAppId
)->OverlapsWithOrigin(url
))
1362 gfx::Image
ChromeLauncherController::GetAppListIcon(
1363 content::WebContents
* web_contents
) const {
1364 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
1365 if (IsIncognito(web_contents
))
1366 return rb
.GetImageNamed(IDR_ASH_SHELF_LIST_INCOGNITO_BROWSER
);
1367 favicon::FaviconDriver
* favicon_driver
=
1368 favicon::ContentFaviconDriver::FromWebContents(web_contents
);
1369 gfx::Image result
= favicon_driver
->GetFavicon();
1370 if (result
.IsEmpty())
1371 return rb
.GetImageNamed(IDR_DEFAULT_FAVICON
);
1375 base::string16
ChromeLauncherController::GetAppListTitle(
1376 content::WebContents
* web_contents
) const {
1377 base::string16 title
= web_contents
->GetTitle();
1380 WebContentsToAppIDMap::const_iterator iter
=
1381 web_contents_to_app_id_
.find(web_contents
);
1382 if (iter
!= web_contents_to_app_id_
.end()) {
1383 std::string app_id
= iter
->second
;
1384 const extensions::Extension
* extension
= GetExtensionForAppID(app_id
);
1386 return base::UTF8ToUTF16(extension
->name());
1388 return l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE
);
1391 ash::ShelfID
ChromeLauncherController::CreateAppShortcutLauncherItem(
1392 const std::string
& app_id
,
1394 return CreateAppShortcutLauncherItemWithType(app_id
,
1396 ash::TYPE_APP_SHORTCUT
);
1399 void ChromeLauncherController::SetAppTabHelperForTest(AppTabHelper
* helper
) {
1400 app_tab_helper_
.reset(helper
);
1403 void ChromeLauncherController::SetAppIconLoaderForTest(
1404 extensions::AppIconLoader
* loader
) {
1405 app_icon_loader_
.reset(loader
);
1408 const std::string
& ChromeLauncherController::GetAppIdFromShelfIdForTest(
1410 return id_to_item_controller_map_
[id
]->app_id();
1413 void ChromeLauncherController::SetShelfItemDelegateManagerForTest(
1414 ash::ShelfItemDelegateManager
* manager
) {
1415 if (item_delegate_manager_
)
1416 item_delegate_manager_
->RemoveObserver(this);
1418 item_delegate_manager_
= manager
;
1420 if (item_delegate_manager_
)
1421 item_delegate_manager_
->AddObserver(this);
1424 void ChromeLauncherController::RememberUnpinnedRunningApplicationOrder() {
1425 RunningAppListIds list
;
1426 for (int i
= 0; i
< model_
->item_count(); i
++) {
1427 ash::ShelfItemType type
= model_
->items()[i
].type
;
1428 if (type
== ash::TYPE_WINDOWED_APP
|| type
== ash::TYPE_PLATFORM_APP
)
1429 list
.push_back(GetAppIDForShelfID(model_
->items()[i
].id
));
1431 last_used_running_application_order_
[
1432 multi_user_util::GetUserIDFromProfile(profile_
)] = list
;
1435 void ChromeLauncherController::RestoreUnpinnedRunningApplicationOrder(
1436 const std::string
& user_id
) {
1437 const RunningAppListIdMap::iterator app_id_list
=
1438 last_used_running_application_order_
.find(user_id
);
1439 if (app_id_list
== last_used_running_application_order_
.end())
1442 // Find the first insertion point for running applications.
1443 int running_index
= model_
->FirstRunningAppIndex();
1444 for (RunningAppListIds::iterator app_id
= app_id_list
->second
.begin();
1445 app_id
!= app_id_list
->second
.end(); ++app_id
) {
1446 ash::ShelfID shelf_id
= GetShelfIDForAppID(*app_id
);
1448 int app_index
= model_
->ItemIndexByID(shelf_id
);
1449 DCHECK_GE(app_index
, 0);
1450 ash::ShelfItemType type
= model_
->items()[app_index
].type
;
1451 if (type
== ash::TYPE_WINDOWED_APP
|| type
== ash::TYPE_PLATFORM_APP
) {
1452 if (running_index
!= app_index
)
1453 model_
->Move(running_index
, app_index
);
1460 ash::ShelfID
ChromeLauncherController::CreateAppShortcutLauncherItemWithType(
1461 const std::string
& app_id
,
1463 ash::ShelfItemType shelf_item_type
) {
1464 AppShortcutLauncherItemController
* controller
=
1465 new AppShortcutLauncherItemController(app_id
, this);
1466 ash::ShelfID shelf_id
= InsertAppLauncherItem(
1467 controller
, app_id
, ash::STATUS_CLOSED
, index
, shelf_item_type
);
1471 LauncherItemController
* ChromeLauncherController::GetLauncherItemController(
1472 const ash::ShelfID id
) {
1473 if (!HasShelfIDToAppIDMapping(id
))
1475 return id_to_item_controller_map_
[id
];
1478 bool ChromeLauncherController::IsBrowserFromActiveUser(Browser
* browser
) {
1479 // If running multi user mode with separate desktops, we have to check if the
1480 // browser is from the active user.
1481 if (chrome::MultiUserWindowManager::GetMultiProfileMode() !=
1482 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED
)
1484 return multi_user_util::IsProfileFromActiveUser(browser
->profile());
1487 bool ChromeLauncherController::ShelfBoundsChangesProbablyWithUser(
1488 aura::Window
* root_window
,
1489 const std::string
& user_id
) const {
1490 Profile
* other_profile
= multi_user_util::GetProfileFromUserID(user_id
);
1491 DCHECK_NE(other_profile
, profile_
);
1493 // Note: The Auto hide state from preferences is not the same as the actual
1494 // visibility of the shelf. Depending on all the various states (full screen,
1495 // no window on desktop, multi user, ..) the shelf could be shown - or not.
1496 bool currently_shown
= ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER
==
1497 GetShelfAutoHideBehaviorFromPrefs(profile_
, root_window
);
1498 bool other_shown
= ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER
==
1499 GetShelfAutoHideBehaviorFromPrefs(other_profile
, root_window
);
1501 return currently_shown
!= other_shown
||
1502 GetShelfAlignmentFromPrefs(profile_
, root_window
) !=
1503 GetShelfAlignmentFromPrefs(other_profile
, root_window
);
1506 void ChromeLauncherController::OnUserProfileReadyToSwitch(Profile
* profile
) {
1507 #if defined(OS_CHROMEOS)
1508 if (user_switch_observer_
.get())
1509 user_switch_observer_
->OnUserProfileReadyToSwitch(profile
);
1513 void ChromeLauncherController::LauncherItemClosed(ash::ShelfID id
) {
1514 IDToItemControllerMap::iterator iter
= id_to_item_controller_map_
.find(id
);
1515 CHECK(iter
!= id_to_item_controller_map_
.end());
1516 CHECK(iter
->second
);
1517 app_icon_loader_
->ClearImage(iter
->second
->app_id());
1518 id_to_item_controller_map_
.erase(iter
);
1519 int index
= model_
->ItemIndexByID(id
);
1520 // A "browser proxy" is not known to the model and this removal does
1521 // therefore not need to be propagated to the model.
1523 model_
->RemoveItemAt(index
);
1526 void ChromeLauncherController::DoPinAppWithID(const std::string
& app_id
) {
1527 // If there is an item, do nothing and return.
1528 if (IsAppPinned(app_id
))
1531 ash::ShelfID shelf_id
= GetShelfIDForAppID(app_id
);
1533 // App item exists, pin it
1536 // Otherwise, create a shortcut item for it.
1537 CreateAppShortcutLauncherItem(app_id
, model_
->item_count());
1539 PersistPinnedState();
1543 void ChromeLauncherController::DoUnpinAppWithID(const std::string
& app_id
) {
1544 ash::ShelfID shelf_id
= GetShelfIDForAppID(app_id
);
1545 if (shelf_id
&& IsPinned(shelf_id
))
1549 int ChromeLauncherController::PinRunningAppInternal(int index
,
1550 ash::ShelfID shelf_id
) {
1551 int running_index
= model_
->ItemIndexByID(shelf_id
);
1552 ash::ShelfItem item
= model_
->items()[running_index
];
1553 DCHECK(item
.type
== ash::TYPE_WINDOWED_APP
||
1554 item
.type
== ash::TYPE_PLATFORM_APP
);
1555 item
.type
= ash::TYPE_APP_SHORTCUT
;
1556 model_
->Set(running_index
, item
);
1557 // The |ShelfModel|'s weight system might reposition the item to a
1558 // new index, so we get the index again.
1559 running_index
= model_
->ItemIndexByID(shelf_id
);
1560 if (running_index
< index
)
1562 if (running_index
!= index
)
1563 model_
->Move(running_index
, index
);
1567 void ChromeLauncherController::UnpinRunningAppInternal(int index
) {
1568 DCHECK_GE(index
, 0);
1569 ash::ShelfItem item
= model_
->items()[index
];
1570 DCHECK_EQ(item
.type
, ash::TYPE_APP_SHORTCUT
);
1571 item
.type
= ash::TYPE_WINDOWED_APP
;
1572 // A platform app and a windowed app are sharing TYPE_APP_SHORTCUT. As such
1573 // we have to check here what this was before it got a shortcut.
1574 LauncherItemController
* controller
= GetLauncherItemController(item
.id
);
1575 if (controller
&& controller
->type() == LauncherItemController::TYPE_APP
)
1576 item
.type
= ash::TYPE_PLATFORM_APP
;
1577 model_
->Set(index
, item
);
1580 void ChromeLauncherController::UpdateAppLaunchersFromPref() {
1581 // There are various functions which will trigger a |PersistPinnedState| call
1582 // like a direct call to |DoPinAppWithID|, or an indirect call to the menu
1583 // model which will use weights to re-arrange the icons to new positions.
1584 // Since this function is meant to synchronize the "is state" with the
1585 // "sync state", it makes no sense to store any changes by this function back
1586 // into the pref state. Therefore we tell |persistPinnedState| to ignore any
1587 // invocations while we are running.
1588 base::AutoReset
<bool> auto_reset(&ignore_persist_pinned_state_change_
, true);
1589 std::vector
<std::string
> pinned_apps
= GetListOfPinnedAppsAndBrowser();
1592 int max_index
= model_
->item_count();
1594 // When one of the two special items cannot be moved (and we do not know where
1595 // yet), we remember the current location in one of these variables.
1596 int chrome_index
= -1;
1597 int app_list_index
= -1;
1599 // Walk the model and |pinned_apps| from the pref lockstep, adding and
1600 // removing items as necessary. NB: This code uses plain old indexing instead
1601 // of iterators because of model mutations as part of the loop.
1602 std::vector
<std::string
>::const_iterator
pref_app_id(pinned_apps
.begin());
1603 for (; index
< max_index
&& pref_app_id
!= pinned_apps
.end(); ++index
) {
1604 // Check if we have an item which we need to handle.
1605 if (*pref_app_id
== extension_misc::kChromeAppId
||
1606 *pref_app_id
== kAppShelfIdPlaceholder
||
1607 IsAppPinned(*pref_app_id
)) {
1608 for (; index
< max_index
; ++index
) {
1609 const ash::ShelfItem
& item(model_
->items()[index
]);
1610 bool is_app_list
= item
.type
== ash::TYPE_APP_LIST
;
1611 bool is_chrome
= item
.type
== ash::TYPE_BROWSER_SHORTCUT
;
1612 if (item
.type
!= ash::TYPE_APP_SHORTCUT
&& !is_app_list
&& !is_chrome
)
1614 LauncherItemController
* controller
= GetLauncherItemController(item
.id
);
1615 if ((kAppShelfIdPlaceholder
== *pref_app_id
&& is_app_list
) ||
1616 (extension_misc::kChromeAppId
== *pref_app_id
&& is_chrome
) ||
1617 (controller
&& controller
->app_id() == *pref_app_id
)) {
1618 // Check if an item needs to be moved here.
1619 MoveChromeOrApplistToFinalPosition(
1620 is_chrome
, is_app_list
, index
, &chrome_index
, &app_list_index
);
1624 if (is_chrome
|| is_app_list
) {
1625 // We cannot delete any of these shortcuts. As such we remember
1626 // their positions and move them later where they belong.
1628 chrome_index
= index
;
1630 app_list_index
= index
;
1631 // And skip the item - or exit the loop if end is reached (note that
1632 // in that case we will reduce the index again by one and this only
1633 // compensates for it).
1634 if (index
>= max_index
- 1)
1638 // Check if this is a platform or a windowed app.
1639 if (item
.type
== ash::TYPE_APP_SHORTCUT
&&
1641 (controller
->locked() ||
1642 controller
->type() == LauncherItemController::TYPE_APP
)) {
1643 // Note: This will not change the amount of items (|max_index|).
1644 // Even changes to the actual |index| due to item weighting
1645 // changes should be fine.
1646 UnpinRunningAppInternal(index
);
1649 LauncherItemClosed(item
.id
);
1656 // If the item wasn't found, that means id_to_item_controller_map_
1658 DCHECK(index
<= max_index
);
1660 // Check if the item was already running but not yet pinned.
1661 ash::ShelfID shelf_id
= GetShelfIDForAppID(*pref_app_id
);
1663 // This app is running but not yet pinned. So pin and move it.
1664 index
= PinRunningAppInternal(index
, shelf_id
);
1666 // This app wasn't pinned before, insert a new entry.
1667 shelf_id
= CreateAppShortcutLauncherItem(*pref_app_id
, index
);
1669 index
= model_
->ItemIndexByID(shelf_id
);
1675 // Remove any trailing existing items.
1676 while (index
< model_
->item_count()) {
1677 const ash::ShelfItem
& item(model_
->items()[index
]);
1678 if (item
.type
== ash::TYPE_APP_SHORTCUT
) {
1679 LauncherItemController
* controller
= GetLauncherItemController(item
.id
);
1681 if (controller
->locked() ||
1682 controller
->type() == LauncherItemController::TYPE_APP
) {
1683 UnpinRunningAppInternal(index
);
1685 LauncherItemClosed(item
.id
);
1689 if (item
.type
== ash::TYPE_BROWSER_SHORTCUT
)
1690 chrome_index
= index
;
1691 else if (item
.type
== ash::TYPE_APP_LIST
)
1692 app_list_index
= index
;
1697 // Append unprocessed items from the pref to the end of the model.
1698 for (; pref_app_id
!= pinned_apps
.end(); ++pref_app_id
) {
1699 // All items but the chrome and / or app list shortcut needs to be added.
1700 bool is_chrome
= *pref_app_id
== extension_misc::kChromeAppId
;
1701 bool is_app_list
= *pref_app_id
== kAppShelfIdPlaceholder
;
1702 // Coming here we know the next item which can be finalized, either the
1703 // chrome item or the app launcher. The final position is the end of the
1704 // list. The menu model will make sure that the item is grouped according
1705 // to its weight (which we do not know here).
1706 if (!is_chrome
&& !is_app_list
) {
1707 DoPinAppWithID(*pref_app_id
);
1708 int target_index
= FindInsertionPoint(false);
1709 ash::ShelfID id
= GetShelfIDForAppID(*pref_app_id
);
1710 int source_index
= model_
->ItemIndexByID(id
);
1711 if (source_index
!= target_index
)
1712 model_
->Move(source_index
, target_index
);
1714 // Needed for the old layout - the weight might force it to be lower in
1716 if (app_list_index
!= -1 && target_index
<= app_list_index
)
1719 int target_index
= FindInsertionPoint(is_app_list
);
1720 MoveChromeOrApplistToFinalPosition(
1721 is_chrome
, is_app_list
, target_index
, &chrome_index
, &app_list_index
);
1726 void ChromeLauncherController::SetShelfAutoHideBehaviorPrefs(
1727 ash::ShelfAutoHideBehavior behavior
,
1728 aura::Window
* root_window
) {
1729 const char* value
= NULL
;
1731 case ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS
:
1732 value
= ash::kShelfAutoHideBehaviorAlways
;
1734 case ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER
:
1735 value
= ash::kShelfAutoHideBehaviorNever
;
1737 case ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN
:
1738 // This one should not be a valid preference option for now. We only want
1739 // to completely hide it when we run in app mode - or while we temporarily
1740 // hide the shelf as part of an animation (e.g. the multi user change).
1744 UpdatePerDisplayPref(
1745 profile_
->GetPrefs(), root_window
, prefs::kShelfAutoHideBehavior
, value
);
1747 if (root_window
== ash::Shell::GetPrimaryRootWindow()) {
1748 // See comment in |kShelfAlignment| about why we have two prefs here.
1749 profile_
->GetPrefs()->SetString(prefs::kShelfAutoHideBehaviorLocal
, value
);
1750 profile_
->GetPrefs()->SetString(prefs::kShelfAutoHideBehavior
, value
);
1754 void ChromeLauncherController::SetShelfAutoHideBehaviorFromPrefs() {
1755 aura::Window::Windows root_windows
= ash::Shell::GetAllRootWindows();
1757 for (aura::Window::Windows::const_iterator iter
= root_windows
.begin();
1758 iter
!= root_windows
.end(); ++iter
) {
1759 ash::Shell::GetInstance()->SetShelfAutoHideBehavior(
1760 GetShelfAutoHideBehavior(*iter
), *iter
);
1764 void ChromeLauncherController::SetShelfAlignmentFromPrefs() {
1765 if (!ash::ShelfWidget::ShelfAlignmentAllowed())
1768 aura::Window::Windows root_windows
= ash::Shell::GetAllRootWindows();
1770 for (aura::Window::Windows::const_iterator iter
= root_windows
.begin();
1771 iter
!= root_windows
.end(); ++iter
) {
1772 ash::Shell::GetInstance()->SetShelfAlignment(
1773 GetShelfAlignmentFromPrefs(profile_
, *iter
), *iter
);
1777 void ChromeLauncherController::SetShelfBehaviorsFromPrefs() {
1778 SetShelfAutoHideBehaviorFromPrefs();
1779 SetShelfAlignmentFromPrefs();
1782 #if defined(OS_CHROMEOS)
1783 void ChromeLauncherController::SetVirtualKeyboardBehaviorFromPrefs() {
1784 const PrefService
* service
= profile_
->GetPrefs();
1785 const bool was_enabled
= keyboard::IsKeyboardEnabled();
1786 if (!service
->HasPrefPath(prefs::kTouchVirtualKeyboardEnabled
)) {
1787 keyboard::SetKeyboardShowOverride(keyboard::KEYBOARD_SHOW_OVERRIDE_NONE
);
1789 const bool enable
= service
->GetBoolean(
1790 prefs::kTouchVirtualKeyboardEnabled
);
1791 keyboard::SetKeyboardShowOverride(
1792 enable
? keyboard::KEYBOARD_SHOW_OVERRIDE_ENABLED
1793 : keyboard::KEYBOARD_SHOW_OVERRIDE_DISABLED
);
1795 const bool is_enabled
= keyboard::IsKeyboardEnabled();
1796 if (was_enabled
&& !is_enabled
)
1797 ash::Shell::GetInstance()->DeactivateKeyboard();
1798 else if (is_enabled
&& !was_enabled
)
1799 ash::Shell::GetInstance()->CreateKeyboard();
1801 #endif // defined(OS_CHROMEOS)
1803 ash::ShelfItemStatus
ChromeLauncherController::GetAppState(
1804 const std::string
& app_id
) {
1805 ash::ShelfItemStatus status
= ash::STATUS_CLOSED
;
1806 for (WebContentsToAppIDMap::iterator it
= web_contents_to_app_id_
.begin();
1807 it
!= web_contents_to_app_id_
.end();
1809 if (it
->second
== app_id
) {
1810 Browser
* browser
= chrome::FindBrowserWithWebContents(it
->first
);
1811 // Usually there should never be an item in our |web_contents_to_app_id_|
1812 // list which got deleted already. However - in some situations e.g.
1813 // Browser::SwapTabContent there is temporarily no associated browser.
1816 if (browser
->window()->IsActive()) {
1817 return browser
->tab_strip_model()->GetActiveWebContents() == it
->first
?
1818 ash::STATUS_ACTIVE
: ash::STATUS_RUNNING
;
1820 status
= ash::STATUS_RUNNING
;
1827 ash::ShelfID
ChromeLauncherController::InsertAppLauncherItem(
1828 LauncherItemController
* controller
,
1829 const std::string
& app_id
,
1830 ash::ShelfItemStatus status
,
1832 ash::ShelfItemType shelf_item_type
) {
1833 ash::ShelfID id
= model_
->next_id();
1834 CHECK(!HasShelfIDToAppIDMapping(id
));
1836 id_to_item_controller_map_
[id
] = controller
;
1837 controller
->set_shelf_id(id
);
1839 ash::ShelfItem item
;
1840 item
.type
= shelf_item_type
;
1841 item
.image
= extensions::util::GetDefaultAppIcon();
1843 ash::ShelfItemStatus new_state
= GetAppState(app_id
);
1844 if (new_state
!= ash::STATUS_CLOSED
)
1847 item
.status
= status
;
1849 model_
->AddAt(index
, item
);
1851 app_icon_loader_
->FetchImage(app_id
);
1852 app_icon_loader_
->UpdateImage(app_id
);
1854 SetShelfItemDelegate(id
, controller
);
1859 std::vector
<content::WebContents
*>
1860 ChromeLauncherController::GetV1ApplicationsFromController(
1861 LauncherItemController
* controller
) {
1862 DCHECK(controller
->type() == LauncherItemController::TYPE_SHORTCUT
);
1863 AppShortcutLauncherItemController
* app_controller
=
1864 static_cast<AppShortcutLauncherItemController
*>(controller
);
1865 return app_controller
->GetRunningApplications();
1868 BrowserShortcutLauncherItemController
*
1869 ChromeLauncherController::GetBrowserShortcutLauncherItemController() {
1870 for (IDToItemControllerMap::iterator i
= id_to_item_controller_map_
.begin();
1871 i
!= id_to_item_controller_map_
.end(); ++i
) {
1872 int index
= model_
->ItemIndexByID(i
->first
);
1873 const ash::ShelfItem
& item
= model_
->items()[index
];
1874 if (item
.type
== ash::TYPE_BROWSER_SHORTCUT
)
1875 return static_cast<BrowserShortcutLauncherItemController
*>(i
->second
);
1877 NOTREACHED() << "There should be always a BrowserLauncherItemController.";
1881 ash::ShelfID
ChromeLauncherController::CreateBrowserShortcutLauncherItem() {
1882 ash::ShelfItem browser_shortcut
;
1883 browser_shortcut
.type
= ash::TYPE_BROWSER_SHORTCUT
;
1884 ResourceBundle
& rb
= ResourceBundle::GetSharedInstance();
1885 browser_shortcut
.image
= *rb
.GetImageSkiaNamed(IDR_PRODUCT_LOGO_32
);
1886 ash::ShelfID id
= model_
->next_id();
1887 size_t index
= GetChromeIconIndexForCreation();
1888 model_
->AddAt(index
, browser_shortcut
);
1889 id_to_item_controller_map_
[id
] =
1890 new BrowserShortcutLauncherItemController(this);
1891 id_to_item_controller_map_
[id
]->set_shelf_id(id
);
1892 // ShelfItemDelegateManager owns BrowserShortcutLauncherItemController.
1893 SetShelfItemDelegate(id
, id_to_item_controller_map_
[id
]);
1897 void ChromeLauncherController::PersistChromeItemIndex(int index
) {
1898 profile_
->GetPrefs()->SetInteger(prefs::kShelfChromeIconIndex
, index
);
1901 int ChromeLauncherController::GetChromeIconIndexFromPref() const {
1902 size_t index
= profile_
->GetPrefs()->GetInteger(prefs::kShelfChromeIconIndex
);
1903 const base::ListValue
* pinned_apps_pref
=
1904 profile_
->GetPrefs()->GetList(prefs::kPinnedLauncherApps
);
1905 return std::max(static_cast<size_t>(0),
1906 std::min(pinned_apps_pref
->GetSize(), index
));
1909 void ChromeLauncherController::MoveChromeOrApplistToFinalPosition(
1914 int* app_list_index
) {
1915 if (is_chrome
&& *chrome_index
!= -1) {
1916 model_
->Move(*chrome_index
, target_index
);
1917 if (*app_list_index
!= -1 &&
1918 *chrome_index
< *app_list_index
&&
1919 target_index
> *app_list_index
)
1920 --(*app_list_index
);
1922 } else if (is_app_list
&& *app_list_index
!= -1) {
1923 model_
->Move(*app_list_index
, target_index
);
1924 if (*chrome_index
!= -1 &&
1925 *app_list_index
< *chrome_index
&&
1926 target_index
> *chrome_index
)
1928 *app_list_index
= -1;
1932 int ChromeLauncherController::FindInsertionPoint(bool is_app_list
) {
1933 // Keeping this change small to backport to M33&32 (see crbug.com/329597).
1934 // TODO(skuhne): With the removal of the legacy shelf layout we should remove
1935 // the ability to move the app list item since this was never used. We should
1936 // instead ask the ShelfModel::ValidateInsertionIndex or similir for an index.
1940 for (int i
= model_
->item_count() - 1; i
> 0; --i
) {
1941 ash::ShelfItemType type
= model_
->items()[i
].type
;
1942 if (type
== ash::TYPE_APP_SHORTCUT
||
1943 (is_app_list
&& type
== ash::TYPE_APP_LIST
) ||
1944 type
== ash::TYPE_BROWSER_SHORTCUT
) {
1951 int ChromeLauncherController::GetChromeIconIndexForCreation() {
1952 // We get the list of pinned apps as they currently would get pinned.
1953 // Within this list the chrome icon will be the correct location.
1954 std::vector
<std::string
> pinned_apps
= GetListOfPinnedAppsAndBrowser();
1956 std::vector
<std::string
>::iterator it
=
1957 std::find(pinned_apps
.begin(),
1959 std::string(extension_misc::kChromeAppId
));
1960 DCHECK(it
!= pinned_apps
.end());
1961 int index
= it
- pinned_apps
.begin();
1963 // We should do here a comparison between the is state and the "want to be"
1964 // state since some apps might be able to pin but are not yet. Instead - for
1965 // the time being we clamp against the amount of known items and wait for the
1966 // next |UpdateAppLaunchersFromPref()| call to correct it - it will come since
1967 // the pinning will be done then.
1968 return std::min(model_
->item_count(), index
);
1971 std::vector
<std::string
>
1972 ChromeLauncherController::GetListOfPinnedAppsAndBrowser() {
1973 // Adding the app list item to the list of items requires that the ID is not
1974 // a valid and known ID for the extension system. The ID was constructed that
1975 // way - but just to make sure...
1976 DCHECK(!app_tab_helper_
->IsValidIDForCurrentUser(kAppShelfIdPlaceholder
));
1978 std::vector
<std::string
> pinned_apps
;
1980 // Get the new incarnation of the list.
1981 const base::ListValue
* pinned_apps_pref
=
1982 profile_
->GetPrefs()->GetList(prefs::kPinnedLauncherApps
);
1984 // Keep track of the addition of the chrome and the app list icon.
1985 bool chrome_icon_added
= false;
1986 bool app_list_icon_added
= false;
1987 size_t chrome_icon_index
= GetChromeIconIndexFromPref();
1989 // See if the chrome string is already in the pinned list and remove it if
1991 base::Value
* chrome_app
= ash::CreateAppDict(extension_misc::kChromeAppId
);
1993 chrome_icon_added
= pinned_apps_pref
->Find(*chrome_app
) !=
1994 pinned_apps_pref
->end();
1998 for (size_t index
= 0; index
< pinned_apps_pref
->GetSize(); ++index
) {
1999 // We need to position the chrome icon relative to it's place in the pinned
2000 // preference list - even if an item of that list isn't shown yet.
2001 if (index
== chrome_icon_index
&& !chrome_icon_added
) {
2002 pinned_apps
.push_back(extension_misc::kChromeAppId
);
2003 chrome_icon_added
= true;
2005 const base::DictionaryValue
* app
= NULL
;
2007 if (pinned_apps_pref
->GetDictionary(index
, &app
) &&
2008 app
->GetString(ash::kPinnedAppsPrefAppIDPath
, &app_id
) &&
2009 (std::find(pinned_apps
.begin(), pinned_apps
.end(), app_id
) ==
2010 pinned_apps
.end())) {
2011 if (app_id
== extension_misc::kChromeAppId
) {
2012 chrome_icon_added
= true;
2013 pinned_apps
.push_back(extension_misc::kChromeAppId
);
2014 } else if (app_id
== kAppShelfIdPlaceholder
) {
2015 app_list_icon_added
= true;
2016 pinned_apps
.push_back(kAppShelfIdPlaceholder
);
2017 } else if (app_tab_helper_
->IsValidIDForCurrentUser(app_id
)) {
2018 // Note: In multi profile scenarios we only want to show pinnable apps
2019 // here which is correct. Running applications from the other users will
2020 // continue to run. So no need for multi profile modifications.
2021 pinned_apps
.push_back(app_id
);
2026 // If not added yet, the chrome item will be the last item in the list.
2027 if (!chrome_icon_added
)
2028 pinned_apps
.push_back(extension_misc::kChromeAppId
);
2030 // If not added yet, add the app list item either at the end or at the
2031 // beginning - depending on the shelf layout.
2032 if (!app_list_icon_added
) {
2033 pinned_apps
.insert(pinned_apps
.begin(), kAppShelfIdPlaceholder
);
2038 bool ChromeLauncherController::IsIncognito(
2039 const content::WebContents
* web_contents
) const {
2040 const Profile
* profile
=
2041 Profile::FromBrowserContext(web_contents
->GetBrowserContext());
2042 return profile
->IsOffTheRecord() && !profile
->IsGuestSession() &&
2043 !profile
->IsSystemProfile();
2046 void ChromeLauncherController::CloseWindowedAppsFromRemovedExtension(
2047 const std::string
& app_id
,
2048 const Profile
* profile
) {
2049 // This function cannot rely on the controller's enumeration functionality
2050 // since the extension has already be unloaded.
2051 const BrowserList
* ash_browser_list
=
2052 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH
);
2053 std::vector
<Browser
*> browser_to_close
;
2054 for (BrowserList::const_reverse_iterator
2055 it
= ash_browser_list
->begin_last_active();
2056 it
!= ash_browser_list
->end_last_active(); ++it
) {
2057 Browser
* browser
= *it
;
2058 if (!browser
->is_type_tabbed() && browser
->is_type_popup() &&
2059 browser
->is_app() &&
2061 web_app::GetExtensionIdFromApplicationName(browser
->app_name()) &&
2062 profile
== browser
->profile()) {
2063 browser_to_close
.push_back(browser
);
2066 while (!browser_to_close
.empty()) {
2067 TabStripModel
* tab_strip
= browser_to_close
.back()->tab_strip_model();
2068 tab_strip
->CloseWebContentsAt(0, TabStripModel::CLOSE_NONE
);
2069 browser_to_close
.pop_back();
2073 void ChromeLauncherController::SetShelfItemDelegate(
2075 ash::ShelfItemDelegate
* item_delegate
) {
2077 DCHECK(item_delegate
);
2078 DCHECK(item_delegate_manager_
);
2079 item_delegate_manager_
->SetShelfItemDelegate(
2080 id
, scoped_ptr
<ash::ShelfItemDelegate
>(item_delegate
).Pass());
2083 void ChromeLauncherController::AttachProfile(Profile
* profile
) {
2085 // Either add the profile to the list of known profiles and make it the active
2086 // one for some functions of AppTabHelper or create a new one.
2087 if (!app_tab_helper_
.get())
2088 app_tab_helper_
.reset(new LauncherAppTabHelper(profile_
));
2090 app_tab_helper_
->SetCurrentUser(profile_
);
2091 // TODO(skuhne): The AppIconLoaderImpl has the same problem. Each loaded
2092 // image is associated with a profile (it's loader requires the profile).
2093 // Since icon size changes are possible, the icon could be requested to be
2094 // reloaded. However - having it not multi profile aware would cause problems
2095 // if the icon cache gets deleted upon user switch.
2096 app_icon_loader_
.reset(new extensions::AppIconLoaderImpl(
2097 profile_
, extension_misc::EXTENSION_ICON_SMALL
, this));
2099 pref_change_registrar_
.Init(profile_
->GetPrefs());
2100 pref_change_registrar_
.Add(
2101 prefs::kPinnedLauncherApps
,
2102 base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref
,
2103 base::Unretained(this)));
2104 pref_change_registrar_
.Add(
2105 prefs::kShelfAlignmentLocal
,
2106 base::Bind(&ChromeLauncherController::SetShelfAlignmentFromPrefs
,
2107 base::Unretained(this)));
2108 pref_change_registrar_
.Add(
2109 prefs::kShelfAutoHideBehaviorLocal
,
2110 base::Bind(&ChromeLauncherController::
2111 SetShelfAutoHideBehaviorFromPrefs
,
2112 base::Unretained(this)));
2113 pref_change_registrar_
.Add(
2114 prefs::kShelfPreferences
,
2115 base::Bind(&ChromeLauncherController::SetShelfBehaviorsFromPrefs
,
2116 base::Unretained(this)));
2117 #if defined(OS_CHROMEOS)
2118 pref_change_registrar_
.Add(
2119 prefs::kTouchVirtualKeyboardEnabled
,
2120 base::Bind(&ChromeLauncherController::SetVirtualKeyboardBehaviorFromPrefs
,
2121 base::Unretained(this)));
2122 #endif // defined(OS_CHROMEOS)
2124 extensions::ExtensionRegistry::Get(profile_
)->AddObserver(this);
2127 void ChromeLauncherController::ReleaseProfile() {
2128 if (app_sync_ui_state_
)
2129 app_sync_ui_state_
->RemoveObserver(this);
2131 extensions::ExtensionRegistry::Get(profile_
)->RemoveObserver(this);
2133 PrefServiceSyncable::FromProfile(profile_
)->RemoveObserver(this);
2135 pref_change_registrar_
.RemoveAll();