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_util.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 "components/syncable_prefs/pref_service_syncable.h"
72 #include "content/public/browser/navigation_entry.h"
73 #include "content/public/browser/web_contents.h"
74 #include "extensions/browser/extension_prefs.h"
75 #include "extensions/browser/extension_registry.h"
76 #include "extensions/browser/extension_system.h"
77 #include "extensions/browser/extension_util.h"
78 #include "extensions/common/constants.h"
79 #include "extensions/common/extension.h"
80 #include "extensions/common/extension_resource.h"
81 #include "extensions/common/manifest_handlers/icons_handler.h"
82 #include "extensions/common/url_pattern.h"
83 #include "grit/ash_resources.h"
84 #include "grit/theme_resources.h"
85 #include "net/base/url_util.h"
86 #include "ui/aura/window.h"
87 #include "ui/aura/window_event_dispatcher.h"
88 #include "ui/base/l10n/l10n_util.h"
89 #include "ui/base/window_open_disposition.h"
90 #include "ui/keyboard/keyboard_util.h"
91 #include "ui/resources/grit/ui_resources.h"
92 #include "ui/wm/core/window_animations.h"
94 #if defined(OS_CHROMEOS)
95 #include "chrome/browser/browser_process.h"
96 #include "chrome/browser/ui/ash/chrome_shell_delegate.h"
97 #include "chrome/browser/ui/ash/launcher/multi_profile_app_window_launcher_controller.h"
98 #include "chrome/browser/ui/ash/launcher/multi_profile_browser_status_monitor.h"
99 #include "components/user_manager/user_manager.h"
102 using extensions::Extension
;
103 using extensions::UnloadedExtensionInfo
;
104 using extension_misc::kGmailAppId
;
105 using content::WebContents
;
108 ChromeLauncherController
* ChromeLauncherController::instance_
= NULL
;
112 // This will be used as placeholder in the list of the pinned applciatons.
113 // Note that this is NOT a valid extension identifier so that pre M31 versions
115 const char kAppShelfIdPlaceholder
[] = "AppShelfIDPlaceholder--------";
117 std::string
GetPrefKeyForRootWindow(aura::Window
* root_window
) {
118 gfx::Display display
= gfx::Screen::GetScreenFor(
119 root_window
)->GetDisplayNearestWindow(root_window
);
120 DCHECK(display
.is_valid());
122 return base::Int64ToString(display
.id());
125 void UpdatePerDisplayPref(PrefService
* pref_service
,
126 aura::Window
* root_window
,
127 const char* pref_key
,
128 const std::string
& value
) {
129 std::string key
= GetPrefKeyForRootWindow(root_window
);
133 DictionaryPrefUpdate
update(pref_service
, prefs::kShelfPreferences
);
134 base::DictionaryValue
* shelf_prefs
= update
.Get();
135 base::DictionaryValue
* prefs
= NULL
;
136 if (!shelf_prefs
->GetDictionary(key
, &prefs
)) {
137 prefs
= new base::DictionaryValue();
138 shelf_prefs
->Set(key
, prefs
);
140 prefs
->SetStringWithoutPathExpansion(pref_key
, value
);
143 // Returns a pref value in |pref_service| for the display of |root_window|. The
144 // pref value is stored in |local_path| and |path|, but |pref_service| may have
145 // per-display preferences and the value can be specified by policy. Here is
147 // * A value managed by policy. This is a single value that applies to all
149 // * A user-set value for the specified display.
150 // * A user-set value in |local_path| or |path|, if no per-display settings are
151 // ever specified (see http://crbug.com/173719 for why). |local_path| is
152 // preferred. See comment in |kShelfAlignment| as to why we consider two
153 // prefs and why |local_path| is preferred.
154 // * A value recommended by policy. This is a single value that applies to all
156 // * The default value for |local_path| if the value is not recommended by
158 std::string
GetPrefForRootWindow(PrefService
* pref_service
,
159 aura::Window
* root_window
,
160 const char* local_path
,
162 const PrefService::Preference
* local_pref
=
163 pref_service
->FindPreference(local_path
);
164 const std::string
value(pref_service
->GetString(local_path
));
165 if (local_pref
->IsManaged())
168 std::string pref_key
= GetPrefKeyForRootWindow(root_window
);
169 bool has_per_display_prefs
= false;
170 if (!pref_key
.empty()) {
171 const base::DictionaryValue
* shelf_prefs
= pref_service
->GetDictionary(
172 prefs::kShelfPreferences
);
173 const base::DictionaryValue
* display_pref
= NULL
;
174 std::string per_display_value
;
175 if (shelf_prefs
->GetDictionary(pref_key
, &display_pref
) &&
176 display_pref
->GetString(path
, &per_display_value
))
177 return per_display_value
;
179 // If the pref for the specified display is not found, scan the whole prefs
180 // and check if the prefs for other display is already specified.
181 std::string unused_value
;
182 for (base::DictionaryValue::Iterator
iter(*shelf_prefs
);
183 !iter
.IsAtEnd(); iter
.Advance()) {
184 const base::DictionaryValue
* display_pref
= NULL
;
185 if (iter
.value().GetAsDictionary(&display_pref
) &&
186 display_pref
->GetString(path
, &unused_value
)) {
187 has_per_display_prefs
= true;
193 if (local_pref
->IsRecommended() || !has_per_display_prefs
)
196 const base::Value
* default_value
=
197 pref_service
->GetDefaultPrefValue(local_path
);
198 std::string default_string
;
199 default_value
->GetAsString(&default_string
);
200 return default_string
;
203 // Gets the shelf auto hide behavior from prefs for a root window.
204 ash::ShelfAutoHideBehavior
GetShelfAutoHideBehaviorFromPrefs(
206 aura::Window
* root_window
) {
209 // Don't show the shelf in app mode.
210 if (chrome::IsRunningInAppMode())
211 return ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN
;
213 // See comment in |kShelfAlignment| as to why we consider two prefs.
214 const std::string
behavior_value(
215 GetPrefForRootWindow(profile
->GetPrefs(),
217 prefs::kShelfAutoHideBehaviorLocal
,
218 prefs::kShelfAutoHideBehavior
));
220 // Note: To maintain sync compatibility with old images of chrome/chromeos
221 // the set of values that may be encountered includes the now-extinct
222 // "Default" as well as "Never" and "Always", "Default" should now
223 // be treated as "Never" (http://crbug.com/146773).
224 if (behavior_value
== ash::kShelfAutoHideBehaviorAlways
)
225 return ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS
;
226 return ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER
;
229 // Gets the shelf alignment from prefs for a root window.
230 ash::ShelfAlignment
GetShelfAlignmentFromPrefs(Profile
* profile
,
231 aura::Window
* root_window
) {
234 // See comment in |kShelfAlignment| as to why we consider two prefs.
235 const std::string
alignment_value(
236 GetPrefForRootWindow(profile
->GetPrefs(),
238 prefs::kShelfAlignmentLocal
,
239 prefs::kShelfAlignment
));
240 if (alignment_value
== ash::kShelfAlignmentLeft
)
241 return ash::SHELF_ALIGNMENT_LEFT
;
242 else if (alignment_value
== ash::kShelfAlignmentRight
)
243 return ash::SHELF_ALIGNMENT_RIGHT
;
244 else if (alignment_value
== ash::kShelfAlignmentTop
)
245 return ash::SHELF_ALIGNMENT_TOP
;
246 return ash::SHELF_ALIGNMENT_BOTTOM
;
249 // If prefs have synced and no user-set value exists at |local_path|, the value
250 // from |synced_path| is copied to |local_path|.
251 void MaybePropagatePrefToLocal(
252 syncable_prefs::PrefServiceSyncable
* pref_service
,
253 const char* local_path
,
254 const char* synced_path
) {
255 if (!pref_service
->FindPreference(local_path
)->HasUserSetting() &&
256 pref_service
->IsSyncing()) {
257 // First time the user is using this machine, propagate from remote to
259 pref_service
->SetString(local_path
, pref_service
->GetString(synced_path
));
263 std::string
GetSourceFromAppListSource(ash::LaunchSource source
) {
265 case ash::LAUNCH_FROM_APP_LIST
:
266 return std::string(extension_urls::kLaunchSourceAppList
);
267 case ash::LAUNCH_FROM_APP_LIST_SEARCH
:
268 return std::string(extension_urls::kLaunchSourceAppListSearch
);
269 default: return std::string();
275 #if defined(OS_CHROMEOS)
276 // A class to get events from ChromeOS when a user gets changed or added.
277 class ChromeLauncherControllerUserSwitchObserver
278 : public user_manager::UserManager::UserSessionStateObserver
{
280 ChromeLauncherControllerUserSwitchObserver(
281 ChromeLauncherController
* controller
)
282 : controller_(controller
) {
283 DCHECK(user_manager::UserManager::IsInitialized());
284 user_manager::UserManager::Get()->AddSessionStateObserver(this);
286 ~ChromeLauncherControllerUserSwitchObserver() override
{
287 user_manager::UserManager::Get()->RemoveSessionStateObserver(this);
290 // user_manager::UserManager::UserSessionStateObserver overrides:
291 void UserAddedToSession(const user_manager::User
* added_user
) override
;
293 // ChromeLauncherControllerUserSwitchObserver:
294 void OnUserProfileReadyToSwitch(Profile
* profile
);
297 // Add a user to the session.
298 void AddUser(Profile
* profile
);
300 // The owning ChromeLauncherController.
301 ChromeLauncherController
* controller_
;
303 // Users which were just added to the system, but which profiles were not yet
305 std::set
<std::string
> added_user_ids_waiting_for_profiles_
;
307 DISALLOW_COPY_AND_ASSIGN(ChromeLauncherControllerUserSwitchObserver
);
310 void ChromeLauncherControllerUserSwitchObserver::UserAddedToSession(
311 const user_manager::User
* active_user
) {
312 Profile
* profile
= multi_user_util::GetProfileFromUserID(
313 active_user
->email());
314 // If we do not have a profile yet, we postpone forwarding the notification
315 // until it is loaded.
317 added_user_ids_waiting_for_profiles_
.insert(active_user
->email());
322 void ChromeLauncherControllerUserSwitchObserver::OnUserProfileReadyToSwitch(
324 if (!added_user_ids_waiting_for_profiles_
.empty()) {
325 // Check if the profile is from a user which was on the waiting list.
326 std::string user_id
= multi_user_util::GetUserIDFromProfile(profile
);
327 std::set
<std::string
>::iterator it
= std::find(
328 added_user_ids_waiting_for_profiles_
.begin(),
329 added_user_ids_waiting_for_profiles_
.end(),
331 if (it
!= added_user_ids_waiting_for_profiles_
.end()) {
332 added_user_ids_waiting_for_profiles_
.erase(it
);
333 AddUser(profile
->GetOriginalProfile());
338 void ChromeLauncherControllerUserSwitchObserver::AddUser(Profile
* profile
) {
339 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
340 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED
)
341 chrome::MultiUserWindowManager::GetInstance()->AddUser(profile
);
342 controller_
->AdditionalUserAddedToSession(profile
->GetOriginalProfile());
346 ChromeLauncherController::ChromeLauncherController(Profile
* profile
,
347 ash::ShelfModel
* model
)
349 item_delegate_manager_(NULL
),
351 app_sync_ui_state_(NULL
),
352 ignore_persist_pinned_state_change_(false) {
354 // If no profile was passed, we take the currently active profile and use it
355 // as the owner of the current desktop.
356 // Use the original profile as on chromeos we may get a temporary off the
357 // record profile, unless in guest session (where off the record profile is
359 profile_
= ProfileManager::GetActiveUserProfile();
360 if (!profile_
->IsGuestSession() && !profile_
->IsSystemProfile())
361 profile_
= profile_
->GetOriginalProfile();
363 app_sync_ui_state_
= AppSyncUIState::Get(profile_
);
364 if (app_sync_ui_state_
)
365 app_sync_ui_state_
->AddObserver(this);
368 // All profile relevant settings get bound to the current profile.
369 AttachProfile(profile_
);
370 model_
->AddObserver(this);
372 // In multi profile mode we might have a window manager. We try to create it
373 // here. If the instantiation fails, the manager is not needed.
374 chrome::MultiUserWindowManager::CreateInstance();
376 #if defined(OS_CHROMEOS)
377 // On Chrome OS using multi profile we want to switch the content of the shelf
378 // with a user change. Note that for unit tests the instance can be NULL.
379 if (chrome::MultiUserWindowManager::GetMultiProfileMode() !=
380 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_OFF
) {
381 user_switch_observer_
.reset(
382 new ChromeLauncherControllerUserSwitchObserver(this));
385 // Create our v1/v2 application / browser monitors which will inform the
386 // launcher of status changes.
387 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
388 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED
) {
389 // If running in separated destkop mode, we create the multi profile version
390 // of status monitor.
391 browser_status_monitor_
.reset(new MultiProfileBrowserStatusMonitor(this));
392 app_window_controller_
.reset(
393 new MultiProfileAppWindowLauncherController(this));
395 // Create our v1/v2 application / browser monitors which will inform the
396 // launcher of status changes.
397 browser_status_monitor_
.reset(new BrowserStatusMonitor(this));
398 app_window_controller_
.reset(new AppWindowLauncherController(this));
401 // Create our v1/v2 application / browser monitors which will inform the
402 // launcher of status changes.
403 browser_status_monitor_
.reset(new BrowserStatusMonitor(this));
404 app_window_controller_
.reset(new AppWindowLauncherController(this));
407 // Right now ash::Shell isn't created for tests.
408 // TODO(mukai): Allows it to observe display change and write tests.
409 if (ash::Shell::HasInstance()) {
410 ash::Shell::GetInstance()->window_tree_host_manager()->AddObserver(this);
411 // If it got already set, we remove the observer first again and swap the
412 // ItemDelegateManager.
413 if (item_delegate_manager_
)
414 item_delegate_manager_
->RemoveObserver(this);
415 item_delegate_manager_
=
416 ash::Shell::GetInstance()->shelf_item_delegate_manager();
417 item_delegate_manager_
->AddObserver(this);
421 ChromeLauncherController::~ChromeLauncherController() {
422 if (item_delegate_manager_
)
423 item_delegate_manager_
->RemoveObserver(this);
425 // Reset the BrowserStatusMonitor as it has a weak pointer to this.
426 browser_status_monitor_
.reset();
428 // Reset the app window controller here since it has a weak pointer to this.
429 app_window_controller_
.reset();
431 for (std::set
<ash::Shelf
*>::iterator iter
= shelves_
.begin();
432 iter
!= shelves_
.end();
434 (*iter
)->shelf_widget()->shelf_layout_manager()->RemoveObserver(this);
436 model_
->RemoveObserver(this);
437 if (ash::Shell::HasInstance())
438 ash::Shell::GetInstance()->window_tree_host_manager()->RemoveObserver(this);
439 for (IDToItemControllerMap::iterator i
= id_to_item_controller_map_
.begin();
440 i
!= id_to_item_controller_map_
.end(); ++i
) {
441 int index
= model_
->ItemIndexByID(i
->first
);
442 // A "browser proxy" is not known to the model and this removal does
443 // therefore not need to be propagated to the model.
445 model_
->items()[index
].type
!= ash::TYPE_BROWSER_SHORTCUT
)
446 model_
->RemoveItemAt(index
);
449 if (ash::Shell::HasInstance())
450 ash::Shell::GetInstance()->RemoveShellObserver(this);
452 // Release all profile dependent resources.
454 if (instance_
== this)
457 // Get rid of the multi user window manager instance.
458 chrome::MultiUserWindowManager::DeleteInstance();
462 ChromeLauncherController
* ChromeLauncherController::CreateInstance(
464 ash::ShelfModel
* model
) {
465 // We do not check here for re-creation of the ChromeLauncherController since
466 // it appears that it might be intentional that the ChromeLauncherController
467 // can be re-created.
468 instance_
= new ChromeLauncherController(profile
, model
);
472 void ChromeLauncherController::Init() {
473 CreateBrowserShortcutLauncherItem();
474 UpdateAppLaunchersFromPref();
476 // TODO(sky): update unit test so that this test isn't necessary.
477 if (ash::Shell::HasInstance()) {
478 SetShelfAutoHideBehaviorFromPrefs();
479 SetShelfAlignmentFromPrefs();
480 #if defined(OS_CHROMEOS)
481 SetVirtualKeyboardBehaviorFromPrefs();
482 #endif // defined(OS_CHROMEOS)
483 syncable_prefs::PrefServiceSyncable
* prefs
=
484 PrefServiceSyncableFromProfile(profile_
);
485 if (!prefs
->FindPreference(prefs::kShelfAlignmentLocal
)->HasUserSetting() ||
486 !prefs
->FindPreference(prefs::kShelfAutoHideBehaviorLocal
)->
488 // This causes OnIsSyncingChanged to be called when the value of
489 // PrefService::IsSyncing() changes.
490 prefs
->AddObserver(this);
492 ash::Shell::GetInstance()->AddShellObserver(this);
496 ash::ShelfID
ChromeLauncherController::CreateAppLauncherItem(
497 LauncherItemController
* controller
,
498 const std::string
& app_id
,
499 ash::ShelfItemStatus status
) {
502 // Panels are inserted on the left so as not to push all existing panels over.
503 if (controller
->GetShelfItemType() != ash::TYPE_APP_PANEL
)
504 index
= model_
->item_count();
505 return InsertAppLauncherItem(controller
,
509 controller
->GetShelfItemType());
512 void ChromeLauncherController::SetItemStatus(ash::ShelfID id
,
513 ash::ShelfItemStatus status
) {
514 int index
= model_
->ItemIndexByID(id
);
515 ash::ShelfItemStatus old_status
= model_
->items()[index
].status
;
516 // Since ordinary browser windows are not registered, we might get a negative
518 if (index
>= 0 && old_status
!= status
) {
519 ash::ShelfItem item
= model_
->items()[index
];
520 item
.status
= status
;
521 model_
->Set(index
, item
);
525 void ChromeLauncherController::SetItemController(
527 LauncherItemController
* controller
) {
529 IDToItemControllerMap::iterator iter
= id_to_item_controller_map_
.find(id
);
530 CHECK(iter
!= id_to_item_controller_map_
.end());
531 controller
->set_shelf_id(id
);
532 iter
->second
= controller
;
533 // Existing controller is destroyed and replaced by registering again.
534 SetShelfItemDelegate(id
, controller
);
537 void ChromeLauncherController::CloseLauncherItem(ash::ShelfID id
) {
540 // Create a new shortcut controller.
541 IDToItemControllerMap::iterator iter
= id_to_item_controller_map_
.find(id
);
542 CHECK(iter
!= id_to_item_controller_map_
.end());
543 SetItemStatus(id
, ash::STATUS_CLOSED
);
544 std::string app_id
= iter
->second
->app_id();
545 iter
->second
= new AppShortcutLauncherItemController(app_id
, this);
546 iter
->second
->set_shelf_id(id
);
547 // Existing controller is destroyed and replaced by registering again.
548 SetShelfItemDelegate(id
, iter
->second
);
550 LauncherItemClosed(id
);
554 void ChromeLauncherController::Pin(ash::ShelfID id
) {
555 DCHECK(HasShelfIDToAppIDMapping(id
));
557 int index
= model_
->ItemIndexByID(id
);
560 ash::ShelfItem item
= model_
->items()[index
];
562 if (item
.type
== ash::TYPE_PLATFORM_APP
||
563 item
.type
== ash::TYPE_WINDOWED_APP
) {
564 item
.type
= ash::TYPE_APP_SHORTCUT
;
565 model_
->Set(index
, item
);
566 } else if (item
.type
!= ash::TYPE_APP_SHORTCUT
) {
571 PersistPinnedState();
574 void ChromeLauncherController::Unpin(ash::ShelfID id
) {
575 LauncherItemController
* controller
= GetLauncherItemController(id
);
578 if (controller
->type() == LauncherItemController::TYPE_APP
||
579 controller
->locked()) {
580 UnpinRunningAppInternal(model_
->ItemIndexByID(id
));
582 LauncherItemClosed(id
);
585 PersistPinnedState();
588 bool ChromeLauncherController::IsPinned(ash::ShelfID id
) {
589 int index
= model_
->ItemIndexByID(id
);
592 ash::ShelfItemType type
= model_
->items()[index
].type
;
593 return (type
== ash::TYPE_APP_SHORTCUT
|| type
== ash::TYPE_BROWSER_SHORTCUT
);
596 void ChromeLauncherController::TogglePinned(ash::ShelfID id
) {
597 if (!HasShelfIDToAppIDMapping(id
))
598 return; // May happen if item closed with menu open.
606 bool ChromeLauncherController::IsPinnable(ash::ShelfID id
) const {
607 int index
= model_
->ItemIndexByID(id
);
611 ash::ShelfItemType type
= model_
->items()[index
].type
;
612 return ((type
== ash::TYPE_APP_SHORTCUT
||
613 type
== ash::TYPE_PLATFORM_APP
||
614 type
== ash::TYPE_WINDOWED_APP
) &&
618 void ChromeLauncherController::LockV1AppWithID(const std::string
& app_id
) {
619 ash::ShelfID id
= GetShelfIDForAppID(app_id
);
620 if (!IsPinned(id
) && !IsWindowedAppInLauncher(app_id
)) {
621 CreateAppShortcutLauncherItemWithType(app_id
,
622 model_
->item_count(),
623 ash::TYPE_WINDOWED_APP
);
624 id
= GetShelfIDForAppID(app_id
);
627 id_to_item_controller_map_
[id
]->lock();
630 void ChromeLauncherController::UnlockV1AppWithID(const std::string
& app_id
) {
631 ash::ShelfID id
= GetShelfIDForAppID(app_id
);
633 CHECK(IsPinned(id
) || IsWindowedAppInLauncher(app_id
));
634 LauncherItemController
* controller
= id_to_item_controller_map_
[id
];
635 controller
->unlock();
636 if (!controller
->locked() && !IsPinned(id
))
637 CloseLauncherItem(id
);
640 void ChromeLauncherController::Launch(ash::ShelfID id
, int event_flags
) {
641 LauncherItemController
* controller
= GetLauncherItemController(id
);
643 return; // In case invoked from menu and item closed while menu up.
644 controller
->Launch(ash::LAUNCH_FROM_UNKNOWN
, event_flags
);
647 void ChromeLauncherController::Close(ash::ShelfID id
) {
648 LauncherItemController
* controller
= GetLauncherItemController(id
);
650 return; // May happen if menu closed.
654 bool ChromeLauncherController::IsOpen(ash::ShelfID id
) {
655 LauncherItemController
* controller
= GetLauncherItemController(id
);
658 return controller
->IsOpen();
661 bool ChromeLauncherController::IsPlatformApp(ash::ShelfID id
) {
662 if (!HasShelfIDToAppIDMapping(id
))
665 std::string app_id
= GetAppIDForShelfID(id
);
666 const Extension
* extension
= GetExtensionForAppID(app_id
);
667 // An extension can be synced / updated at any time and therefore not be
669 return extension
? extension
->is_platform_app() : false;
672 void ChromeLauncherController::LaunchApp(const std::string
& app_id
,
673 ash::LaunchSource source
,
675 // |extension| could be NULL when it is being unloaded for updating.
676 const Extension
* extension
= GetExtensionForAppID(app_id
);
680 if (!extensions::util::IsAppLaunchableWithoutEnabling(app_id
, profile_
)) {
681 // Do nothing if there is already a running enable flow.
682 if (extension_enable_flow_
)
685 extension_enable_flow_
.reset(
686 new ExtensionEnableFlow(profile_
, app_id
, this));
687 extension_enable_flow_
->StartForNativeWindow(NULL
);
692 if (LaunchedInNativeDesktop(app_id
))
696 // The app will be created for the currently active profile.
697 AppLaunchParams
params(
698 profile_
, extension
, ui::DispositionFromEventFlags(event_flags
),
699 chrome::HOST_DESKTOP_TYPE_ASH
, extensions::SOURCE_APP_LAUNCHER
);
700 if (source
!= ash::LAUNCH_FROM_UNKNOWN
&&
701 app_id
== extensions::kWebStoreAppId
) {
702 // Get the corresponding source string.
703 std::string source_value
= GetSourceFromAppListSource(source
);
705 // Set an override URL to include the source.
706 GURL extension_url
= extensions::AppLaunchInfo::GetFullLaunchURL(extension
);
707 params
.override_url
= net::AppendQueryParameter(
708 extension_url
, extension_urls::kWebstoreSourceField
, source_value
);
711 OpenApplication(params
);
714 void ChromeLauncherController::ActivateApp(const std::string
& app_id
,
715 ash::LaunchSource source
,
717 // If there is an existing non-shortcut controller for this app, open it.
718 ash::ShelfID id
= GetShelfIDForAppID(app_id
);
720 LauncherItemController
* controller
= GetLauncherItemController(id
);
721 controller
->Activate(source
);
725 // Create a temporary application launcher item and use it to see if there are
726 // running instances.
727 scoped_ptr
<AppShortcutLauncherItemController
> app_controller(
728 new AppShortcutLauncherItemController(app_id
, this));
729 if (!app_controller
->GetRunningApplications().empty())
730 app_controller
->Activate(source
);
732 LaunchApp(app_id
, source
, event_flags
);
735 extensions::LaunchType
ChromeLauncherController::GetLaunchType(
737 const Extension
* extension
= GetExtensionForAppID(GetAppIDForShelfID(id
));
739 // An extension can be unloaded/updated/unavailable at any time.
741 return extensions::LAUNCH_TYPE_DEFAULT
;
743 return extensions::GetLaunchType(extensions::ExtensionPrefs::Get(profile_
),
747 ash::ShelfID
ChromeLauncherController::GetShelfIDForAppID(
748 const std::string
& app_id
) {
749 for (IDToItemControllerMap::const_iterator i
=
750 id_to_item_controller_map_
.begin();
751 i
!= id_to_item_controller_map_
.end(); ++i
) {
752 if (i
->second
->type() == LauncherItemController::TYPE_APP_PANEL
)
753 continue; // Don't include panels
754 if (i
->second
->app_id() == app_id
)
760 bool ChromeLauncherController::HasShelfIDToAppIDMapping(ash::ShelfID id
) const {
761 return id_to_item_controller_map_
.find(id
) !=
762 id_to_item_controller_map_
.end();
765 const std::string
& ChromeLauncherController::GetAppIDForShelfID(
767 LauncherItemController
* controller
= GetLauncherItemController(id
);
769 return controller
->app_id();
772 void ChromeLauncherController::SetAppImage(const std::string
& id
,
773 const gfx::ImageSkia
& image
) {
774 // TODO: need to get this working for shortcuts.
775 for (IDToItemControllerMap::const_iterator i
=
776 id_to_item_controller_map_
.begin();
777 i
!= id_to_item_controller_map_
.end(); ++i
) {
778 LauncherItemController
* controller
= i
->second
;
779 if (controller
->app_id() != id
)
781 if (controller
->image_set_by_controller())
783 int index
= model_
->ItemIndexByID(i
->first
);
786 ash::ShelfItem item
= model_
->items()[index
];
788 model_
->Set(index
, item
);
789 // It's possible we're waiting on more than one item, so don't break.
793 void ChromeLauncherController::OnAutoHideBehaviorChanged(
794 aura::Window
* root_window
,
795 ash::ShelfAutoHideBehavior new_behavior
) {
796 SetShelfAutoHideBehaviorPrefs(new_behavior
, root_window
);
799 void ChromeLauncherController::SetLauncherItemImage(
800 ash::ShelfID shelf_id
,
801 const gfx::ImageSkia
& image
) {
802 int index
= model_
->ItemIndexByID(shelf_id
);
805 ash::ShelfItem item
= model_
->items()[index
];
807 model_
->Set(index
, item
);
810 bool ChromeLauncherController::CanPin() const {
811 const PrefService::Preference
* pref
=
812 profile_
->GetPrefs()->FindPreference(prefs::kPinnedLauncherApps
);
813 return pref
&& pref
->IsUserModifiable();
816 bool ChromeLauncherController::IsAppPinned(const std::string
& app_id
) {
817 for (IDToItemControllerMap::const_iterator i
=
818 id_to_item_controller_map_
.begin();
819 i
!= id_to_item_controller_map_
.end(); ++i
) {
820 if (IsPinned(i
->first
) && i
->second
->app_id() == app_id
)
826 bool ChromeLauncherController::IsWindowedAppInLauncher(
827 const std::string
& app_id
) {
828 int index
= model_
->ItemIndexByID(GetShelfIDForAppID(app_id
));
832 ash::ShelfItemType type
= model_
->items()[index
].type
;
833 return type
== ash::TYPE_WINDOWED_APP
;
836 void ChromeLauncherController::PinAppWithID(const std::string
& app_id
) {
838 DoPinAppWithID(app_id
);
843 void ChromeLauncherController::SetLaunchType(
845 extensions::LaunchType launch_type
) {
846 LauncherItemController
* controller
= GetLauncherItemController(id
);
850 extensions::SetLaunchType(profile_
, controller
->app_id(), launch_type
);
853 void ChromeLauncherController::UnpinAppWithID(const std::string
& app_id
) {
855 DoUnpinAppWithID(app_id
);
860 void ChromeLauncherController::OnSetShelfItemDelegate(
862 ash::ShelfItemDelegate
* item_delegate
) {
863 // TODO(skuhne): This fixes crbug.com/429870, but it does not answer why we
864 // get into this state in the first place.
865 IDToItemControllerMap::iterator iter
= id_to_item_controller_map_
.find(id
);
866 if (iter
== id_to_item_controller_map_
.end() || item_delegate
== iter
->second
)
868 LOG(ERROR
) << "Unexpected change of shelf item id: " << id
;
869 id_to_item_controller_map_
.erase(iter
);
872 bool ChromeLauncherController::IsLoggedInAsGuest() {
873 return profile_
->IsGuestSession();
876 void ChromeLauncherController::CreateNewWindow() {
877 // Use the currently active user.
878 chrome::NewEmptyWindow(profile_
, chrome::HOST_DESKTOP_TYPE_ASH
);
881 void ChromeLauncherController::CreateNewIncognitoWindow() {
882 // Use the currently active user.
883 chrome::NewEmptyWindow(profile_
->GetOffTheRecordProfile(),
884 chrome::HOST_DESKTOP_TYPE_ASH
);
887 void ChromeLauncherController::PersistPinnedState() {
888 if (ignore_persist_pinned_state_change_
)
890 // It is a coding error to call PersistPinnedState() if the pinned apps are
891 // not user-editable. The code should check earlier and not perform any
892 // modification actions that trigger persisting the state.
894 NOTREACHED() << "Can't pin but pinned state being updated";
897 // Mutating kPinnedLauncherApps is going to notify us and trigger us to
898 // process the change. We don't want that to happen so remove ourselves as a
900 pref_change_registrar_
.Remove(prefs::kPinnedLauncherApps
);
902 ListPrefUpdate
updater(profile_
->GetPrefs(), prefs::kPinnedLauncherApps
);
904 for (size_t i
= 0; i
< model_
->items().size(); ++i
) {
905 if (model_
->items()[i
].type
== ash::TYPE_APP_SHORTCUT
) {
906 ash::ShelfID id
= model_
->items()[i
].id
;
907 LauncherItemController
* controller
= GetLauncherItemController(id
);
908 if (controller
&& IsPinned(id
)) {
909 base::DictionaryValue
* app_value
= ash::CreateAppDict(
910 controller
->app_id());
912 updater
->Append(app_value
);
914 } else if (model_
->items()[i
].type
== ash::TYPE_BROWSER_SHORTCUT
) {
915 PersistChromeItemIndex(i
);
916 } else if (model_
->items()[i
].type
== ash::TYPE_APP_LIST
) {
917 base::DictionaryValue
* app_value
= ash::CreateAppDict(
918 kAppShelfIdPlaceholder
);
920 updater
->Append(app_value
);
924 pref_change_registrar_
.Add(
925 prefs::kPinnedLauncherApps
,
926 base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref
,
927 base::Unretained(this)));
930 ash::ShelfModel
* ChromeLauncherController::model() {
934 Profile
* ChromeLauncherController::profile() {
938 ash::ShelfAutoHideBehavior
ChromeLauncherController::GetShelfAutoHideBehavior(
939 aura::Window
* root_window
) const {
940 return GetShelfAutoHideBehaviorFromPrefs(profile_
, root_window
);
943 bool ChromeLauncherController::CanUserModifyShelfAutoHideBehavior(
944 aura::Window
* root_window
) const {
946 // Disable shelf auto-hide behavior on screen sides in Metro mode.
947 if (ash::Shell::GetInstance()->GetShelfAlignment(root_window
) !=
948 ash::SHELF_ALIGNMENT_BOTTOM
) {
952 return profile_
->GetPrefs()->
953 FindPreference(prefs::kShelfAutoHideBehaviorLocal
)->IsUserModifiable();
956 void ChromeLauncherController::ToggleShelfAutoHideBehavior(
957 aura::Window
* root_window
) {
958 ash::ShelfAutoHideBehavior behavior
= GetShelfAutoHideBehavior(root_window
) ==
959 ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS
?
960 ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER
:
961 ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS
;
962 SetShelfAutoHideBehaviorPrefs(behavior
, root_window
);
966 void ChromeLauncherController::UpdateAppState(content::WebContents
* contents
,
967 AppState app_state
) {
968 std::string app_id
= app_tab_helper_
->GetAppID(contents
);
970 // Check if the gMail app is loaded and it matches the given content.
971 // This special treatment is needed to address crbug.com/234268.
972 if (app_id
.empty() && ContentCanBeHandledByGmailApp(contents
))
973 app_id
= kGmailAppId
;
975 // Check the old |app_id| for a tab. If the contents has changed we need to
976 // remove it from the previous app.
977 if (web_contents_to_app_id_
.find(contents
) != web_contents_to_app_id_
.end()) {
978 std::string last_app_id
= web_contents_to_app_id_
[contents
];
979 if (last_app_id
!= app_id
) {
980 ash::ShelfID id
= GetShelfIDForAppID(last_app_id
);
982 // Since GetAppState() will use |web_contents_to_app_id_| we remove
983 // the connection before calling it.
984 web_contents_to_app_id_
.erase(contents
);
985 SetItemStatus(id
, GetAppState(last_app_id
));
990 if (app_state
== APP_STATE_REMOVED
)
991 web_contents_to_app_id_
.erase(contents
);
993 web_contents_to_app_id_
[contents
] = app_id
;
995 ash::ShelfID id
= GetShelfIDForAppID(app_id
);
997 SetItemStatus(id
, (app_state
== APP_STATE_WINDOW_ACTIVE
||
998 app_state
== APP_STATE_ACTIVE
) ? ash::STATUS_ACTIVE
:
999 GetAppState(app_id
));
1003 ash::ShelfID
ChromeLauncherController::GetShelfIDForWebContents(
1004 content::WebContents
* contents
) {
1007 std::string app_id
= app_tab_helper_
->GetAppID(contents
);
1009 if (app_id
.empty() && ContentCanBeHandledByGmailApp(contents
))
1010 app_id
= kGmailAppId
;
1012 ash::ShelfID id
= GetShelfIDForAppID(app_id
);
1014 if (app_id
.empty() || !id
) {
1015 int browser_index
= model_
->GetItemIndexForType(ash::TYPE_BROWSER_SHORTCUT
);
1016 return model_
->items()[browser_index
].id
;
1022 void ChromeLauncherController::SetRefocusURLPatternForTest(ash::ShelfID id
,
1024 LauncherItemController
* controller
= GetLauncherItemController(id
);
1027 int index
= model_
->ItemIndexByID(id
);
1029 NOTREACHED() << "Invalid launcher id";
1033 ash::ShelfItemType type
= model_
->items()[index
].type
;
1034 if (type
== ash::TYPE_APP_SHORTCUT
|| type
== ash::TYPE_WINDOWED_APP
) {
1035 AppShortcutLauncherItemController
* app_controller
=
1036 static_cast<AppShortcutLauncherItemController
*>(controller
);
1037 app_controller
->set_refocus_url(url
);
1039 NOTREACHED() << "Invalid launcher type";
1043 const Extension
* ChromeLauncherController::GetExtensionForAppID(
1044 const std::string
& app_id
) const {
1045 return extensions::ExtensionRegistry::Get(profile_
)->GetExtensionById(
1046 app_id
, extensions::ExtensionRegistry::EVERYTHING
);
1049 ash::ShelfItemDelegate::PerformedAction
1050 ChromeLauncherController::ActivateWindowOrMinimizeIfActive(
1051 ui::BaseWindow
* window
,
1052 bool allow_minimize
) {
1053 // In separated desktop mode we might have to teleport a window back to the
1055 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
1056 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED
) {
1057 aura::Window
* native_window
= window
->GetNativeWindow();
1058 const std::string
& current_user
=
1059 multi_user_util::GetUserIDFromProfile(profile());
1060 chrome::MultiUserWindowManager
* manager
=
1061 chrome::MultiUserWindowManager::GetInstance();
1062 if (!manager
->IsWindowOnDesktopOfUser(native_window
, current_user
)) {
1063 ash::MultiProfileUMA::RecordTeleportAction(
1064 ash::MultiProfileUMA::TELEPORT_WINDOW_RETURN_BY_LAUNCHER
);
1065 manager
->ShowWindowForUser(native_window
, current_user
);
1067 return ash::ShelfItemDelegate::kExistingWindowActivated
;
1071 if (window
->IsActive() && allow_minimize
) {
1072 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1073 switches::kDisableMinimizeOnSecondLauncherItemClick
)) {
1074 AnimateWindow(window
->GetNativeWindow(),
1075 wm::WINDOW_ANIMATION_TYPE_BOUNCE
);
1078 return ash::ShelfItemDelegate::kExistingWindowMinimized
;
1083 return ash::ShelfItemDelegate::kExistingWindowActivated
;
1085 return ash::ShelfItemDelegate::kNoAction
;
1088 void ChromeLauncherController::OnShelfCreated(ash::Shelf
* shelf
) {
1089 shelves_
.insert(shelf
);
1090 shelf
->shelf_widget()->shelf_layout_manager()->AddObserver(this);
1093 void ChromeLauncherController::OnShelfDestroyed(ash::Shelf
* shelf
) {
1094 shelves_
.erase(shelf
);
1095 // RemoveObserver is not called here, since by the time this method is called
1096 // Shelf is already in its destructor.
1099 void ChromeLauncherController::ShelfItemAdded(int index
) {
1100 // The app list launcher can get added to the shelf after we applied the
1101 // preferences. In that case the item might be at the wrong spot. As such we
1102 // call the function again.
1103 if (model_
->items()[index
].type
== ash::TYPE_APP_LIST
)
1104 UpdateAppLaunchersFromPref();
1107 void ChromeLauncherController::ShelfItemRemoved(int index
, ash::ShelfID id
) {
1108 // TODO(skuhne): This fixes crbug.com/429870, but it does not answer why we
1109 // get into this state in the first place.
1110 IDToItemControllerMap::iterator iter
= id_to_item_controller_map_
.find(id
);
1111 if (iter
== id_to_item_controller_map_
.end())
1114 LOG(ERROR
) << "Unexpected change of shelf item id: " << id
;
1116 id_to_item_controller_map_
.erase(iter
);
1119 void ChromeLauncherController::ShelfItemMoved(int start_index
,
1121 const ash::ShelfItem
& item
= model_
->items()[target_index
];
1122 // We remember the moved item position if it is either pinnable or
1123 // it is the app list with the alternate shelf layout.
1124 if ((HasShelfIDToAppIDMapping(item
.id
) && IsPinned(item
.id
)) ||
1125 item
.type
== ash::TYPE_APP_LIST
)
1126 PersistPinnedState();
1129 void ChromeLauncherController::ShelfItemChanged(
1131 const ash::ShelfItem
& old_item
) {
1134 void ChromeLauncherController::ShelfStatusChanged() {
1137 void ChromeLauncherController::ActiveUserChanged(
1138 const std::string
& user_email
) {
1139 // Store the order of running applications for the user which gets inactive.
1140 RememberUnpinnedRunningApplicationOrder();
1141 // Coming here the default profile is already switched. All profile specific
1142 // resources get released and the new profile gets attached instead.
1144 // When coming here, the active user has already be changed so that we can
1145 // set it as active.
1146 AttachProfile(ProfileManager::GetActiveUserProfile());
1147 // Update the V1 applications.
1148 browser_status_monitor_
->ActiveUserChanged(user_email
);
1149 // Switch the running applications to the new user.
1150 app_window_controller_
->ActiveUserChanged(user_email
);
1151 // Update the user specific shell properties from the new user profile.
1152 UpdateAppLaunchersFromPref();
1153 SetShelfAlignmentFromPrefs();
1154 SetShelfAutoHideBehaviorFromPrefs();
1155 SetShelfBehaviorsFromPrefs();
1156 #if defined(OS_CHROMEOS)
1157 SetVirtualKeyboardBehaviorFromPrefs();
1158 #endif // defined(OS_CHROMEOS)
1159 // Restore the order of running, but unpinned applications for the activated
1161 RestoreUnpinnedRunningApplicationOrder(user_email
);
1162 // Inform the system tray of the change.
1163 ash::Shell::GetInstance()->system_tray_delegate()->ActiveUserWasChanged();
1164 // Force on-screen keyboard to reset.
1165 if (keyboard::IsKeyboardEnabled())
1166 ash::Shell::GetInstance()->CreateKeyboard();
1169 void ChromeLauncherController::AdditionalUserAddedToSession(Profile
* profile
) {
1170 // Switch the running applications to the new user.
1171 app_window_controller_
->AdditionalUserAddedToSession(profile
);
1174 void ChromeLauncherController::OnExtensionLoaded(
1175 content::BrowserContext
* browser_context
,
1176 const Extension
* extension
) {
1177 if (IsAppPinned(extension
->id())) {
1178 // Clear and re-fetch to ensure icon is up-to-date.
1179 app_icon_loader_
->ClearImage(extension
->id());
1180 app_icon_loader_
->FetchImage(extension
->id());
1183 UpdateAppLaunchersFromPref();
1186 void ChromeLauncherController::OnExtensionUnloaded(
1187 content::BrowserContext
* browser_context
,
1188 const Extension
* extension
,
1189 UnloadedExtensionInfo::Reason reason
) {
1190 const std::string
& id
= extension
->id();
1191 const Profile
* profile
= Profile::FromBrowserContext(browser_context
);
1193 // Since we might have windowed apps of this type which might have
1194 // outstanding locks which needs to be removed.
1195 if (GetShelfIDForAppID(id
) &&
1196 reason
== UnloadedExtensionInfo::REASON_UNINSTALL
) {
1197 CloseWindowedAppsFromRemovedExtension(id
, profile
);
1200 if (IsAppPinned(id
)) {
1201 if (reason
== UnloadedExtensionInfo::REASON_UNINSTALL
) {
1202 if (profile
== profile_
) {
1203 DoUnpinAppWithID(id
);
1205 app_icon_loader_
->ClearImage(id
);
1207 app_icon_loader_
->UpdateImage(id
);
1212 void ChromeLauncherController::OnShelfAlignmentChanged(
1213 aura::Window
* root_window
) {
1214 const char* pref_value
= NULL
;
1215 switch (ash::Shell::GetInstance()->GetShelfAlignment(root_window
)) {
1216 case ash::SHELF_ALIGNMENT_BOTTOM
:
1217 pref_value
= ash::kShelfAlignmentBottom
;
1219 case ash::SHELF_ALIGNMENT_LEFT
:
1220 pref_value
= ash::kShelfAlignmentLeft
;
1222 case ash::SHELF_ALIGNMENT_RIGHT
:
1223 pref_value
= ash::kShelfAlignmentRight
;
1225 case ash::SHELF_ALIGNMENT_TOP
:
1226 pref_value
= ash::kShelfAlignmentTop
;
1229 UpdatePerDisplayPref(
1230 profile_
->GetPrefs(), root_window
, prefs::kShelfAlignment
, pref_value
);
1232 if (root_window
== ash::Shell::GetPrimaryRootWindow()) {
1233 // See comment in |kShelfAlignment| about why we have two prefs here.
1234 profile_
->GetPrefs()->SetString(prefs::kShelfAlignmentLocal
, pref_value
);
1235 profile_
->GetPrefs()->SetString(prefs::kShelfAlignment
, pref_value
);
1239 void ChromeLauncherController::OnDisplayConfigurationChanged() {
1240 SetShelfBehaviorsFromPrefs();
1243 void ChromeLauncherController::OnIsSyncingChanged() {
1244 syncable_prefs::PrefServiceSyncable
* prefs
=
1245 PrefServiceSyncableFromProfile(profile_
);
1246 MaybePropagatePrefToLocal(prefs
,
1247 prefs::kShelfAlignmentLocal
,
1248 prefs::kShelfAlignment
);
1249 MaybePropagatePrefToLocal(prefs
,
1250 prefs::kShelfAutoHideBehaviorLocal
,
1251 prefs::kShelfAutoHideBehavior
);
1254 void ChromeLauncherController::OnAppSyncUIStatusChanged() {
1255 if (app_sync_ui_state_
->status() == AppSyncUIState::STATUS_SYNCING
)
1256 model_
->SetStatus(ash::ShelfModel::STATUS_LOADING
);
1258 model_
->SetStatus(ash::ShelfModel::STATUS_NORMAL
);
1261 void ChromeLauncherController::ExtensionEnableFlowFinished() {
1262 LaunchApp(extension_enable_flow_
->extension_id(),
1263 ash::LAUNCH_FROM_UNKNOWN
,
1265 extension_enable_flow_
.reset();
1268 void ChromeLauncherController::ExtensionEnableFlowAborted(bool user_initiated
) {
1269 extension_enable_flow_
.reset();
1272 ChromeLauncherAppMenuItems
ChromeLauncherController::GetApplicationList(
1273 const ash::ShelfItem
& item
,
1275 // Make sure that there is a controller associated with the id and that the
1276 // extension itself is a valid application and not a panel.
1277 LauncherItemController
* controller
= GetLauncherItemController(item
.id
);
1278 if (!controller
|| !GetShelfIDForAppID(controller
->app_id()))
1279 return ChromeLauncherAppMenuItems().Pass();
1281 return controller
->GetApplicationList(event_flags
);
1284 std::vector
<content::WebContents
*>
1285 ChromeLauncherController::GetV1ApplicationsFromAppId(std::string app_id
) {
1286 ash::ShelfID id
= GetShelfIDForAppID(app_id
);
1288 // If there is no such an item pinned to the launcher, no menu gets created.
1290 LauncherItemController
* controller
= GetLauncherItemController(id
);
1292 if (controller
->type() == LauncherItemController::TYPE_SHORTCUT
)
1293 return GetV1ApplicationsFromController(controller
);
1295 return std::vector
<content::WebContents
*>();
1298 void ChromeLauncherController::ActivateShellApp(const std::string
& app_id
,
1300 ash::ShelfID id
= GetShelfIDForAppID(app_id
);
1302 LauncherItemController
* controller
= GetLauncherItemController(id
);
1303 if (controller
&& controller
->type() == LauncherItemController::TYPE_APP
) {
1304 AppWindowLauncherItemController
* app_window_controller
=
1305 static_cast<AppWindowLauncherItemController
*>(controller
);
1306 app_window_controller
->ActivateIndexedApp(index
);
1311 bool ChromeLauncherController::IsWebContentHandledByApplication(
1312 content::WebContents
* web_contents
,
1313 const std::string
& app_id
) {
1314 if ((web_contents_to_app_id_
.find(web_contents
) !=
1315 web_contents_to_app_id_
.end()) &&
1316 (web_contents_to_app_id_
[web_contents
] == app_id
))
1318 return (app_id
== kGmailAppId
&& ContentCanBeHandledByGmailApp(web_contents
));
1321 bool ChromeLauncherController::ContentCanBeHandledByGmailApp(
1322 content::WebContents
* web_contents
) {
1323 ash::ShelfID id
= GetShelfIDForAppID(kGmailAppId
);
1325 const GURL url
= web_contents
->GetURL();
1326 // We need to extend the application matching for the gMail app beyond the
1327 // manifest file's specification. This is required because of the namespace
1328 // overlap with the offline app ("/mail/mu/").
1329 if (!base::MatchPattern(url
.path(), "/mail/mu/*") &&
1330 base::MatchPattern(url
.path(), "/mail/*") &&
1331 GetExtensionForAppID(kGmailAppId
) &&
1332 GetExtensionForAppID(kGmailAppId
)->OverlapsWithOrigin(url
))
1338 gfx::Image
ChromeLauncherController::GetAppListIcon(
1339 content::WebContents
* web_contents
) const {
1340 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
1341 if (IsIncognito(web_contents
))
1342 return rb
.GetImageNamed(IDR_ASH_SHELF_LIST_INCOGNITO_BROWSER
);
1343 favicon::FaviconDriver
* favicon_driver
=
1344 favicon::ContentFaviconDriver::FromWebContents(web_contents
);
1345 gfx::Image result
= favicon_driver
->GetFavicon();
1346 if (result
.IsEmpty())
1347 return rb
.GetImageNamed(IDR_DEFAULT_FAVICON
);
1351 base::string16
ChromeLauncherController::GetAppListTitle(
1352 content::WebContents
* web_contents
) const {
1353 base::string16 title
= web_contents
->GetTitle();
1356 WebContentsToAppIDMap::const_iterator iter
=
1357 web_contents_to_app_id_
.find(web_contents
);
1358 if (iter
!= web_contents_to_app_id_
.end()) {
1359 std::string app_id
= iter
->second
;
1360 const extensions::Extension
* extension
= GetExtensionForAppID(app_id
);
1362 return base::UTF8ToUTF16(extension
->name());
1364 return l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE
);
1367 ash::ShelfID
ChromeLauncherController::CreateAppShortcutLauncherItem(
1368 const std::string
& app_id
,
1370 return CreateAppShortcutLauncherItemWithType(app_id
,
1372 ash::TYPE_APP_SHORTCUT
);
1375 void ChromeLauncherController::SetAppTabHelperForTest(AppTabHelper
* helper
) {
1376 app_tab_helper_
.reset(helper
);
1379 void ChromeLauncherController::SetAppIconLoaderForTest(
1380 extensions::AppIconLoader
* loader
) {
1381 app_icon_loader_
.reset(loader
);
1384 const std::string
& ChromeLauncherController::GetAppIdFromShelfIdForTest(
1386 return id_to_item_controller_map_
[id
]->app_id();
1389 void ChromeLauncherController::SetShelfItemDelegateManagerForTest(
1390 ash::ShelfItemDelegateManager
* manager
) {
1391 if (item_delegate_manager_
)
1392 item_delegate_manager_
->RemoveObserver(this);
1394 item_delegate_manager_
= manager
;
1396 if (item_delegate_manager_
)
1397 item_delegate_manager_
->AddObserver(this);
1400 void ChromeLauncherController::RememberUnpinnedRunningApplicationOrder() {
1401 RunningAppListIds list
;
1402 for (int i
= 0; i
< model_
->item_count(); i
++) {
1403 ash::ShelfItemType type
= model_
->items()[i
].type
;
1404 if (type
== ash::TYPE_WINDOWED_APP
|| type
== ash::TYPE_PLATFORM_APP
)
1405 list
.push_back(GetAppIDForShelfID(model_
->items()[i
].id
));
1407 last_used_running_application_order_
[
1408 multi_user_util::GetUserIDFromProfile(profile_
)] = list
;
1411 void ChromeLauncherController::RestoreUnpinnedRunningApplicationOrder(
1412 const std::string
& user_id
) {
1413 const RunningAppListIdMap::iterator app_id_list
=
1414 last_used_running_application_order_
.find(user_id
);
1415 if (app_id_list
== last_used_running_application_order_
.end())
1418 // Find the first insertion point for running applications.
1419 int running_index
= model_
->FirstRunningAppIndex();
1420 for (RunningAppListIds::iterator app_id
= app_id_list
->second
.begin();
1421 app_id
!= app_id_list
->second
.end(); ++app_id
) {
1422 ash::ShelfID shelf_id
= GetShelfIDForAppID(*app_id
);
1424 int app_index
= model_
->ItemIndexByID(shelf_id
);
1425 DCHECK_GE(app_index
, 0);
1426 ash::ShelfItemType type
= model_
->items()[app_index
].type
;
1427 if (type
== ash::TYPE_WINDOWED_APP
|| type
== ash::TYPE_PLATFORM_APP
) {
1428 if (running_index
!= app_index
)
1429 model_
->Move(running_index
, app_index
);
1436 ash::ShelfID
ChromeLauncherController::CreateAppShortcutLauncherItemWithType(
1437 const std::string
& app_id
,
1439 ash::ShelfItemType shelf_item_type
) {
1440 AppShortcutLauncherItemController
* controller
=
1441 new AppShortcutLauncherItemController(app_id
, this);
1442 ash::ShelfID shelf_id
= InsertAppLauncherItem(
1443 controller
, app_id
, ash::STATUS_CLOSED
, index
, shelf_item_type
);
1447 LauncherItemController
* ChromeLauncherController::GetLauncherItemController(
1448 const ash::ShelfID id
) {
1449 if (!HasShelfIDToAppIDMapping(id
))
1451 return id_to_item_controller_map_
[id
];
1454 bool ChromeLauncherController::IsBrowserFromActiveUser(Browser
* browser
) {
1455 // If running multi user mode with separate desktops, we have to check if the
1456 // browser is from the active user.
1457 if (chrome::MultiUserWindowManager::GetMultiProfileMode() !=
1458 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED
)
1460 return multi_user_util::IsProfileFromActiveUser(browser
->profile());
1463 bool ChromeLauncherController::ShelfBoundsChangesProbablyWithUser(
1464 aura::Window
* root_window
,
1465 const std::string
& user_id
) const {
1466 Profile
* other_profile
= multi_user_util::GetProfileFromUserID(user_id
);
1467 DCHECK_NE(other_profile
, profile_
);
1469 // Note: The Auto hide state from preferences is not the same as the actual
1470 // visibility of the shelf. Depending on all the various states (full screen,
1471 // no window on desktop, multi user, ..) the shelf could be shown - or not.
1472 bool currently_shown
= ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER
==
1473 GetShelfAutoHideBehaviorFromPrefs(profile_
, root_window
);
1474 bool other_shown
= ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER
==
1475 GetShelfAutoHideBehaviorFromPrefs(other_profile
, root_window
);
1477 return currently_shown
!= other_shown
||
1478 GetShelfAlignmentFromPrefs(profile_
, root_window
) !=
1479 GetShelfAlignmentFromPrefs(other_profile
, root_window
);
1482 void ChromeLauncherController::OnUserProfileReadyToSwitch(Profile
* profile
) {
1483 #if defined(OS_CHROMEOS)
1484 if (user_switch_observer_
.get())
1485 user_switch_observer_
->OnUserProfileReadyToSwitch(profile
);
1489 void ChromeLauncherController::LauncherItemClosed(ash::ShelfID id
) {
1490 IDToItemControllerMap::iterator iter
= id_to_item_controller_map_
.find(id
);
1491 CHECK(iter
!= id_to_item_controller_map_
.end());
1492 CHECK(iter
->second
);
1493 app_icon_loader_
->ClearImage(iter
->second
->app_id());
1494 id_to_item_controller_map_
.erase(iter
);
1495 int index
= model_
->ItemIndexByID(id
);
1496 // A "browser proxy" is not known to the model and this removal does
1497 // therefore not need to be propagated to the model.
1499 model_
->RemoveItemAt(index
);
1502 void ChromeLauncherController::DoPinAppWithID(const std::string
& app_id
) {
1503 // If there is an item, do nothing and return.
1504 if (IsAppPinned(app_id
))
1507 ash::ShelfID shelf_id
= GetShelfIDForAppID(app_id
);
1509 // App item exists, pin it
1512 // Otherwise, create a shortcut item for it.
1513 CreateAppShortcutLauncherItem(app_id
, model_
->item_count());
1515 PersistPinnedState();
1519 void ChromeLauncherController::DoUnpinAppWithID(const std::string
& app_id
) {
1520 ash::ShelfID shelf_id
= GetShelfIDForAppID(app_id
);
1521 if (shelf_id
&& IsPinned(shelf_id
))
1525 int ChromeLauncherController::PinRunningAppInternal(int index
,
1526 ash::ShelfID shelf_id
) {
1527 int running_index
= model_
->ItemIndexByID(shelf_id
);
1528 ash::ShelfItem item
= model_
->items()[running_index
];
1529 DCHECK(item
.type
== ash::TYPE_WINDOWED_APP
||
1530 item
.type
== ash::TYPE_PLATFORM_APP
);
1531 item
.type
= ash::TYPE_APP_SHORTCUT
;
1532 model_
->Set(running_index
, item
);
1533 // The |ShelfModel|'s weight system might reposition the item to a
1534 // new index, so we get the index again.
1535 running_index
= model_
->ItemIndexByID(shelf_id
);
1536 if (running_index
< index
)
1538 if (running_index
!= index
)
1539 model_
->Move(running_index
, index
);
1543 void ChromeLauncherController::UnpinRunningAppInternal(int index
) {
1544 DCHECK_GE(index
, 0);
1545 ash::ShelfItem item
= model_
->items()[index
];
1546 DCHECK_EQ(item
.type
, ash::TYPE_APP_SHORTCUT
);
1547 item
.type
= ash::TYPE_WINDOWED_APP
;
1548 // A platform app and a windowed app are sharing TYPE_APP_SHORTCUT. As such
1549 // we have to check here what this was before it got a shortcut.
1550 LauncherItemController
* controller
= GetLauncherItemController(item
.id
);
1551 if (controller
&& controller
->type() == LauncherItemController::TYPE_APP
)
1552 item
.type
= ash::TYPE_PLATFORM_APP
;
1553 model_
->Set(index
, item
);
1556 void ChromeLauncherController::UpdateAppLaunchersFromPref() {
1557 // There are various functions which will trigger a |PersistPinnedState| call
1558 // like a direct call to |DoPinAppWithID|, or an indirect call to the menu
1559 // model which will use weights to re-arrange the icons to new positions.
1560 // Since this function is meant to synchronize the "is state" with the
1561 // "sync state", it makes no sense to store any changes by this function back
1562 // into the pref state. Therefore we tell |persistPinnedState| to ignore any
1563 // invocations while we are running.
1564 base::AutoReset
<bool> auto_reset(&ignore_persist_pinned_state_change_
, true);
1565 std::vector
<std::string
> pinned_apps
= GetListOfPinnedAppsAndBrowser();
1568 int max_index
= model_
->item_count();
1570 // When one of the two special items cannot be moved (and we do not know where
1571 // yet), we remember the current location in one of these variables.
1572 int chrome_index
= -1;
1573 int app_list_index
= -1;
1575 // Walk the model and |pinned_apps| from the pref lockstep, adding and
1576 // removing items as necessary. NB: This code uses plain old indexing instead
1577 // of iterators because of model mutations as part of the loop.
1578 std::vector
<std::string
>::const_iterator
pref_app_id(pinned_apps
.begin());
1579 for (; index
< max_index
&& pref_app_id
!= pinned_apps
.end(); ++index
) {
1580 // Check if we have an item which we need to handle.
1581 if (*pref_app_id
== extension_misc::kChromeAppId
||
1582 *pref_app_id
== kAppShelfIdPlaceholder
||
1583 IsAppPinned(*pref_app_id
)) {
1584 for (; index
< max_index
; ++index
) {
1585 const ash::ShelfItem
& item(model_
->items()[index
]);
1586 bool is_app_list
= item
.type
== ash::TYPE_APP_LIST
;
1587 bool is_chrome
= item
.type
== ash::TYPE_BROWSER_SHORTCUT
;
1588 if (item
.type
!= ash::TYPE_APP_SHORTCUT
&& !is_app_list
&& !is_chrome
)
1590 LauncherItemController
* controller
= GetLauncherItemController(item
.id
);
1591 if ((kAppShelfIdPlaceholder
== *pref_app_id
&& is_app_list
) ||
1592 (extension_misc::kChromeAppId
== *pref_app_id
&& is_chrome
) ||
1593 (controller
&& controller
->app_id() == *pref_app_id
)) {
1594 // Check if an item needs to be moved here.
1595 MoveChromeOrApplistToFinalPosition(
1596 is_chrome
, is_app_list
, index
, &chrome_index
, &app_list_index
);
1600 if (is_chrome
|| is_app_list
) {
1601 // We cannot delete any of these shortcuts. As such we remember
1602 // their positions and move them later where they belong.
1604 chrome_index
= index
;
1606 app_list_index
= index
;
1607 // And skip the item - or exit the loop if end is reached (note that
1608 // in that case we will reduce the index again by one and this only
1609 // compensates for it).
1610 if (index
>= max_index
- 1)
1614 // Check if this is a platform or a windowed app.
1615 if (item
.type
== ash::TYPE_APP_SHORTCUT
&&
1617 (controller
->locked() ||
1618 controller
->type() == LauncherItemController::TYPE_APP
)) {
1619 // Note: This will not change the amount of items (|max_index|).
1620 // Even changes to the actual |index| due to item weighting
1621 // changes should be fine.
1622 UnpinRunningAppInternal(index
);
1625 LauncherItemClosed(item
.id
);
1632 // If the item wasn't found, that means id_to_item_controller_map_
1634 DCHECK(index
<= max_index
);
1636 // Check if the item was already running but not yet pinned.
1637 ash::ShelfID shelf_id
= GetShelfIDForAppID(*pref_app_id
);
1639 // This app is running but not yet pinned. So pin and move it.
1640 index
= PinRunningAppInternal(index
, shelf_id
);
1642 // This app wasn't pinned before, insert a new entry.
1643 shelf_id
= CreateAppShortcutLauncherItem(*pref_app_id
, index
);
1645 index
= model_
->ItemIndexByID(shelf_id
);
1651 // Remove any trailing existing items.
1652 while (index
< model_
->item_count()) {
1653 const ash::ShelfItem
& item(model_
->items()[index
]);
1654 if (item
.type
== ash::TYPE_APP_SHORTCUT
) {
1655 LauncherItemController
* controller
= GetLauncherItemController(item
.id
);
1657 if (controller
->locked() ||
1658 controller
->type() == LauncherItemController::TYPE_APP
) {
1659 UnpinRunningAppInternal(index
);
1661 LauncherItemClosed(item
.id
);
1665 if (item
.type
== ash::TYPE_BROWSER_SHORTCUT
)
1666 chrome_index
= index
;
1667 else if (item
.type
== ash::TYPE_APP_LIST
)
1668 app_list_index
= index
;
1673 // Append unprocessed items from the pref to the end of the model.
1674 for (; pref_app_id
!= pinned_apps
.end(); ++pref_app_id
) {
1675 // All items but the chrome and / or app list shortcut needs to be added.
1676 bool is_chrome
= *pref_app_id
== extension_misc::kChromeAppId
;
1677 bool is_app_list
= *pref_app_id
== kAppShelfIdPlaceholder
;
1678 // Coming here we know the next item which can be finalized, either the
1679 // chrome item or the app launcher. The final position is the end of the
1680 // list. The menu model will make sure that the item is grouped according
1681 // to its weight (which we do not know here).
1682 if (!is_chrome
&& !is_app_list
) {
1683 DoPinAppWithID(*pref_app_id
);
1684 int target_index
= FindInsertionPoint(false);
1685 ash::ShelfID id
= GetShelfIDForAppID(*pref_app_id
);
1686 int source_index
= model_
->ItemIndexByID(id
);
1687 if (source_index
!= target_index
)
1688 model_
->Move(source_index
, target_index
);
1690 // Needed for the old layout - the weight might force it to be lower in
1692 if (app_list_index
!= -1 && target_index
<= app_list_index
)
1695 int target_index
= FindInsertionPoint(is_app_list
);
1696 MoveChromeOrApplistToFinalPosition(
1697 is_chrome
, is_app_list
, target_index
, &chrome_index
, &app_list_index
);
1702 void ChromeLauncherController::SetShelfAutoHideBehaviorPrefs(
1703 ash::ShelfAutoHideBehavior behavior
,
1704 aura::Window
* root_window
) {
1705 const char* value
= NULL
;
1707 case ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS
:
1708 value
= ash::kShelfAutoHideBehaviorAlways
;
1710 case ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER
:
1711 value
= ash::kShelfAutoHideBehaviorNever
;
1713 case ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN
:
1714 // This one should not be a valid preference option for now. We only want
1715 // to completely hide it when we run in app mode - or while we temporarily
1716 // hide the shelf as part of an animation (e.g. the multi user change).
1720 UpdatePerDisplayPref(
1721 profile_
->GetPrefs(), root_window
, prefs::kShelfAutoHideBehavior
, value
);
1723 if (root_window
== ash::Shell::GetPrimaryRootWindow()) {
1724 // See comment in |kShelfAlignment| about why we have two prefs here.
1725 profile_
->GetPrefs()->SetString(prefs::kShelfAutoHideBehaviorLocal
, value
);
1726 profile_
->GetPrefs()->SetString(prefs::kShelfAutoHideBehavior
, value
);
1730 void ChromeLauncherController::SetShelfAutoHideBehaviorFromPrefs() {
1731 aura::Window::Windows root_windows
= ash::Shell::GetAllRootWindows();
1733 for (aura::Window::Windows::const_iterator iter
= root_windows
.begin();
1734 iter
!= root_windows
.end(); ++iter
) {
1735 ash::Shell::GetInstance()->SetShelfAutoHideBehavior(
1736 GetShelfAutoHideBehavior(*iter
), *iter
);
1740 void ChromeLauncherController::SetShelfAlignmentFromPrefs() {
1741 if (!ash::ShelfWidget::ShelfAlignmentAllowed())
1744 aura::Window::Windows root_windows
= ash::Shell::GetAllRootWindows();
1746 for (aura::Window::Windows::const_iterator iter
= root_windows
.begin();
1747 iter
!= root_windows
.end(); ++iter
) {
1748 ash::Shell::GetInstance()->SetShelfAlignment(
1749 GetShelfAlignmentFromPrefs(profile_
, *iter
), *iter
);
1753 void ChromeLauncherController::SetShelfBehaviorsFromPrefs() {
1754 SetShelfAutoHideBehaviorFromPrefs();
1755 SetShelfAlignmentFromPrefs();
1758 #if defined(OS_CHROMEOS)
1759 void ChromeLauncherController::SetVirtualKeyboardBehaviorFromPrefs() {
1760 const PrefService
* service
= profile_
->GetPrefs();
1761 const bool was_enabled
= keyboard::IsKeyboardEnabled();
1762 if (!service
->HasPrefPath(prefs::kTouchVirtualKeyboardEnabled
)) {
1763 keyboard::SetKeyboardShowOverride(keyboard::KEYBOARD_SHOW_OVERRIDE_NONE
);
1765 const bool enable
= service
->GetBoolean(
1766 prefs::kTouchVirtualKeyboardEnabled
);
1767 keyboard::SetKeyboardShowOverride(
1768 enable
? keyboard::KEYBOARD_SHOW_OVERRIDE_ENABLED
1769 : keyboard::KEYBOARD_SHOW_OVERRIDE_DISABLED
);
1771 const bool is_enabled
= keyboard::IsKeyboardEnabled();
1772 if (was_enabled
&& !is_enabled
)
1773 ash::Shell::GetInstance()->DeactivateKeyboard();
1774 else if (is_enabled
&& !was_enabled
)
1775 ash::Shell::GetInstance()->CreateKeyboard();
1777 #endif // defined(OS_CHROMEOS)
1779 ash::ShelfItemStatus
ChromeLauncherController::GetAppState(
1780 const std::string
& app_id
) {
1781 ash::ShelfItemStatus status
= ash::STATUS_CLOSED
;
1782 for (WebContentsToAppIDMap::iterator it
= web_contents_to_app_id_
.begin();
1783 it
!= web_contents_to_app_id_
.end();
1785 if (it
->second
== app_id
) {
1786 Browser
* browser
= chrome::FindBrowserWithWebContents(it
->first
);
1787 // Usually there should never be an item in our |web_contents_to_app_id_|
1788 // list which got deleted already. However - in some situations e.g.
1789 // Browser::SwapTabContent there is temporarily no associated browser.
1792 if (browser
->window()->IsActive()) {
1793 return browser
->tab_strip_model()->GetActiveWebContents() == it
->first
?
1794 ash::STATUS_ACTIVE
: ash::STATUS_RUNNING
;
1796 status
= ash::STATUS_RUNNING
;
1803 ash::ShelfID
ChromeLauncherController::InsertAppLauncherItem(
1804 LauncherItemController
* controller
,
1805 const std::string
& app_id
,
1806 ash::ShelfItemStatus status
,
1808 ash::ShelfItemType shelf_item_type
) {
1809 ash::ShelfID id
= model_
->next_id();
1810 CHECK(!HasShelfIDToAppIDMapping(id
));
1812 id_to_item_controller_map_
[id
] = controller
;
1813 controller
->set_shelf_id(id
);
1815 ash::ShelfItem item
;
1816 item
.type
= shelf_item_type
;
1817 item
.image
= extensions::util::GetDefaultAppIcon();
1819 ash::ShelfItemStatus new_state
= GetAppState(app_id
);
1820 if (new_state
!= ash::STATUS_CLOSED
)
1823 item
.status
= status
;
1825 model_
->AddAt(index
, item
);
1827 app_icon_loader_
->FetchImage(app_id
);
1828 app_icon_loader_
->UpdateImage(app_id
);
1830 SetShelfItemDelegate(id
, controller
);
1835 std::vector
<content::WebContents
*>
1836 ChromeLauncherController::GetV1ApplicationsFromController(
1837 LauncherItemController
* controller
) {
1838 DCHECK(controller
->type() == LauncherItemController::TYPE_SHORTCUT
);
1839 AppShortcutLauncherItemController
* app_controller
=
1840 static_cast<AppShortcutLauncherItemController
*>(controller
);
1841 return app_controller
->GetRunningApplications();
1844 BrowserShortcutLauncherItemController
*
1845 ChromeLauncherController::GetBrowserShortcutLauncherItemController() {
1846 for (IDToItemControllerMap::iterator i
= id_to_item_controller_map_
.begin();
1847 i
!= id_to_item_controller_map_
.end(); ++i
) {
1848 int index
= model_
->ItemIndexByID(i
->first
);
1849 const ash::ShelfItem
& item
= model_
->items()[index
];
1850 if (item
.type
== ash::TYPE_BROWSER_SHORTCUT
)
1851 return static_cast<BrowserShortcutLauncherItemController
*>(i
->second
);
1853 NOTREACHED() << "There should be always a BrowserLauncherItemController.";
1857 ash::ShelfID
ChromeLauncherController::CreateBrowserShortcutLauncherItem() {
1858 ash::ShelfItem browser_shortcut
;
1859 browser_shortcut
.type
= ash::TYPE_BROWSER_SHORTCUT
;
1860 ResourceBundle
& rb
= ResourceBundle::GetSharedInstance();
1861 browser_shortcut
.image
= *rb
.GetImageSkiaNamed(IDR_PRODUCT_LOGO_32
);
1862 ash::ShelfID id
= model_
->next_id();
1863 size_t index
= GetChromeIconIndexForCreation();
1864 model_
->AddAt(index
, browser_shortcut
);
1865 id_to_item_controller_map_
[id
] =
1866 new BrowserShortcutLauncherItemController(this);
1867 id_to_item_controller_map_
[id
]->set_shelf_id(id
);
1868 // ShelfItemDelegateManager owns BrowserShortcutLauncherItemController.
1869 SetShelfItemDelegate(id
, id_to_item_controller_map_
[id
]);
1873 void ChromeLauncherController::PersistChromeItemIndex(int index
) {
1874 profile_
->GetPrefs()->SetInteger(prefs::kShelfChromeIconIndex
, index
);
1877 int ChromeLauncherController::GetChromeIconIndexFromPref() const {
1878 size_t index
= profile_
->GetPrefs()->GetInteger(prefs::kShelfChromeIconIndex
);
1879 const base::ListValue
* pinned_apps_pref
=
1880 profile_
->GetPrefs()->GetList(prefs::kPinnedLauncherApps
);
1881 return std::max(static_cast<size_t>(0),
1882 std::min(pinned_apps_pref
->GetSize(), index
));
1885 void ChromeLauncherController::MoveChromeOrApplistToFinalPosition(
1890 int* app_list_index
) {
1891 if (is_chrome
&& *chrome_index
!= -1) {
1892 model_
->Move(*chrome_index
, target_index
);
1893 if (*app_list_index
!= -1 &&
1894 *chrome_index
< *app_list_index
&&
1895 target_index
> *app_list_index
)
1896 --(*app_list_index
);
1898 } else if (is_app_list
&& *app_list_index
!= -1) {
1899 model_
->Move(*app_list_index
, target_index
);
1900 if (*chrome_index
!= -1 &&
1901 *app_list_index
< *chrome_index
&&
1902 target_index
> *chrome_index
)
1904 *app_list_index
= -1;
1908 int ChromeLauncherController::FindInsertionPoint(bool is_app_list
) {
1909 // Keeping this change small to backport to M33&32 (see crbug.com/329597).
1910 // TODO(skuhne): With the removal of the legacy shelf layout we should remove
1911 // the ability to move the app list item since this was never used. We should
1912 // instead ask the ShelfModel::ValidateInsertionIndex or similir for an index.
1916 for (int i
= model_
->item_count() - 1; i
> 0; --i
) {
1917 ash::ShelfItemType type
= model_
->items()[i
].type
;
1918 if (type
== ash::TYPE_APP_SHORTCUT
||
1919 (is_app_list
&& type
== ash::TYPE_APP_LIST
) ||
1920 type
== ash::TYPE_BROWSER_SHORTCUT
) {
1927 int ChromeLauncherController::GetChromeIconIndexForCreation() {
1928 // We get the list of pinned apps as they currently would get pinned.
1929 // Within this list the chrome icon will be the correct location.
1930 std::vector
<std::string
> pinned_apps
= GetListOfPinnedAppsAndBrowser();
1932 std::vector
<std::string
>::iterator it
=
1933 std::find(pinned_apps
.begin(),
1935 std::string(extension_misc::kChromeAppId
));
1936 DCHECK(it
!= pinned_apps
.end());
1937 int index
= it
- pinned_apps
.begin();
1939 // We should do here a comparison between the is state and the "want to be"
1940 // state since some apps might be able to pin but are not yet. Instead - for
1941 // the time being we clamp against the amount of known items and wait for the
1942 // next |UpdateAppLaunchersFromPref()| call to correct it - it will come since
1943 // the pinning will be done then.
1944 return std::min(model_
->item_count(), index
);
1947 std::vector
<std::string
>
1948 ChromeLauncherController::GetListOfPinnedAppsAndBrowser() {
1949 // Adding the app list item to the list of items requires that the ID is not
1950 // a valid and known ID for the extension system. The ID was constructed that
1951 // way - but just to make sure...
1952 DCHECK(!app_tab_helper_
->IsValidIDForCurrentUser(kAppShelfIdPlaceholder
));
1954 std::vector
<std::string
> pinned_apps
;
1956 // Get the new incarnation of the list.
1957 const base::ListValue
* pinned_apps_pref
=
1958 profile_
->GetPrefs()->GetList(prefs::kPinnedLauncherApps
);
1960 // Keep track of the addition of the chrome and the app list icon.
1961 bool chrome_icon_added
= false;
1962 bool app_list_icon_added
= false;
1963 size_t chrome_icon_index
= GetChromeIconIndexFromPref();
1965 // See if the chrome string is already in the pinned list and remove it if
1967 base::Value
* chrome_app
= ash::CreateAppDict(extension_misc::kChromeAppId
);
1969 chrome_icon_added
= pinned_apps_pref
->Find(*chrome_app
) !=
1970 pinned_apps_pref
->end();
1974 for (size_t index
= 0; index
< pinned_apps_pref
->GetSize(); ++index
) {
1975 // We need to position the chrome icon relative to it's place in the pinned
1976 // preference list - even if an item of that list isn't shown yet.
1977 if (index
== chrome_icon_index
&& !chrome_icon_added
) {
1978 pinned_apps
.push_back(extension_misc::kChromeAppId
);
1979 chrome_icon_added
= true;
1981 const base::DictionaryValue
* app
= NULL
;
1983 if (pinned_apps_pref
->GetDictionary(index
, &app
) &&
1984 app
->GetString(ash::kPinnedAppsPrefAppIDPath
, &app_id
) &&
1985 (std::find(pinned_apps
.begin(), pinned_apps
.end(), app_id
) ==
1986 pinned_apps
.end())) {
1987 if (app_id
== extension_misc::kChromeAppId
) {
1988 chrome_icon_added
= true;
1989 pinned_apps
.push_back(extension_misc::kChromeAppId
);
1990 } else if (app_id
== kAppShelfIdPlaceholder
) {
1991 app_list_icon_added
= true;
1992 pinned_apps
.push_back(kAppShelfIdPlaceholder
);
1993 } else if (app_tab_helper_
->IsValidIDForCurrentUser(app_id
)) {
1994 // Note: In multi profile scenarios we only want to show pinnable apps
1995 // here which is correct. Running applications from the other users will
1996 // continue to run. So no need for multi profile modifications.
1997 pinned_apps
.push_back(app_id
);
2002 // If not added yet, the chrome item will be the last item in the list.
2003 if (!chrome_icon_added
)
2004 pinned_apps
.push_back(extension_misc::kChromeAppId
);
2006 // If not added yet, add the app list item either at the end or at the
2007 // beginning - depending on the shelf layout.
2008 if (!app_list_icon_added
) {
2009 pinned_apps
.insert(pinned_apps
.begin(), kAppShelfIdPlaceholder
);
2014 bool ChromeLauncherController::IsIncognito(
2015 const content::WebContents
* web_contents
) const {
2016 const Profile
* profile
=
2017 Profile::FromBrowserContext(web_contents
->GetBrowserContext());
2018 return profile
->IsOffTheRecord() && !profile
->IsGuestSession() &&
2019 !profile
->IsSystemProfile();
2022 void ChromeLauncherController::CloseWindowedAppsFromRemovedExtension(
2023 const std::string
& app_id
,
2024 const Profile
* profile
) {
2025 // This function cannot rely on the controller's enumeration functionality
2026 // since the extension has already be unloaded.
2027 const BrowserList
* ash_browser_list
=
2028 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH
);
2029 std::vector
<Browser
*> browser_to_close
;
2030 for (BrowserList::const_reverse_iterator
2031 it
= ash_browser_list
->begin_last_active();
2032 it
!= ash_browser_list
->end_last_active(); ++it
) {
2033 Browser
* browser
= *it
;
2034 if (!browser
->is_type_tabbed() && browser
->is_type_popup() &&
2035 browser
->is_app() &&
2037 web_app::GetExtensionIdFromApplicationName(browser
->app_name()) &&
2038 profile
== browser
->profile()) {
2039 browser_to_close
.push_back(browser
);
2042 while (!browser_to_close
.empty()) {
2043 TabStripModel
* tab_strip
= browser_to_close
.back()->tab_strip_model();
2044 tab_strip
->CloseWebContentsAt(0, TabStripModel::CLOSE_NONE
);
2045 browser_to_close
.pop_back();
2049 void ChromeLauncherController::SetShelfItemDelegate(
2051 ash::ShelfItemDelegate
* item_delegate
) {
2053 DCHECK(item_delegate
);
2054 DCHECK(item_delegate_manager_
);
2055 item_delegate_manager_
->SetShelfItemDelegate(
2056 id
, scoped_ptr
<ash::ShelfItemDelegate
>(item_delegate
).Pass());
2059 void ChromeLauncherController::AttachProfile(Profile
* profile
) {
2061 // Either add the profile to the list of known profiles and make it the active
2062 // one for some functions of AppTabHelper or create a new one.
2063 if (!app_tab_helper_
.get())
2064 app_tab_helper_
.reset(new LauncherAppTabHelper(profile_
));
2066 app_tab_helper_
->SetCurrentUser(profile_
);
2067 // TODO(skuhne): The AppIconLoaderImpl has the same problem. Each loaded
2068 // image is associated with a profile (it's loader requires the profile).
2069 // Since icon size changes are possible, the icon could be requested to be
2070 // reloaded. However - having it not multi profile aware would cause problems
2071 // if the icon cache gets deleted upon user switch.
2072 app_icon_loader_
.reset(new extensions::AppIconLoaderImpl(
2073 profile_
, extension_misc::EXTENSION_ICON_SMALL
, this));
2075 pref_change_registrar_
.Init(profile_
->GetPrefs());
2076 pref_change_registrar_
.Add(
2077 prefs::kPinnedLauncherApps
,
2078 base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref
,
2079 base::Unretained(this)));
2080 pref_change_registrar_
.Add(
2081 prefs::kShelfAlignmentLocal
,
2082 base::Bind(&ChromeLauncherController::SetShelfAlignmentFromPrefs
,
2083 base::Unretained(this)));
2084 pref_change_registrar_
.Add(
2085 prefs::kShelfAutoHideBehaviorLocal
,
2086 base::Bind(&ChromeLauncherController::
2087 SetShelfAutoHideBehaviorFromPrefs
,
2088 base::Unretained(this)));
2089 pref_change_registrar_
.Add(
2090 prefs::kShelfPreferences
,
2091 base::Bind(&ChromeLauncherController::SetShelfBehaviorsFromPrefs
,
2092 base::Unretained(this)));
2093 #if defined(OS_CHROMEOS)
2094 pref_change_registrar_
.Add(
2095 prefs::kTouchVirtualKeyboardEnabled
,
2096 base::Bind(&ChromeLauncherController::SetVirtualKeyboardBehaviorFromPrefs
,
2097 base::Unretained(this)));
2098 #endif // defined(OS_CHROMEOS)
2100 extensions::ExtensionRegistry::Get(profile_
)->AddObserver(this);
2103 void ChromeLauncherController::ReleaseProfile() {
2104 if (app_sync_ui_state_
)
2105 app_sync_ui_state_
->RemoveObserver(this);
2107 extensions::ExtensionRegistry::Get(profile_
)->RemoveObserver(this);
2109 PrefServiceSyncableFromProfile(profile_
)->RemoveObserver(this);
2111 pref_change_registrar_
.RemoveAll();