1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
9 #include "ash/ash_switches.h"
10 #include "ash/desktop_background/desktop_background_controller.h"
11 #include "ash/multi_profile_uma.h"
12 #include "ash/root_window_controller.h"
13 #include "ash/shelf/shelf.h"
14 #include "ash/shelf/shelf_item_delegate_manager.h"
15 #include "ash/shelf/shelf_layout_manager.h"
16 #include "ash/shelf/shelf_model.h"
17 #include "ash/shelf/shelf_widget.h"
18 #include "ash/shell.h"
19 #include "ash/system/tray/system_tray_delegate.h"
20 #include "ash/wm/window_util.h"
21 #include "base/command_line.h"
22 #include "base/prefs/scoped_user_pref_update.h"
23 #include "base/strings/string_number_conversions.h"
24 #include "base/strings/string_util.h"
25 #include "base/strings/utf_string_conversions.h"
26 #include "base/values.h"
27 #include "chrome/browser/app_mode/app_mode_utils.h"
28 #include "chrome/browser/chrome_notification_types.h"
29 #include "chrome/browser/defaults.h"
30 #include "chrome/browser/extensions/app_icon_loader_impl.h"
31 #include "chrome/browser/extensions/extension_util.h"
32 #include "chrome/browser/extensions/launch_util.h"
33 #include "chrome/browser/favicon/favicon_tab_helper.h"
34 #include "chrome/browser/prefs/incognito_mode_prefs.h"
35 #include "chrome/browser/prefs/pref_service_syncable.h"
36 #include "chrome/browser/profiles/profile.h"
37 #include "chrome/browser/profiles/profile_manager.h"
38 #include "chrome/browser/ui/ash/app_sync_ui_state.h"
39 #include "chrome/browser/ui/ash/chrome_launcher_prefs.h"
40 #include "chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.h"
41 #include "chrome/browser/ui/ash/launcher/app_window_launcher_controller.h"
42 #include "chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h"
43 #include "chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.h"
44 #include "chrome/browser/ui/ash/launcher/browser_status_monitor.h"
45 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h"
46 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_browser.h"
47 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_tab.h"
48 #include "chrome/browser/ui/ash/launcher/chrome_launcher_types.h"
49 #include "chrome/browser/ui/ash/launcher/launcher_app_tab_helper.h"
50 #include "chrome/browser/ui/ash/launcher/launcher_item_controller.h"
51 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
52 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager.h"
53 #include "chrome/browser/ui/browser.h"
54 #include "chrome/browser/ui/browser_commands.h"
55 #include "chrome/browser/ui/browser_finder.h"
56 #include "chrome/browser/ui/browser_list.h"
57 #include "chrome/browser/ui/browser_tabstrip.h"
58 #include "chrome/browser/ui/browser_window.h"
59 #include "chrome/browser/ui/extensions/application_launch.h"
60 #include "chrome/browser/ui/extensions/extension_enable_flow.h"
61 #include "chrome/browser/ui/host_desktop.h"
62 #include "chrome/browser/ui/tabs/tab_strip_model.h"
63 #include "chrome/browser/web_applications/web_app.h"
64 #include "chrome/common/chrome_switches.h"
65 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
66 #include "chrome/common/pref_names.h"
67 #include "chrome/common/url_constants.h"
68 #include "chrome/grit/generated_resources.h"
69 #include "content/public/browser/navigation_entry.h"
70 #include "content/public/browser/notification_registrar.h"
71 #include "content/public/browser/notification_service.h"
72 #include "content/public/browser/web_contents.h"
73 #include "extensions/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/keyboard/keyboard_util.h"
89 #include "ui/resources/grit/ui_resources.h"
90 #include "ui/wm/core/window_animations.h"
92 #if defined(OS_CHROMEOS)
93 #include "chrome/browser/browser_process.h"
94 #include "chrome/browser/ui/ash/chrome_shell_delegate.h"
95 #include "chrome/browser/ui/ash/launcher/multi_profile_app_window_launcher_controller.h"
96 #include "chrome/browser/ui/ash/launcher/multi_profile_browser_status_monitor.h"
97 #include "components/user_manager/user_manager.h"
100 using extensions::Extension;
101 using extensions::UnloadedExtensionInfo;
102 using extension_misc::kGmailAppId;
103 using content::WebContents;
106 ChromeLauncherController* ChromeLauncherController::instance_ = NULL;
110 // This will be used as placeholder in the list of the pinned applciatons.
111 // Note that this is NOT a valid extension identifier so that pre M31 versions
113 const char kAppShelfIdPlaceholder[] = "AppShelfIDPlaceholder--------";
115 std::string GetPrefKeyForRootWindow(aura::Window* root_window) {
116 gfx::Display display = gfx::Screen::GetScreenFor(
117 root_window)->GetDisplayNearestWindow(root_window);
118 DCHECK(display.is_valid());
120 return base::Int64ToString(display.id());
123 void UpdatePerDisplayPref(PrefService* pref_service,
124 aura::Window* root_window,
125 const char* pref_key,
126 const std::string& value) {
127 std::string key = GetPrefKeyForRootWindow(root_window);
131 DictionaryPrefUpdate update(pref_service, prefs::kShelfPreferences);
132 base::DictionaryValue* shelf_prefs = update.Get();
133 base::DictionaryValue* prefs = NULL;
134 if (!shelf_prefs->GetDictionary(key, &prefs)) {
135 prefs = new base::DictionaryValue();
136 shelf_prefs->Set(key, prefs);
138 prefs->SetStringWithoutPathExpansion(pref_key, value);
141 // Returns a pref value in |pref_service| for the display of |root_window|. The
142 // pref value is stored in |local_path| and |path|, but |pref_service| may have
143 // per-display preferences and the value can be specified by policy. Here is
145 // * A value managed by policy. This is a single value that applies to all
147 // * A user-set value for the specified display.
148 // * A user-set value in |local_path| or |path|, if no per-display settings are
149 // ever specified (see http://crbug.com/173719 for why). |local_path| is
150 // preferred. See comment in |kShelfAlignment| as to why we consider two
151 // prefs and why |local_path| is preferred.
152 // * A value recommended by policy. This is a single value that applies to all
154 // * The default value for |local_path| if the value is not recommended by
156 std::string GetPrefForRootWindow(PrefService* pref_service,
157 aura::Window* root_window,
158 const char* local_path,
160 const PrefService::Preference* local_pref =
161 pref_service->FindPreference(local_path);
162 const std::string value(pref_service->GetString(local_path));
163 if (local_pref->IsManaged())
166 std::string pref_key = GetPrefKeyForRootWindow(root_window);
167 bool has_per_display_prefs = false;
168 if (!pref_key.empty()) {
169 const base::DictionaryValue* shelf_prefs = pref_service->GetDictionary(
170 prefs::kShelfPreferences);
171 const base::DictionaryValue* display_pref = NULL;
172 std::string per_display_value;
173 if (shelf_prefs->GetDictionary(pref_key, &display_pref) &&
174 display_pref->GetString(path, &per_display_value))
175 return per_display_value;
177 // If the pref for the specified display is not found, scan the whole prefs
178 // and check if the prefs for other display is already specified.
179 std::string unused_value;
180 for (base::DictionaryValue::Iterator iter(*shelf_prefs);
181 !iter.IsAtEnd(); iter.Advance()) {
182 const base::DictionaryValue* display_pref = NULL;
183 if (iter.value().GetAsDictionary(&display_pref) &&
184 display_pref->GetString(path, &unused_value)) {
185 has_per_display_prefs = true;
191 if (local_pref->IsRecommended() || !has_per_display_prefs)
194 const base::Value* default_value =
195 pref_service->GetDefaultPrefValue(local_path);
196 std::string default_string;
197 default_value->GetAsString(&default_string);
198 return default_string;
201 // Gets the shelf auto hide behavior from prefs for a root window.
202 ash::ShelfAutoHideBehavior GetShelfAutoHideBehaviorFromPrefs(
204 aura::Window* root_window) {
205 // Don't show the shelf in app mode.
206 if (chrome::IsRunningInAppMode())
207 return ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN;
209 // See comment in |kShelfAlignment| as to why we consider two prefs.
210 const std::string behavior_value(
211 GetPrefForRootWindow(profile->GetPrefs(),
213 prefs::kShelfAutoHideBehaviorLocal,
214 prefs::kShelfAutoHideBehavior));
216 // Note: To maintain sync compatibility with old images of chrome/chromeos
217 // the set of values that may be encountered includes the now-extinct
218 // "Default" as well as "Never" and "Always", "Default" should now
219 // be treated as "Never" (http://crbug.com/146773).
220 if (behavior_value == ash::kShelfAutoHideBehaviorAlways)
221 return ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS;
222 return ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER;
225 // Gets the shelf alignment from prefs for a root window.
226 ash::ShelfAlignment GetShelfAlignmentFromPrefs(Profile* profile,
227 aura::Window* root_window) {
228 // See comment in |kShelfAlignment| as to why we consider two prefs.
229 const std::string alignment_value(
230 GetPrefForRootWindow(profile->GetPrefs(),
232 prefs::kShelfAlignmentLocal,
233 prefs::kShelfAlignment));
234 if (alignment_value == ash::kShelfAlignmentLeft)
235 return ash::SHELF_ALIGNMENT_LEFT;
236 else if (alignment_value == ash::kShelfAlignmentRight)
237 return ash::SHELF_ALIGNMENT_RIGHT;
238 else if (alignment_value == ash::kShelfAlignmentTop)
239 return ash::SHELF_ALIGNMENT_TOP;
240 return ash::SHELF_ALIGNMENT_BOTTOM;
243 // If prefs have synced and no user-set value exists at |local_path|, the value
244 // from |synced_path| is copied to |local_path|.
245 void MaybePropagatePrefToLocal(PrefServiceSyncable* pref_service,
246 const char* local_path,
247 const char* synced_path) {
248 if (!pref_service->FindPreference(local_path)->HasUserSetting() &&
249 pref_service->IsSyncing()) {
250 // First time the user is using this machine, propagate from remote to
252 pref_service->SetString(local_path, pref_service->GetString(synced_path));
256 std::string GetSourceFromAppListSource(ash::LaunchSource source) {
258 case ash::LAUNCH_FROM_APP_LIST:
259 return std::string(extension_urls::kLaunchSourceAppList);
260 case ash::LAUNCH_FROM_APP_LIST_SEARCH:
261 return std::string(extension_urls::kLaunchSourceAppListSearch);
262 default: return std::string();
268 #if defined(OS_CHROMEOS)
269 // A class to get events from ChromeOS when a user gets changed or added.
270 class ChromeLauncherControllerUserSwitchObserverChromeOS
271 : public ChromeLauncherControllerUserSwitchObserver,
272 public user_manager::UserManager::UserSessionStateObserver,
273 content::NotificationObserver {
275 ChromeLauncherControllerUserSwitchObserverChromeOS(
276 ChromeLauncherController* controller)
277 : controller_(controller) {
278 DCHECK(user_manager::UserManager::IsInitialized());
279 user_manager::UserManager::Get()->AddSessionStateObserver(this);
280 // A UserAddedToSession notification can be sent before a profile is loaded.
281 // Since our observers require that we have already a profile, we might have
282 // to postpone the notification until the ProfileManager lets us know that
283 // the profile for that newly added user was added to the ProfileManager.
284 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_ADDED,
285 content::NotificationService::AllSources());
287 virtual ~ChromeLauncherControllerUserSwitchObserverChromeOS() {
288 user_manager::UserManager::Get()->RemoveSessionStateObserver(this);
291 // user_manager::UserManager::UserSessionStateObserver overrides:
292 virtual void UserAddedToSession(
293 const user_manager::User* added_user) override;
295 // content::NotificationObserver overrides:
296 virtual void Observe(int type,
297 const content::NotificationSource& source,
298 const content::NotificationDetails& details) override;
301 // Add a user to the session.
302 void AddUser(Profile* profile);
304 // The owning ChromeLauncherController.
305 ChromeLauncherController* controller_;
307 // The notification registrar to track the Profile creations after a user got
308 // added to the session (if required).
309 content::NotificationRegistrar registrar_;
311 // Users which were just added to the system, but which profiles were not yet
313 std::set<std::string> added_user_ids_waiting_for_profiles_;
315 DISALLOW_COPY_AND_ASSIGN(ChromeLauncherControllerUserSwitchObserverChromeOS);
318 void ChromeLauncherControllerUserSwitchObserverChromeOS::UserAddedToSession(
319 const user_manager::User* active_user) {
320 Profile* profile = multi_user_util::GetProfileFromUserID(
321 active_user->email());
322 // If we do not have a profile yet, we postpone forwarding the notification
323 // until it is loaded.
325 added_user_ids_waiting_for_profiles_.insert(active_user->email());
330 void ChromeLauncherControllerUserSwitchObserverChromeOS::Observe(
332 const content::NotificationSource& source,
333 const content::NotificationDetails& details) {
334 if (type == chrome::NOTIFICATION_PROFILE_ADDED &&
335 !added_user_ids_waiting_for_profiles_.empty()) {
336 // Check if the profile is from a user which was on the waiting list.
337 Profile* profile = content::Source<Profile>(source).ptr();
338 std::string user_id = multi_user_util::GetUserIDFromProfile(profile);
339 std::set<std::string>::iterator it = std::find(
340 added_user_ids_waiting_for_profiles_.begin(),
341 added_user_ids_waiting_for_profiles_.end(),
343 if (it != added_user_ids_waiting_for_profiles_.end()) {
344 added_user_ids_waiting_for_profiles_.erase(it);
345 AddUser(profile->GetOriginalProfile());
350 void ChromeLauncherControllerUserSwitchObserverChromeOS::AddUser(
352 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
353 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED)
354 chrome::MultiUserWindowManager::GetInstance()->AddUser(profile);
355 controller_->AdditionalUserAddedToSession(profile->GetOriginalProfile());
359 ChromeLauncherController::ChromeLauncherController(Profile* profile,
360 ash::ShelfModel* model)
362 item_delegate_manager_(NULL),
364 app_sync_ui_state_(NULL),
365 ignore_persist_pinned_state_change_(false) {
367 // If no profile was passed, we take the currently active profile and use it
368 // as the owner of the current desktop.
369 // Use the original profile as on chromeos we may get a temporary off the
370 // record profile, unless in guest session (where off the record profile is
372 Profile* active_profile = ProfileManager::GetActiveUserProfile();
373 profile_ = active_profile->IsGuestSession() ? active_profile :
374 active_profile->GetOriginalProfile();
376 app_sync_ui_state_ = AppSyncUIState::Get(profile_);
377 if (app_sync_ui_state_)
378 app_sync_ui_state_->AddObserver(this);
381 // All profile relevant settings get bound to the current profile.
382 AttachProfile(profile_);
383 model_->AddObserver(this);
385 // In multi profile mode we might have a window manager. We try to create it
386 // here. If the instantiation fails, the manager is not needed.
387 chrome::MultiUserWindowManager::CreateInstance();
389 #if defined(OS_CHROMEOS)
390 // On Chrome OS using multi profile we want to switch the content of the shelf
391 // with a user change. Note that for unit tests the instance can be NULL.
392 if (chrome::MultiUserWindowManager::GetMultiProfileMode() !=
393 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_OFF) {
394 user_switch_observer_.reset(
395 new ChromeLauncherControllerUserSwitchObserverChromeOS(this));
398 // Create our v1/v2 application / browser monitors which will inform the
399 // launcher of status changes.
400 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
401 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED) {
402 // If running in separated destkop mode, we create the multi profile version
403 // of status monitor.
404 browser_status_monitor_.reset(new MultiProfileBrowserStatusMonitor(this));
405 app_window_controller_.reset(
406 new MultiProfileAppWindowLauncherController(this));
408 // Create our v1/v2 application / browser monitors which will inform the
409 // launcher of status changes.
410 browser_status_monitor_.reset(new BrowserStatusMonitor(this));
411 app_window_controller_.reset(new AppWindowLauncherController(this));
414 // Create our v1/v2 application / browser monitors which will inform the
415 // launcher of status changes.
416 browser_status_monitor_.reset(new BrowserStatusMonitor(this));
417 app_window_controller_.reset(new AppWindowLauncherController(this));
420 // Right now ash::Shell isn't created for tests.
421 // TODO(mukai): Allows it to observe display change and write tests.
422 if (ash::Shell::HasInstance()) {
423 ash::Shell::GetInstance()->display_controller()->AddObserver(this);
424 item_delegate_manager_ =
425 ash::Shell::GetInstance()->shelf_item_delegate_manager();
429 ChromeLauncherController::~ChromeLauncherController() {
430 // Reset the BrowserStatusMonitor as it has a weak pointer to this.
431 browser_status_monitor_.reset();
433 // Reset the app window controller here since it has a weak pointer to this.
434 app_window_controller_.reset();
436 for (std::set<ash::Shelf*>::iterator iter = shelves_.begin();
437 iter != shelves_.end();
439 (*iter)->shelf_widget()->shelf_layout_manager()->RemoveObserver(this);
441 model_->RemoveObserver(this);
442 if (ash::Shell::HasInstance())
443 ash::Shell::GetInstance()->display_controller()->RemoveObserver(this);
444 for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin();
445 i != id_to_item_controller_map_.end(); ++i) {
446 int index = model_->ItemIndexByID(i->first);
447 // A "browser proxy" is not known to the model and this removal does
448 // therefore not need to be propagated to the model.
450 model_->items()[index].type != ash::TYPE_BROWSER_SHORTCUT)
451 model_->RemoveItemAt(index);
454 if (ash::Shell::HasInstance())
455 ash::Shell::GetInstance()->RemoveShellObserver(this);
457 // Release all profile dependent resources.
459 if (instance_ == this)
462 // Get rid of the multi user window manager instance.
463 chrome::MultiUserWindowManager::DeleteInstance();
467 ChromeLauncherController* ChromeLauncherController::CreateInstance(
469 ash::ShelfModel* model) {
470 // We do not check here for re-creation of the ChromeLauncherController since
471 // it appears that it might be intentional that the ChromeLauncherController
472 // can be re-created.
473 instance_ = new ChromeLauncherController(profile, model);
477 void ChromeLauncherController::Init() {
478 CreateBrowserShortcutLauncherItem();
479 UpdateAppLaunchersFromPref();
481 // TODO(sky): update unit test so that this test isn't necessary.
482 if (ash::Shell::HasInstance()) {
483 SetShelfAutoHideBehaviorFromPrefs();
484 SetShelfAlignmentFromPrefs();
485 #if defined(OS_CHROMEOS)
486 SetVirtualKeyboardBehaviorFromPrefs();
487 #endif // defined(OS_CHROMEOS)
488 PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_);
489 if (!prefs->FindPreference(prefs::kShelfAlignmentLocal)->HasUserSetting() ||
490 !prefs->FindPreference(prefs::kShelfAutoHideBehaviorLocal)->
492 // This causes OnIsSyncingChanged to be called when the value of
493 // PrefService::IsSyncing() changes.
494 prefs->AddObserver(this);
496 ash::Shell::GetInstance()->AddShellObserver(this);
500 ash::ShelfID ChromeLauncherController::CreateAppLauncherItem(
501 LauncherItemController* controller,
502 const std::string& app_id,
503 ash::ShelfItemStatus status) {
506 // Panels are inserted on the left so as not to push all existing panels over.
507 if (controller->GetShelfItemType() != ash::TYPE_APP_PANEL)
508 index = model_->item_count();
509 return InsertAppLauncherItem(controller,
513 controller->GetShelfItemType());
516 void ChromeLauncherController::SetItemStatus(ash::ShelfID id,
517 ash::ShelfItemStatus status) {
518 int index = model_->ItemIndexByID(id);
519 ash::ShelfItemStatus old_status = model_->items()[index].status;
520 // Since ordinary browser windows are not registered, we might get a negative
522 if (index >= 0 && old_status != status) {
523 ash::ShelfItem item = model_->items()[index];
524 item.status = status;
525 model_->Set(index, item);
529 void ChromeLauncherController::SetItemController(
531 LauncherItemController* controller) {
533 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
534 CHECK(iter != id_to_item_controller_map_.end());
535 controller->set_shelf_id(id);
536 iter->second = controller;
537 // Existing controller is destroyed and replaced by registering again.
538 SetShelfItemDelegate(id, controller);
541 void ChromeLauncherController::CloseLauncherItem(ash::ShelfID id) {
544 // Create a new shortcut controller.
545 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
546 CHECK(iter != id_to_item_controller_map_.end());
547 SetItemStatus(id, ash::STATUS_CLOSED);
548 std::string app_id = iter->second->app_id();
549 iter->second = new AppShortcutLauncherItemController(app_id, this);
550 iter->second->set_shelf_id(id);
551 // Existing controller is destroyed and replaced by registering again.
552 SetShelfItemDelegate(id, iter->second);
554 LauncherItemClosed(id);
558 void ChromeLauncherController::Pin(ash::ShelfID id) {
559 DCHECK(HasItemController(id));
561 int index = model_->ItemIndexByID(id);
564 ash::ShelfItem item = model_->items()[index];
566 if (item.type == ash::TYPE_PLATFORM_APP ||
567 item.type == ash::TYPE_WINDOWED_APP) {
568 item.type = ash::TYPE_APP_SHORTCUT;
569 model_->Set(index, item);
570 } else if (item.type != ash::TYPE_APP_SHORTCUT) {
575 PersistPinnedState();
578 void ChromeLauncherController::Unpin(ash::ShelfID id) {
579 DCHECK(HasItemController(id));
581 LauncherItemController* controller = id_to_item_controller_map_[id];
582 if (controller->type() == LauncherItemController::TYPE_APP ||
583 controller->locked()) {
584 UnpinRunningAppInternal(model_->ItemIndexByID(id));
586 LauncherItemClosed(id);
589 PersistPinnedState();
592 bool ChromeLauncherController::IsPinned(ash::ShelfID id) {
593 int index = model_->ItemIndexByID(id);
596 ash::ShelfItemType type = model_->items()[index].type;
597 return (type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_BROWSER_SHORTCUT);
600 void ChromeLauncherController::TogglePinned(ash::ShelfID id) {
601 if (!HasItemController(id))
602 return; // May happen if item closed with menu open.
610 bool ChromeLauncherController::IsPinnable(ash::ShelfID id) const {
611 int index = model_->ItemIndexByID(id);
615 ash::ShelfItemType type = model_->items()[index].type;
616 return ((type == ash::TYPE_APP_SHORTCUT ||
617 type == ash::TYPE_PLATFORM_APP ||
618 type == ash::TYPE_WINDOWED_APP) &&
622 void ChromeLauncherController::Install(ash::ShelfID id) {
623 if (!HasItemController(id))
626 std::string app_id = GetAppIDForShelfID(id);
627 if (extensions::util::IsExtensionInstalledPermanently(app_id, profile_))
630 LauncherItemController* controller = id_to_item_controller_map_[id];
631 if (controller->type() == LauncherItemController::TYPE_APP) {
632 AppWindowLauncherItemController* app_window_controller =
633 static_cast<AppWindowLauncherItemController*>(controller);
634 app_window_controller->InstallApp();
638 bool ChromeLauncherController::CanInstall(ash::ShelfID id) {
639 int index = model_->ItemIndexByID(id);
643 ash::ShelfItemType type = model_->items()[index].type;
644 if (type != ash::TYPE_PLATFORM_APP)
647 return extensions::util::IsEphemeralApp(GetAppIDForShelfID(id), profile_);
650 void ChromeLauncherController::LockV1AppWithID(
651 const std::string& app_id) {
652 ash::ShelfID id = GetShelfIDForAppID(app_id);
653 if (!IsPinned(id) && !IsWindowedAppInLauncher(app_id)) {
654 CreateAppShortcutLauncherItemWithType(app_id,
655 model_->item_count(),
656 ash::TYPE_WINDOWED_APP);
657 id = GetShelfIDForAppID(app_id);
660 id_to_item_controller_map_[id]->lock();
663 void ChromeLauncherController::UnlockV1AppWithID(const std::string& app_id) {
664 ash::ShelfID id = GetShelfIDForAppID(app_id);
665 CHECK(IsPinned(id) || IsWindowedAppInLauncher(app_id));
667 LauncherItemController* controller = id_to_item_controller_map_[id];
668 controller->unlock();
669 if (!controller->locked() && !IsPinned(id))
670 CloseLauncherItem(id);
673 void ChromeLauncherController::Launch(ash::ShelfID id, int event_flags) {
674 if (!HasItemController(id))
675 return; // In case invoked from menu and item closed while menu up.
676 id_to_item_controller_map_[id]->Launch(ash::LAUNCH_FROM_UNKNOWN, event_flags);
679 void ChromeLauncherController::Close(ash::ShelfID id) {
680 if (!HasItemController(id))
681 return; // May happen if menu closed.
682 id_to_item_controller_map_[id]->Close();
685 bool ChromeLauncherController::IsOpen(ash::ShelfID id) {
686 if (!HasItemController(id))
688 return id_to_item_controller_map_[id]->IsOpen();
691 bool ChromeLauncherController::IsPlatformApp(ash::ShelfID id) {
692 if (!HasItemController(id))
695 std::string app_id = GetAppIDForShelfID(id);
696 const Extension* extension = GetExtensionForAppID(app_id);
697 // An extension can be synced / updated at any time and therefore not be
699 return extension ? extension->is_platform_app() : false;
702 void ChromeLauncherController::LaunchApp(const std::string& app_id,
703 ash::LaunchSource source,
705 // |extension| could be NULL when it is being unloaded for updating.
706 const Extension* extension = GetExtensionForAppID(app_id);
710 if (!extensions::util::IsAppLaunchableWithoutEnabling(app_id, profile_)) {
711 // Do nothing if there is already a running enable flow.
712 if (extension_enable_flow_)
715 extension_enable_flow_.reset(
716 new ExtensionEnableFlow(profile_, app_id, this));
717 extension_enable_flow_->StartForNativeWindow(NULL);
722 if (LaunchedInNativeDesktop(app_id))
726 // The app will be created for the currently active profile.
727 AppLaunchParams params(profile_,
730 chrome::HOST_DESKTOP_TYPE_ASH);
731 if (source != ash::LAUNCH_FROM_UNKNOWN &&
732 app_id == extensions::kWebStoreAppId) {
733 // Get the corresponding source string.
734 std::string source_value = GetSourceFromAppListSource(source);
736 // Set an override URL to include the source.
737 GURL extension_url = extensions::AppLaunchInfo::GetFullLaunchURL(extension);
738 params.override_url = net::AppendQueryParameter(
739 extension_url, extension_urls::kWebstoreSourceField, source_value);
742 params.source = (source == ash::LAUNCH_FROM_UNKNOWN)
743 ? extensions::SOURCE_UNTRACKED
744 : extensions::SOURCE_APP_LAUNCHER;
746 OpenApplication(params);
749 void ChromeLauncherController::ActivateApp(const std::string& app_id,
750 ash::LaunchSource source,
752 // If there is an existing non-shortcut controller for this app, open it.
753 ash::ShelfID id = GetShelfIDForAppID(app_id);
755 LauncherItemController* controller = id_to_item_controller_map_[id];
756 controller->Activate(source);
760 // Create a temporary application launcher item and use it to see if there are
761 // running instances.
762 scoped_ptr<AppShortcutLauncherItemController> app_controller(
763 new AppShortcutLauncherItemController(app_id, this));
764 if (!app_controller->GetRunningApplications().empty())
765 app_controller->Activate(source);
767 LaunchApp(app_id, source, event_flags);
770 extensions::LaunchType ChromeLauncherController::GetLaunchType(
772 DCHECK(HasItemController(id));
774 const Extension* extension = GetExtensionForAppID(
775 id_to_item_controller_map_[id]->app_id());
777 // An extension can be unloaded/updated/unavailable at any time.
779 return extensions::LAUNCH_TYPE_DEFAULT;
781 return extensions::GetLaunchType(extensions::ExtensionPrefs::Get(profile_),
785 ash::ShelfID ChromeLauncherController::GetShelfIDForAppID(
786 const std::string& app_id) {
787 for (IDToItemControllerMap::const_iterator i =
788 id_to_item_controller_map_.begin();
789 i != id_to_item_controller_map_.end(); ++i) {
790 if (i->second->type() == LauncherItemController::TYPE_APP_PANEL)
791 continue; // Don't include panels
792 if (i->second->app_id() == app_id)
798 const std::string& ChromeLauncherController::GetAppIDForShelfID(
800 CHECK(HasItemController(id));
801 return id_to_item_controller_map_[id]->app_id();
804 void ChromeLauncherController::SetAppImage(const std::string& id,
805 const gfx::ImageSkia& image) {
806 // TODO: need to get this working for shortcuts.
807 for (IDToItemControllerMap::const_iterator i =
808 id_to_item_controller_map_.begin();
809 i != id_to_item_controller_map_.end(); ++i) {
810 LauncherItemController* controller = i->second;
811 if (controller->app_id() != id)
813 if (controller->image_set_by_controller())
815 int index = model_->ItemIndexByID(i->first);
818 ash::ShelfItem item = model_->items()[index];
820 model_->Set(index, item);
821 // It's possible we're waiting on more than one item, so don't break.
825 void ChromeLauncherController::OnAutoHideBehaviorChanged(
826 aura::Window* root_window,
827 ash::ShelfAutoHideBehavior new_behavior) {
828 SetShelfAutoHideBehaviorPrefs(new_behavior, root_window);
831 void ChromeLauncherController::SetLauncherItemImage(
832 ash::ShelfID shelf_id,
833 const gfx::ImageSkia& image) {
834 int index = model_->ItemIndexByID(shelf_id);
837 ash::ShelfItem item = model_->items()[index];
839 model_->Set(index, item);
842 bool ChromeLauncherController::CanPin() const {
843 const PrefService::Preference* pref =
844 profile_->GetPrefs()->FindPreference(prefs::kPinnedLauncherApps);
845 return pref && pref->IsUserModifiable();
848 bool ChromeLauncherController::IsAppPinned(const std::string& app_id) {
849 for (IDToItemControllerMap::const_iterator i =
850 id_to_item_controller_map_.begin();
851 i != id_to_item_controller_map_.end(); ++i) {
852 if (IsPinned(i->first) && i->second->app_id() == app_id)
858 bool ChromeLauncherController::IsWindowedAppInLauncher(
859 const std::string& app_id) {
860 int index = model_->ItemIndexByID(GetShelfIDForAppID(app_id));
864 ash::ShelfItemType type = model_->items()[index].type;
865 return type == ash::TYPE_WINDOWED_APP;
868 void ChromeLauncherController::PinAppWithID(const std::string& app_id) {
870 DoPinAppWithID(app_id);
875 void ChromeLauncherController::SetLaunchType(
877 extensions::LaunchType launch_type) {
878 if (!HasItemController(id))
881 extensions::SetLaunchType(
882 extensions::ExtensionSystem::Get(profile_)->extension_service(),
883 id_to_item_controller_map_[id]->app_id(),
887 void ChromeLauncherController::UnpinAppWithID(const std::string& app_id) {
889 DoUnpinAppWithID(app_id);
894 bool ChromeLauncherController::IsLoggedInAsGuest() {
895 return profile_->IsGuestSession();
898 void ChromeLauncherController::CreateNewWindow() {
899 // Use the currently active user.
900 chrome::NewEmptyWindow(profile_, chrome::HOST_DESKTOP_TYPE_ASH);
903 void ChromeLauncherController::CreateNewIncognitoWindow() {
904 // Use the currently active user.
905 chrome::NewEmptyWindow(profile_->GetOffTheRecordProfile(),
906 chrome::HOST_DESKTOP_TYPE_ASH);
909 void ChromeLauncherController::PersistPinnedState() {
910 if (ignore_persist_pinned_state_change_)
912 // It is a coding error to call PersistPinnedState() if the pinned apps are
913 // not user-editable. The code should check earlier and not perform any
914 // modification actions that trigger persisting the state.
916 NOTREACHED() << "Can't pin but pinned state being updated";
919 // Mutating kPinnedLauncherApps is going to notify us and trigger us to
920 // process the change. We don't want that to happen so remove ourselves as a
922 pref_change_registrar_.Remove(prefs::kPinnedLauncherApps);
924 ListPrefUpdate updater(profile_->GetPrefs(), prefs::kPinnedLauncherApps);
926 for (size_t i = 0; i < model_->items().size(); ++i) {
927 if (model_->items()[i].type == ash::TYPE_APP_SHORTCUT) {
928 ash::ShelfID id = model_->items()[i].id;
929 if (HasItemController(id) && IsPinned(id)) {
930 base::DictionaryValue* app_value = ash::CreateAppDict(
931 id_to_item_controller_map_[id]->app_id());
933 updater->Append(app_value);
935 } else if (model_->items()[i].type == ash::TYPE_BROWSER_SHORTCUT) {
936 PersistChromeItemIndex(i);
937 } else if (model_->items()[i].type == ash::TYPE_APP_LIST) {
938 base::DictionaryValue* app_value = ash::CreateAppDict(
939 kAppShelfIdPlaceholder);
941 updater->Append(app_value);
945 pref_change_registrar_.Add(
946 prefs::kPinnedLauncherApps,
947 base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref,
948 base::Unretained(this)));
951 ash::ShelfModel* ChromeLauncherController::model() {
955 Profile* ChromeLauncherController::profile() {
959 ash::ShelfAutoHideBehavior ChromeLauncherController::GetShelfAutoHideBehavior(
960 aura::Window* root_window) const {
961 return GetShelfAutoHideBehaviorFromPrefs(profile_, root_window);
964 bool ChromeLauncherController::CanUserModifyShelfAutoHideBehavior(
965 aura::Window* root_window) const {
966 return profile_->GetPrefs()->
967 FindPreference(prefs::kShelfAutoHideBehaviorLocal)->IsUserModifiable();
970 void ChromeLauncherController::ToggleShelfAutoHideBehavior(
971 aura::Window* root_window) {
972 ash::ShelfAutoHideBehavior behavior = GetShelfAutoHideBehavior(root_window) ==
973 ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS ?
974 ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER :
975 ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS;
976 SetShelfAutoHideBehaviorPrefs(behavior, root_window);
980 void ChromeLauncherController::UpdateAppState(content::WebContents* contents,
981 AppState app_state) {
982 std::string app_id = app_tab_helper_->GetAppID(contents);
984 // Check if the gMail app is loaded and it matches the given content.
985 // This special treatment is needed to address crbug.com/234268.
986 if (app_id.empty() && ContentCanBeHandledByGmailApp(contents))
987 app_id = kGmailAppId;
989 // Check the old |app_id| for a tab. If the contents has changed we need to
990 // remove it from the previous app.
991 if (web_contents_to_app_id_.find(contents) != web_contents_to_app_id_.end()) {
992 std::string last_app_id = web_contents_to_app_id_[contents];
993 if (last_app_id != app_id) {
994 ash::ShelfID id = GetShelfIDForAppID(last_app_id);
996 // Since GetAppState() will use |web_contents_to_app_id_| we remove
997 // the connection before calling it.
998 web_contents_to_app_id_.erase(contents);
999 SetItemStatus(id, GetAppState(last_app_id));
1004 if (app_state == APP_STATE_REMOVED)
1005 web_contents_to_app_id_.erase(contents);
1007 web_contents_to_app_id_[contents] = app_id;
1009 ash::ShelfID id = GetShelfIDForAppID(app_id);
1011 SetItemStatus(id, (app_state == APP_STATE_WINDOW_ACTIVE ||
1012 app_state == APP_STATE_ACTIVE) ? ash::STATUS_ACTIVE :
1013 GetAppState(app_id));
1017 ash::ShelfID ChromeLauncherController::GetShelfIDForWebContents(
1018 content::WebContents* contents) {
1021 std::string app_id = app_tab_helper_->GetAppID(contents);
1023 if (app_id.empty() && ContentCanBeHandledByGmailApp(contents))
1024 app_id = kGmailAppId;
1026 ash::ShelfID id = GetShelfIDForAppID(app_id);
1028 if (app_id.empty() || !id) {
1029 int browser_index = model_->GetItemIndexForType(ash::TYPE_BROWSER_SHORTCUT);
1030 return model_->items()[browser_index].id;
1036 void ChromeLauncherController::SetRefocusURLPatternForTest(ash::ShelfID id,
1038 DCHECK(HasItemController(id));
1039 LauncherItemController* controller = id_to_item_controller_map_[id];
1041 int index = model_->ItemIndexByID(id);
1043 NOTREACHED() << "Invalid launcher id";
1047 ash::ShelfItemType type = model_->items()[index].type;
1048 if (type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_WINDOWED_APP) {
1049 AppShortcutLauncherItemController* app_controller =
1050 static_cast<AppShortcutLauncherItemController*>(controller);
1051 app_controller->set_refocus_url(url);
1053 NOTREACHED() << "Invalid launcher type";
1057 const Extension* ChromeLauncherController::GetExtensionForAppID(
1058 const std::string& app_id) const {
1059 return extensions::ExtensionRegistry::Get(profile_)->GetExtensionById(
1060 app_id, extensions::ExtensionRegistry::EVERYTHING);
1063 void ChromeLauncherController::ActivateWindowOrMinimizeIfActive(
1064 ui::BaseWindow* window,
1065 bool allow_minimize) {
1066 // In separated desktop mode we might have to teleport a window back to the
1068 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
1069 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED) {
1070 aura::Window* native_window = window->GetNativeWindow();
1071 const std::string& current_user =
1072 multi_user_util::GetUserIDFromProfile(profile());
1073 chrome::MultiUserWindowManager* manager =
1074 chrome::MultiUserWindowManager::GetInstance();
1075 if (!manager->IsWindowOnDesktopOfUser(native_window, current_user)) {
1076 ash::MultiProfileUMA::RecordTeleportAction(
1077 ash::MultiProfileUMA::TELEPORT_WINDOW_RETURN_BY_LAUNCHER);
1078 manager->ShowWindowForUser(native_window, current_user);
1084 if (window->IsActive() && allow_minimize) {
1085 if (CommandLine::ForCurrentProcess()->HasSwitch(
1086 switches::kDisableMinimizeOnSecondLauncherItemClick)) {
1087 AnimateWindow(window->GetNativeWindow(),
1088 wm::WINDOW_ANIMATION_TYPE_BOUNCE);
1098 void ChromeLauncherController::OnShelfCreated(ash::Shelf* shelf) {
1099 shelves_.insert(shelf);
1100 shelf->shelf_widget()->shelf_layout_manager()->AddObserver(this);
1103 void ChromeLauncherController::OnShelfDestroyed(ash::Shelf* shelf) {
1104 shelves_.erase(shelf);
1105 // RemoveObserver is not called here, since by the time this method is called
1106 // Shelf is already in its destructor.
1109 void ChromeLauncherController::ShelfItemAdded(int index) {
1110 // The app list launcher can get added to the shelf after we applied the
1111 // preferences. In that case the item might be at the wrong spot. As such we
1112 // call the function again.
1113 if (model_->items()[index].type == ash::TYPE_APP_LIST)
1114 UpdateAppLaunchersFromPref();
1117 void ChromeLauncherController::ShelfItemRemoved(int index, ash::ShelfID id) {
1120 void ChromeLauncherController::ShelfItemMoved(int start_index,
1122 const ash::ShelfItem& item = model_->items()[target_index];
1123 // We remember the moved item position if it is either pinnable or
1124 // it is the app list with the alternate shelf layout.
1125 if ((HasItemController(item.id) && IsPinned(item.id)) ||
1126 item.type == ash::TYPE_APP_LIST)
1127 PersistPinnedState();
1130 void ChromeLauncherController::ShelfItemChanged(
1132 const ash::ShelfItem& old_item) {
1135 void ChromeLauncherController::ShelfStatusChanged() {
1138 void ChromeLauncherController::ActiveUserChanged(
1139 const std::string& user_email) {
1140 // Store the order of running applications for the user which gets inactive.
1141 RememberUnpinnedRunningApplicationOrder();
1142 // Coming here the default profile is already switched. All profile specific
1143 // resources get released and the new profile gets attached instead.
1145 // When coming here, the active user has already be changed so that we can
1146 // set it as active.
1147 AttachProfile(ProfileManager::GetActiveUserProfile());
1148 // Update the V1 applications.
1149 browser_status_monitor_->ActiveUserChanged(user_email);
1150 // Switch the running applications to the new user.
1151 app_window_controller_->ActiveUserChanged(user_email);
1152 // Update the user specific shell properties from the new user profile.
1153 UpdateAppLaunchersFromPref();
1154 SetShelfAlignmentFromPrefs();
1155 SetShelfAutoHideBehaviorFromPrefs();
1156 SetShelfBehaviorsFromPrefs();
1157 #if defined(OS_CHROMEOS)
1158 SetVirtualKeyboardBehaviorFromPrefs();
1159 #endif // defined(OS_CHROMEOS)
1160 // Restore the order of running, but unpinned applications for the activated
1162 RestoreUnpinnedRunningApplicationOrder(user_email);
1163 // Inform the system tray of the change.
1164 ash::Shell::GetInstance()->system_tray_delegate()->ActiveUserWasChanged();
1165 // Force on-screen keyboard to reset.
1166 if (keyboard::IsKeyboardEnabled())
1167 ash::Shell::GetInstance()->CreateKeyboard();
1170 void ChromeLauncherController::AdditionalUserAddedToSession(Profile* profile) {
1171 // Switch the running applications to the new user.
1172 app_window_controller_->AdditionalUserAddedToSession(profile);
1175 void ChromeLauncherController::OnExtensionLoaded(
1176 content::BrowserContext* browser_context,
1177 const Extension* extension) {
1178 if (IsAppPinned(extension->id())) {
1179 // Clear and re-fetch to ensure icon is up-to-date.
1180 app_icon_loader_->ClearImage(extension->id());
1181 app_icon_loader_->FetchImage(extension->id());
1184 UpdateAppLaunchersFromPref();
1187 void ChromeLauncherController::OnExtensionUnloaded(
1188 content::BrowserContext* browser_context,
1189 const Extension* extension,
1190 UnloadedExtensionInfo::Reason reason) {
1191 const std::string& id = extension->id();
1192 const Profile* profile = Profile::FromBrowserContext(browser_context);
1194 // Since we might have windowed apps of this type which might have
1195 // outstanding locks which needs to be removed.
1196 if (GetShelfIDForAppID(id) &&
1197 reason == UnloadedExtensionInfo::REASON_UNINSTALL) {
1198 CloseWindowedAppsFromRemovedExtension(id, profile);
1201 if (IsAppPinned(id)) {
1202 if (reason == UnloadedExtensionInfo::REASON_UNINSTALL) {
1203 if (profile == profile_) {
1204 DoUnpinAppWithID(id);
1206 app_icon_loader_->ClearImage(id);
1208 app_icon_loader_->UpdateImage(id);
1213 void ChromeLauncherController::OnShelfAlignmentChanged(
1214 aura::Window* root_window) {
1215 const char* pref_value = NULL;
1216 switch (ash::Shell::GetInstance()->GetShelfAlignment(root_window)) {
1217 case ash::SHELF_ALIGNMENT_BOTTOM:
1218 pref_value = ash::kShelfAlignmentBottom;
1220 case ash::SHELF_ALIGNMENT_LEFT:
1221 pref_value = ash::kShelfAlignmentLeft;
1223 case ash::SHELF_ALIGNMENT_RIGHT:
1224 pref_value = ash::kShelfAlignmentRight;
1226 case ash::SHELF_ALIGNMENT_TOP:
1227 pref_value = ash::kShelfAlignmentTop;
1230 UpdatePerDisplayPref(
1231 profile_->GetPrefs(), root_window, prefs::kShelfAlignment, pref_value);
1233 if (root_window == ash::Shell::GetPrimaryRootWindow()) {
1234 // See comment in |kShelfAlignment| about why we have two prefs here.
1235 profile_->GetPrefs()->SetString(prefs::kShelfAlignmentLocal, pref_value);
1236 profile_->GetPrefs()->SetString(prefs::kShelfAlignment, pref_value);
1240 void ChromeLauncherController::OnDisplayConfigurationChanged() {
1241 SetShelfBehaviorsFromPrefs();
1244 void ChromeLauncherController::OnIsSyncingChanged() {
1245 PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(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 if (!HasItemController(item.id) ||
1278 !GetShelfIDForAppID(id_to_item_controller_map_[item.id]->app_id()))
1279 return ChromeLauncherAppMenuItems().Pass();
1281 return id_to_item_controller_map_[item.id]->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 = id_to_item_controller_map_[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 = id_to_item_controller_map_[id];
1303 if (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 (!MatchPattern(url.path(), "/mail/mu/*") &&
1330 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 FaviconTabHelper* favicon_tab_helper =
1344 FaviconTabHelper::FromWebContents(web_contents);
1345 gfx::Image result = favicon_tab_helper->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 item_delegate_manager_ = manager;
1394 void ChromeLauncherController::RememberUnpinnedRunningApplicationOrder() {
1395 RunningAppListIds list;
1396 for (int i = 0; i < model_->item_count(); i++) {
1397 ash::ShelfItemType type = model_->items()[i].type;
1398 if (type == ash::TYPE_WINDOWED_APP || type == ash::TYPE_PLATFORM_APP)
1399 list.push_back(GetAppIDForShelfID(model_->items()[i].id));
1401 last_used_running_application_order_[
1402 multi_user_util::GetUserIDFromProfile(profile_)] = list;
1405 void ChromeLauncherController::RestoreUnpinnedRunningApplicationOrder(
1406 const std::string& user_id) {
1407 const RunningAppListIdMap::iterator app_id_list =
1408 last_used_running_application_order_.find(user_id);
1409 if (app_id_list == last_used_running_application_order_.end())
1412 // Find the first insertion point for running applications.
1413 int running_index = model_->FirstRunningAppIndex();
1414 for (RunningAppListIds::iterator app_id = app_id_list->second.begin();
1415 app_id != app_id_list->second.end(); ++app_id) {
1416 ash::ShelfID shelf_id = GetShelfIDForAppID(*app_id);
1418 int app_index = model_->ItemIndexByID(shelf_id);
1419 DCHECK_GE(app_index, 0);
1420 ash::ShelfItemType type = model_->items()[app_index].type;
1421 if (type == ash::TYPE_WINDOWED_APP || type == ash::TYPE_PLATFORM_APP) {
1422 if (running_index != app_index)
1423 model_->Move(running_index, app_index);
1430 ash::ShelfID ChromeLauncherController::CreateAppShortcutLauncherItemWithType(
1431 const std::string& app_id,
1433 ash::ShelfItemType shelf_item_type) {
1434 AppShortcutLauncherItemController* controller =
1435 new AppShortcutLauncherItemController(app_id, this);
1436 ash::ShelfID shelf_id = InsertAppLauncherItem(
1437 controller, app_id, ash::STATUS_CLOSED, index, shelf_item_type);
1441 LauncherItemController* ChromeLauncherController::GetLauncherItemController(
1442 const ash::ShelfID id) {
1443 if (!HasItemController(id))
1445 return id_to_item_controller_map_[id];
1448 bool ChromeLauncherController::IsBrowserFromActiveUser(Browser* browser) {
1449 // If running multi user mode with separate desktops, we have to check if the
1450 // browser is from the active user.
1451 if (chrome::MultiUserWindowManager::GetMultiProfileMode() !=
1452 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED)
1454 return multi_user_util::IsProfileFromActiveUser(browser->profile());
1457 bool ChromeLauncherController::ShelfBoundsChangesProbablyWithUser(
1458 aura::Window* root_window,
1459 const std::string& user_id) const {
1460 Profile* other_profile = multi_user_util::GetProfileFromUserID(user_id);
1461 DCHECK_NE(other_profile, profile_);
1463 // Note: The Auto hide state from preferences is not the same as the actual
1464 // visibility of the shelf. Depending on all the various states (full screen,
1465 // no window on desktop, multi user, ..) the shelf could be shown - or not.
1466 bool currently_shown = ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER ==
1467 GetShelfAutoHideBehaviorFromPrefs(profile_, root_window);
1468 bool other_shown = ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER ==
1469 GetShelfAutoHideBehaviorFromPrefs(other_profile, root_window);
1471 return currently_shown != other_shown ||
1472 GetShelfAlignmentFromPrefs(profile_, root_window) !=
1473 GetShelfAlignmentFromPrefs(other_profile, root_window);
1476 void ChromeLauncherController::LauncherItemClosed(ash::ShelfID id) {
1477 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
1478 CHECK(iter != id_to_item_controller_map_.end());
1479 CHECK(iter->second);
1480 app_icon_loader_->ClearImage(iter->second->app_id());
1481 id_to_item_controller_map_.erase(iter);
1482 int index = model_->ItemIndexByID(id);
1483 // A "browser proxy" is not known to the model and this removal does
1484 // therefore not need to be propagated to the model.
1486 model_->RemoveItemAt(index);
1489 void ChromeLauncherController::DoPinAppWithID(const std::string& app_id) {
1490 // If there is an item, do nothing and return.
1491 if (IsAppPinned(app_id))
1494 ash::ShelfID shelf_id = GetShelfIDForAppID(app_id);
1496 // App item exists, pin it
1499 // Otherwise, create a shortcut item for it.
1500 CreateAppShortcutLauncherItem(app_id, model_->item_count());
1502 PersistPinnedState();
1506 void ChromeLauncherController::DoUnpinAppWithID(const std::string& app_id) {
1507 ash::ShelfID shelf_id = GetShelfIDForAppID(app_id);
1508 if (shelf_id && IsPinned(shelf_id))
1512 int ChromeLauncherController::PinRunningAppInternal(int index,
1513 ash::ShelfID shelf_id) {
1514 int running_index = model_->ItemIndexByID(shelf_id);
1515 ash::ShelfItem item = model_->items()[running_index];
1516 DCHECK(item.type == ash::TYPE_WINDOWED_APP ||
1517 item.type == ash::TYPE_PLATFORM_APP);
1518 item.type = ash::TYPE_APP_SHORTCUT;
1519 model_->Set(running_index, item);
1520 // The |ShelfModel|'s weight system might reposition the item to a
1521 // new index, so we get the index again.
1522 running_index = model_->ItemIndexByID(shelf_id);
1523 if (running_index < index)
1525 if (running_index != index)
1526 model_->Move(running_index, index);
1530 void ChromeLauncherController::UnpinRunningAppInternal(int index) {
1531 DCHECK_GE(index, 0);
1532 ash::ShelfItem item = model_->items()[index];
1533 DCHECK_EQ(item.type, ash::TYPE_APP_SHORTCUT);
1534 item.type = ash::TYPE_WINDOWED_APP;
1535 // A platform app and a windowed app are sharing TYPE_APP_SHORTCUT. As such
1536 // we have to check here what this was before it got a shortcut.
1537 if (HasItemController(item.id) &&
1538 id_to_item_controller_map_[item.id]->type() ==
1539 LauncherItemController::TYPE_APP)
1540 item.type = ash::TYPE_PLATFORM_APP;
1541 model_->Set(index, item);
1544 void ChromeLauncherController::UpdateAppLaunchersFromPref() {
1545 // There are various functions which will trigger a |PersistPinnedState| call
1546 // like a direct call to |DoPinAppWithID|, or an indirect call to the menu
1547 // model which will use weights to re-arrange the icons to new positions.
1548 // Since this function is meant to synchronize the "is state" with the
1549 // "sync state", it makes no sense to store any changes by this function back
1550 // into the pref state. Therefore we tell |persistPinnedState| to ignore any
1551 // invocations while we are running.
1552 base::AutoReset<bool> auto_reset(&ignore_persist_pinned_state_change_, true);
1553 std::vector<std::string> pinned_apps = GetListOfPinnedAppsAndBrowser();
1556 int max_index = model_->item_count();
1558 // When one of the two special items cannot be moved (and we do not know where
1559 // yet), we remember the current location in one of these variables.
1560 int chrome_index = -1;
1561 int app_list_index = -1;
1563 // Walk the model and |pinned_apps| from the pref lockstep, adding and
1564 // removing items as necessary. NB: This code uses plain old indexing instead
1565 // of iterators because of model mutations as part of the loop.
1566 std::vector<std::string>::const_iterator pref_app_id(pinned_apps.begin());
1567 for (; index < max_index && pref_app_id != pinned_apps.end(); ++index) {
1568 // Check if we have an item which we need to handle.
1569 if (*pref_app_id == extension_misc::kChromeAppId ||
1570 *pref_app_id == kAppShelfIdPlaceholder ||
1571 IsAppPinned(*pref_app_id)) {
1572 for (; index < max_index; ++index) {
1573 const ash::ShelfItem& item(model_->items()[index]);
1574 bool is_app_list = item.type == ash::TYPE_APP_LIST;
1575 bool is_chrome = item.type == ash::TYPE_BROWSER_SHORTCUT;
1576 if (item.type != ash::TYPE_APP_SHORTCUT && !is_app_list && !is_chrome)
1578 IDToItemControllerMap::const_iterator entry =
1579 id_to_item_controller_map_.find(item.id);
1580 if ((kAppShelfIdPlaceholder == *pref_app_id && is_app_list) ||
1581 (extension_misc::kChromeAppId == *pref_app_id && is_chrome) ||
1582 (entry != id_to_item_controller_map_.end() &&
1583 entry->second->app_id() == *pref_app_id)) {
1584 // Check if an item needs to be moved here.
1585 MoveChromeOrApplistToFinalPosition(
1586 is_chrome, is_app_list, index, &chrome_index, &app_list_index);
1590 if (is_chrome || is_app_list) {
1591 // We cannot delete any of these shortcuts. As such we remember
1592 // their positions and move them later where they belong.
1594 chrome_index = index;
1596 app_list_index = index;
1597 // And skip the item - or exit the loop if end is reached (note that
1598 // in that case we will reduce the index again by one and this only
1599 // compensates for it).
1600 if (index >= max_index - 1)
1604 // Check if this is a platform or a windowed app.
1605 if (item.type == ash::TYPE_APP_SHORTCUT &&
1606 (id_to_item_controller_map_[item.id]->locked() ||
1607 id_to_item_controller_map_[item.id]->type() ==
1608 LauncherItemController::TYPE_APP)) {
1609 // Note: This will not change the amount of items (|max_index|).
1610 // Even changes to the actual |index| due to item weighting
1611 // changes should be fine.
1612 UnpinRunningAppInternal(index);
1614 LauncherItemClosed(item.id);
1621 // If the item wasn't found, that means id_to_item_controller_map_
1623 DCHECK(index <= max_index);
1625 // Check if the item was already running but not yet pinned.
1626 ash::ShelfID shelf_id = GetShelfIDForAppID(*pref_app_id);
1628 // This app is running but not yet pinned. So pin and move it.
1629 index = PinRunningAppInternal(index, shelf_id);
1631 // This app wasn't pinned before, insert a new entry.
1632 shelf_id = CreateAppShortcutLauncherItem(*pref_app_id, index);
1634 index = model_->ItemIndexByID(shelf_id);
1640 // Remove any trailing existing items.
1641 while (index < model_->item_count()) {
1642 const ash::ShelfItem& item(model_->items()[index]);
1643 if (item.type == ash::TYPE_APP_SHORTCUT) {
1644 if (id_to_item_controller_map_[item.id]->locked() ||
1645 id_to_item_controller_map_[item.id]->type() ==
1646 LauncherItemController::TYPE_APP)
1647 UnpinRunningAppInternal(index);
1649 LauncherItemClosed(item.id);
1651 if (item.type == ash::TYPE_BROWSER_SHORTCUT)
1652 chrome_index = index;
1653 else if (item.type == ash::TYPE_APP_LIST)
1654 app_list_index = index;
1659 // Append unprocessed items from the pref to the end of the model.
1660 for (; pref_app_id != pinned_apps.end(); ++pref_app_id) {
1661 // All items but the chrome and / or app list shortcut needs to be added.
1662 bool is_chrome = *pref_app_id == extension_misc::kChromeAppId;
1663 bool is_app_list = *pref_app_id == kAppShelfIdPlaceholder;
1664 // Coming here we know the next item which can be finalized, either the
1665 // chrome item or the app launcher. The final position is the end of the
1666 // list. The menu model will make sure that the item is grouped according
1667 // to its weight (which we do not know here).
1668 if (!is_chrome && !is_app_list) {
1669 DoPinAppWithID(*pref_app_id);
1670 int target_index = FindInsertionPoint(false);
1671 ash::ShelfID id = GetShelfIDForAppID(*pref_app_id);
1672 int source_index = model_->ItemIndexByID(id);
1673 if (source_index != target_index)
1674 model_->Move(source_index, target_index);
1676 // Needed for the old layout - the weight might force it to be lower in
1678 if (app_list_index != -1 && target_index <= app_list_index)
1681 int target_index = FindInsertionPoint(is_app_list);
1682 MoveChromeOrApplistToFinalPosition(
1683 is_chrome, is_app_list, target_index, &chrome_index, &app_list_index);
1688 void ChromeLauncherController::SetShelfAutoHideBehaviorPrefs(
1689 ash::ShelfAutoHideBehavior behavior,
1690 aura::Window* root_window) {
1691 const char* value = NULL;
1693 case ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS:
1694 value = ash::kShelfAutoHideBehaviorAlways;
1696 case ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER:
1697 value = ash::kShelfAutoHideBehaviorNever;
1699 case ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN:
1700 // This one should not be a valid preference option for now. We only want
1701 // to completely hide it when we run in app mode - or while we temporarily
1702 // hide the shelf as part of an animation (e.g. the multi user change).
1706 UpdatePerDisplayPref(
1707 profile_->GetPrefs(), root_window, prefs::kShelfAutoHideBehavior, value);
1709 if (root_window == ash::Shell::GetPrimaryRootWindow()) {
1710 // See comment in |kShelfAlignment| about why we have two prefs here.
1711 profile_->GetPrefs()->SetString(prefs::kShelfAutoHideBehaviorLocal, value);
1712 profile_->GetPrefs()->SetString(prefs::kShelfAutoHideBehavior, value);
1716 void ChromeLauncherController::SetShelfAutoHideBehaviorFromPrefs() {
1717 aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
1719 for (aura::Window::Windows::const_iterator iter = root_windows.begin();
1720 iter != root_windows.end(); ++iter) {
1721 ash::Shell::GetInstance()->SetShelfAutoHideBehavior(
1722 GetShelfAutoHideBehavior(*iter), *iter);
1726 void ChromeLauncherController::SetShelfAlignmentFromPrefs() {
1727 if (!ash::ShelfWidget::ShelfAlignmentAllowed())
1730 aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
1732 for (aura::Window::Windows::const_iterator iter = root_windows.begin();
1733 iter != root_windows.end(); ++iter) {
1734 ash::Shell::GetInstance()->SetShelfAlignment(
1735 GetShelfAlignmentFromPrefs(profile_, *iter), *iter);
1739 void ChromeLauncherController::SetShelfBehaviorsFromPrefs() {
1740 SetShelfAutoHideBehaviorFromPrefs();
1741 SetShelfAlignmentFromPrefs();
1744 #if defined(OS_CHROMEOS)
1745 void ChromeLauncherController::SetVirtualKeyboardBehaviorFromPrefs() {
1746 const PrefService* service = profile_->GetPrefs();
1747 const bool was_enabled = keyboard::IsKeyboardEnabled();
1748 if (!service->HasPrefPath(prefs::kTouchVirtualKeyboardEnabled)) {
1749 keyboard::SetKeyboardShowOverride(keyboard::KEYBOARD_SHOW_OVERRIDE_NONE);
1751 const bool enable = service->GetBoolean(
1752 prefs::kTouchVirtualKeyboardEnabled);
1753 keyboard::SetKeyboardShowOverride(
1754 enable ? keyboard::KEYBOARD_SHOW_OVERRIDE_ENABLED
1755 : keyboard::KEYBOARD_SHOW_OVERRIDE_DISABLED);
1757 const bool is_enabled = keyboard::IsKeyboardEnabled();
1758 if (was_enabled && !is_enabled)
1759 ash::Shell::GetInstance()->DeactivateKeyboard();
1760 else if (is_enabled && !was_enabled)
1761 ash::Shell::GetInstance()->CreateKeyboard();
1763 #endif // defined(OS_CHROMEOS)
1765 ash::ShelfItemStatus ChromeLauncherController::GetAppState(
1766 const std::string& app_id) {
1767 ash::ShelfItemStatus status = ash::STATUS_CLOSED;
1768 for (WebContentsToAppIDMap::iterator it = web_contents_to_app_id_.begin();
1769 it != web_contents_to_app_id_.end();
1771 if (it->second == app_id) {
1772 Browser* browser = chrome::FindBrowserWithWebContents(it->first);
1773 // Usually there should never be an item in our |web_contents_to_app_id_|
1774 // list which got deleted already. However - in some situations e.g.
1775 // Browser::SwapTabContent there is temporarily no associated browser.
1778 if (browser->window()->IsActive()) {
1779 return browser->tab_strip_model()->GetActiveWebContents() == it->first ?
1780 ash::STATUS_ACTIVE : ash::STATUS_RUNNING;
1782 status = ash::STATUS_RUNNING;
1789 ash::ShelfID ChromeLauncherController::InsertAppLauncherItem(
1790 LauncherItemController* controller,
1791 const std::string& app_id,
1792 ash::ShelfItemStatus status,
1794 ash::ShelfItemType shelf_item_type) {
1795 ash::ShelfID id = model_->next_id();
1796 CHECK(!HasItemController(id));
1798 id_to_item_controller_map_[id] = controller;
1799 controller->set_shelf_id(id);
1801 ash::ShelfItem item;
1802 item.type = shelf_item_type;
1803 item.image = extensions::util::GetDefaultAppIcon();
1805 ash::ShelfItemStatus new_state = GetAppState(app_id);
1806 if (new_state != ash::STATUS_CLOSED)
1809 item.status = status;
1811 model_->AddAt(index, item);
1813 app_icon_loader_->FetchImage(app_id);
1815 SetShelfItemDelegate(id, controller);
1820 bool ChromeLauncherController::HasItemController(ash::ShelfID id) const {
1821 return id_to_item_controller_map_.find(id) !=
1822 id_to_item_controller_map_.end();
1825 std::vector<content::WebContents*>
1826 ChromeLauncherController::GetV1ApplicationsFromController(
1827 LauncherItemController* controller) {
1828 DCHECK(controller->type() == LauncherItemController::TYPE_SHORTCUT);
1829 AppShortcutLauncherItemController* app_controller =
1830 static_cast<AppShortcutLauncherItemController*>(controller);
1831 return app_controller->GetRunningApplications();
1834 BrowserShortcutLauncherItemController*
1835 ChromeLauncherController::GetBrowserShortcutLauncherItemController() {
1836 for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin();
1837 i != id_to_item_controller_map_.end(); ++i) {
1838 int index = model_->ItemIndexByID(i->first);
1839 const ash::ShelfItem& item = model_->items()[index];
1840 if (item.type == ash::TYPE_BROWSER_SHORTCUT)
1841 return static_cast<BrowserShortcutLauncherItemController*>(i->second);
1843 // Create a LauncherItemController for the Browser shortcut if it does not
1845 ash::ShelfID id = CreateBrowserShortcutLauncherItem();
1846 DCHECK(id_to_item_controller_map_[id]);
1847 return static_cast<BrowserShortcutLauncherItemController*>(
1848 id_to_item_controller_map_[id]);
1851 ash::ShelfID ChromeLauncherController::CreateBrowserShortcutLauncherItem() {
1852 ash::ShelfItem browser_shortcut;
1853 browser_shortcut.type = ash::TYPE_BROWSER_SHORTCUT;
1854 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
1855 browser_shortcut.image = *rb.GetImageSkiaNamed(IDR_PRODUCT_LOGO_32);
1856 ash::ShelfID id = model_->next_id();
1857 size_t index = GetChromeIconIndexForCreation();
1858 model_->AddAt(index, browser_shortcut);
1859 id_to_item_controller_map_[id] =
1860 new BrowserShortcutLauncherItemController(this);
1861 id_to_item_controller_map_[id]->set_shelf_id(id);
1862 // ShelfItemDelegateManager owns BrowserShortcutLauncherItemController.
1863 SetShelfItemDelegate(id, id_to_item_controller_map_[id]);
1867 void ChromeLauncherController::PersistChromeItemIndex(int index) {
1868 profile_->GetPrefs()->SetInteger(prefs::kShelfChromeIconIndex, index);
1871 int ChromeLauncherController::GetChromeIconIndexFromPref() const {
1872 size_t index = profile_->GetPrefs()->GetInteger(prefs::kShelfChromeIconIndex);
1873 const base::ListValue* pinned_apps_pref =
1874 profile_->GetPrefs()->GetList(prefs::kPinnedLauncherApps);
1875 return std::max(static_cast<size_t>(0),
1876 std::min(pinned_apps_pref->GetSize(), index));
1879 void ChromeLauncherController::MoveChromeOrApplistToFinalPosition(
1884 int* app_list_index) {
1885 if (is_chrome && *chrome_index != -1) {
1886 model_->Move(*chrome_index, target_index);
1887 if (*app_list_index != -1 &&
1888 *chrome_index < *app_list_index &&
1889 target_index > *app_list_index)
1890 --(*app_list_index);
1892 } else if (is_app_list && *app_list_index != -1) {
1893 model_->Move(*app_list_index, target_index);
1894 if (*chrome_index != -1 &&
1895 *app_list_index < *chrome_index &&
1896 target_index > *chrome_index)
1898 *app_list_index = -1;
1902 int ChromeLauncherController::FindInsertionPoint(bool is_app_list) {
1903 // Keeping this change small to backport to M33&32 (see crbug.com/329597).
1904 // TODO(skuhne): With the removal of the legacy shelf layout we should remove
1905 // the ability to move the app list item since this was never used. We should
1906 // instead ask the ShelfModel::ValidateInsertionIndex or similir for an index.
1910 for (int i = model_->item_count() - 1; i > 0; --i) {
1911 ash::ShelfItemType type = model_->items()[i].type;
1912 if (type == ash::TYPE_APP_SHORTCUT ||
1913 (is_app_list && type == ash::TYPE_APP_LIST) ||
1914 type == ash::TYPE_BROWSER_SHORTCUT) {
1921 int ChromeLauncherController::GetChromeIconIndexForCreation() {
1922 // We get the list of pinned apps as they currently would get pinned.
1923 // Within this list the chrome icon will be the correct location.
1924 std::vector<std::string> pinned_apps = GetListOfPinnedAppsAndBrowser();
1926 std::vector<std::string>::iterator it =
1927 std::find(pinned_apps.begin(),
1929 std::string(extension_misc::kChromeAppId));
1930 DCHECK(it != pinned_apps.end());
1931 int index = it - pinned_apps.begin();
1933 // We should do here a comparison between the is state and the "want to be"
1934 // state since some apps might be able to pin but are not yet. Instead - for
1935 // the time being we clamp against the amount of known items and wait for the
1936 // next |UpdateAppLaunchersFromPref()| call to correct it - it will come since
1937 // the pinning will be done then.
1938 return std::min(model_->item_count(), index);
1941 std::vector<std::string>
1942 ChromeLauncherController::GetListOfPinnedAppsAndBrowser() {
1943 // Adding the app list item to the list of items requires that the ID is not
1944 // a valid and known ID for the extension system. The ID was constructed that
1945 // way - but just to make sure...
1946 DCHECK(!app_tab_helper_->IsValidIDForCurrentUser(kAppShelfIdPlaceholder));
1948 std::vector<std::string> pinned_apps;
1950 // Get the new incarnation of the list.
1951 const base::ListValue* pinned_apps_pref =
1952 profile_->GetPrefs()->GetList(prefs::kPinnedLauncherApps);
1954 // Keep track of the addition of the chrome and the app list icon.
1955 bool chrome_icon_added = false;
1956 bool app_list_icon_added = false;
1957 size_t chrome_icon_index = GetChromeIconIndexFromPref();
1959 // See if the chrome string is already in the pinned list and remove it if
1961 base::Value* chrome_app = ash::CreateAppDict(extension_misc::kChromeAppId);
1963 chrome_icon_added = pinned_apps_pref->Find(*chrome_app) !=
1964 pinned_apps_pref->end();
1968 for (size_t index = 0; index < pinned_apps_pref->GetSize(); ++index) {
1969 // We need to position the chrome icon relative to it's place in the pinned
1970 // preference list - even if an item of that list isn't shown yet.
1971 if (index == chrome_icon_index && !chrome_icon_added) {
1972 pinned_apps.push_back(extension_misc::kChromeAppId);
1973 chrome_icon_added = true;
1975 const base::DictionaryValue* app = NULL;
1977 if (pinned_apps_pref->GetDictionary(index, &app) &&
1978 app->GetString(ash::kPinnedAppsPrefAppIDPath, &app_id) &&
1979 (std::find(pinned_apps.begin(), pinned_apps.end(), app_id) ==
1980 pinned_apps.end())) {
1981 if (app_id == extension_misc::kChromeAppId) {
1982 chrome_icon_added = true;
1983 pinned_apps.push_back(extension_misc::kChromeAppId);
1984 } else if (app_id == kAppShelfIdPlaceholder) {
1985 app_list_icon_added = true;
1986 pinned_apps.push_back(kAppShelfIdPlaceholder);
1987 } else if (app_tab_helper_->IsValidIDForCurrentUser(app_id)) {
1988 // Note: In multi profile scenarios we only want to show pinnable apps
1989 // here which is correct. Running applications from the other users will
1990 // continue to run. So no need for multi profile modifications.
1991 pinned_apps.push_back(app_id);
1996 // If not added yet, the chrome item will be the last item in the list.
1997 if (!chrome_icon_added)
1998 pinned_apps.push_back(extension_misc::kChromeAppId);
2000 // If not added yet, add the app list item either at the end or at the
2001 // beginning - depending on the shelf layout.
2002 if (!app_list_icon_added) {
2003 pinned_apps.insert(pinned_apps.begin(), kAppShelfIdPlaceholder);
2008 bool ChromeLauncherController::IsIncognito(
2009 const content::WebContents* web_contents) const {
2010 const Profile* profile =
2011 Profile::FromBrowserContext(web_contents->GetBrowserContext());
2012 return profile->IsOffTheRecord() && !profile->IsGuestSession();
2015 void ChromeLauncherController::CloseWindowedAppsFromRemovedExtension(
2016 const std::string& app_id,
2017 const Profile* profile) {
2018 // This function cannot rely on the controller's enumeration functionality
2019 // since the extension has already be unloaded.
2020 const BrowserList* ash_browser_list =
2021 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
2022 std::vector<Browser*> browser_to_close;
2023 for (BrowserList::const_reverse_iterator
2024 it = ash_browser_list->begin_last_active();
2025 it != ash_browser_list->end_last_active(); ++it) {
2026 Browser* browser = *it;
2027 if (!browser->is_type_tabbed() && browser->is_type_popup() &&
2028 browser->is_app() &&
2030 web_app::GetExtensionIdFromApplicationName(browser->app_name()) &&
2031 profile == browser->profile()) {
2032 browser_to_close.push_back(browser);
2035 while (!browser_to_close.empty()) {
2036 TabStripModel* tab_strip = browser_to_close.back()->tab_strip_model();
2037 tab_strip->CloseWebContentsAt(0, TabStripModel::CLOSE_NONE);
2038 browser_to_close.pop_back();
2042 void ChromeLauncherController::SetShelfItemDelegate(
2044 ash::ShelfItemDelegate* item_delegate) {
2046 DCHECK(item_delegate);
2047 DCHECK(item_delegate_manager_);
2048 item_delegate_manager_->SetShelfItemDelegate(
2049 id, scoped_ptr<ash::ShelfItemDelegate>(item_delegate).Pass());
2052 void ChromeLauncherController::AttachProfile(Profile* profile) {
2054 // Either add the profile to the list of known profiles and make it the active
2055 // one for some functions of AppTabHelper or create a new one.
2056 if (!app_tab_helper_.get())
2057 app_tab_helper_.reset(new LauncherAppTabHelper(profile_));
2059 app_tab_helper_->SetCurrentUser(profile_);
2060 // TODO(skuhne): The AppIconLoaderImpl has the same problem. Each loaded
2061 // image is associated with a profile (it's loader requires the profile).
2062 // Since icon size changes are possible, the icon could be requested to be
2063 // reloaded. However - having it not multi profile aware would cause problems
2064 // if the icon cache gets deleted upon user switch.
2065 app_icon_loader_.reset(new extensions::AppIconLoaderImpl(
2066 profile_, extension_misc::EXTENSION_ICON_SMALL, this));
2068 pref_change_registrar_.Init(profile_->GetPrefs());
2069 pref_change_registrar_.Add(
2070 prefs::kPinnedLauncherApps,
2071 base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref,
2072 base::Unretained(this)));
2073 pref_change_registrar_.Add(
2074 prefs::kShelfAlignmentLocal,
2075 base::Bind(&ChromeLauncherController::SetShelfAlignmentFromPrefs,
2076 base::Unretained(this)));
2077 pref_change_registrar_.Add(
2078 prefs::kShelfAutoHideBehaviorLocal,
2079 base::Bind(&ChromeLauncherController::
2080 SetShelfAutoHideBehaviorFromPrefs,
2081 base::Unretained(this)));
2082 pref_change_registrar_.Add(
2083 prefs::kShelfPreferences,
2084 base::Bind(&ChromeLauncherController::SetShelfBehaviorsFromPrefs,
2085 base::Unretained(this)));
2086 #if defined(OS_CHROMEOS)
2087 pref_change_registrar_.Add(
2088 prefs::kTouchVirtualKeyboardEnabled,
2089 base::Bind(&ChromeLauncherController::SetVirtualKeyboardBehaviorFromPrefs,
2090 base::Unretained(this)));
2091 #endif // defined(OS_CHROMEOS)
2093 extensions::ExtensionRegistry::Get(profile_)->AddObserver(this);
2096 void ChromeLauncherController::ReleaseProfile() {
2097 if (app_sync_ui_state_)
2098 app_sync_ui_state_->RemoveObserver(this);
2100 extensions::ExtensionRegistry::Get(profile_)->RemoveObserver(this);
2102 PrefServiceSyncable::FromProfile(profile_)->RemoveObserver(this);
2104 pref_change_registrar_.RemoveAll();