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/launcher/launcher.h"
12 #include "ash/launcher/launcher_item_delegate_manager.h"
13 #include "ash/launcher/launcher_model.h"
14 #include "ash/root_window_controller.h"
15 #include "ash/shelf/shelf_layout_manager.h"
16 #include "ash/shelf/shelf_model_util.h"
17 #include "ash/shelf/shelf_widget.h"
18 #include "ash/shell.h"
19 #include "ash/wm/window_util.h"
20 #include "base/command_line.h"
21 #include "base/prefs/scoped_user_pref_update.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/utf_string_conversions.h"
24 #include "base/values.h"
25 #include "chrome/browser/app_mode/app_mode_utils.h"
26 #include "chrome/browser/chrome_notification_types.h"
27 #include "chrome/browser/defaults.h"
28 #include "chrome/browser/extensions/app_icon_loader_impl.h"
29 #include "chrome/browser/extensions/extension_service.h"
30 #include "chrome/browser/extensions/extension_system.h"
31 #include "chrome/browser/favicon/favicon_tab_helper.h"
32 #include "chrome/browser/prefs/incognito_mode_prefs.h"
33 #include "chrome/browser/prefs/pref_service_syncable.h"
34 #include "chrome/browser/profiles/profile.h"
35 #include "chrome/browser/profiles/profile_manager.h"
36 #include "chrome/browser/ui/ash/app_sync_ui_state.h"
37 #include "chrome/browser/ui/ash/chrome_launcher_prefs.h"
38 #include "chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.h"
39 #include "chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.h"
40 #include "chrome/browser/ui/ash/launcher/browser_status_monitor.h"
41 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h"
42 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_browser.h"
43 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_tab.h"
44 #include "chrome/browser/ui/ash/launcher/chrome_launcher_types.h"
45 #include "chrome/browser/ui/ash/launcher/launcher_app_tab_helper.h"
46 #include "chrome/browser/ui/ash/launcher/launcher_item_controller.h"
47 #include "chrome/browser/ui/ash/launcher/shell_window_launcher_controller.h"
48 #include "chrome/browser/ui/ash/launcher/shell_window_launcher_item_controller.h"
49 #include "chrome/browser/ui/browser.h"
50 #include "chrome/browser/ui/browser_commands.h"
51 #include "chrome/browser/ui/browser_finder.h"
52 #include "chrome/browser/ui/browser_list.h"
53 #include "chrome/browser/ui/browser_tabstrip.h"
54 #include "chrome/browser/ui/browser_window.h"
55 #include "chrome/browser/ui/extensions/application_launch.h"
56 #include "chrome/browser/ui/extensions/extension_enable_flow.h"
57 #include "chrome/browser/ui/host_desktop.h"
58 #include "chrome/browser/ui/tabs/tab_strip_model.h"
59 #include "chrome/browser/web_applications/web_app.h"
60 #include "chrome/common/chrome_switches.h"
61 #include "chrome/common/extensions/extension.h"
62 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
63 #include "chrome/common/extensions/manifest_handlers/icons_handler.h"
64 #include "chrome/common/pref_names.h"
65 #include "chrome/common/url_constants.h"
66 #include "content/public/browser/navigation_entry.h"
67 #include "content/public/browser/notification_registrar.h"
68 #include "content/public/browser/notification_service.h"
69 #include "content/public/browser/web_contents.h"
70 #include "extensions/common/extension_resource.h"
71 #include "extensions/common/url_pattern.h"
72 #include "grit/ash_resources.h"
73 #include "grit/chromium_strings.h"
74 #include "grit/generated_resources.h"
75 #include "grit/theme_resources.h"
76 #include "grit/ui_resources.h"
77 #include "net/base/url_util.h"
78 #include "ui/aura/root_window.h"
79 #include "ui/aura/window.h"
80 #include "ui/base/l10n/l10n_util.h"
81 #include "ui/views/corewm/window_animations.h"
83 #if defined(OS_CHROMEOS)
84 #include "ash/multi_profile_uma.h"
85 #include "chrome/browser/browser_process.h"
86 #include "chrome/browser/chromeos/login/user_manager.h"
87 #include "chrome/browser/chromeos/login/wallpaper_manager.h"
88 #include "chrome/browser/ui/ash/chrome_shell_delegate.h"
89 #include "chrome/browser/ui/ash/launcher/multi_profile_browser_status_monitor.h"
90 #include "chrome/browser/ui/ash/launcher/multi_profile_shell_window_launcher_controller.h"
91 #include "chrome/browser/ui/ash/multi_user_window_manager.h"
94 using extensions::Extension;
95 using extensions::UnloadedExtensionInfo;
96 using extension_misc::kGmailAppId;
97 using content::WebContents;
100 ChromeLauncherController* ChromeLauncherController::instance_ = NULL;
104 // This will be used as placeholder in the list of the pinned applciatons.
105 // Note that this is NOT a valid extension identifier so that pre M31 versions
107 const char kAppLauncherIdPlaceholder[] = "AppLauncherIDPlaceholder--------";
109 std::string GetPrefKeyForRootWindow(aura::Window* root_window) {
110 gfx::Display display = gfx::Screen::GetScreenFor(
111 root_window)->GetDisplayNearestWindow(root_window);
112 DCHECK(display.is_valid());
114 return base::Int64ToString(display.id());
117 void UpdatePerDisplayPref(PrefService* pref_service,
118 aura::Window* root_window,
119 const char* pref_key,
120 const std::string& value) {
121 std::string key = GetPrefKeyForRootWindow(root_window);
125 DictionaryPrefUpdate update(pref_service, prefs::kShelfPreferences);
126 base::DictionaryValue* shelf_prefs = update.Get();
127 base::DictionaryValue* prefs = NULL;
128 if (!shelf_prefs->GetDictionary(key, &prefs)) {
129 prefs = new base::DictionaryValue();
130 shelf_prefs->Set(key, prefs);
132 prefs->SetStringWithoutPathExpansion(pref_key, value);
135 // Returns a pref value in |pref_service| for the display of |root_window|. The
136 // pref value is stored in |local_path| and |path|, but |pref_service| may have
137 // per-display preferences and the value can be specified by policy. Here is
139 // * A value managed by policy. This is a single value that applies to all
141 // * A user-set value for the specified display.
142 // * A user-set value in |local_path| or |path|, if no per-display settings are
143 // ever specified (see http://crbug.com/173719 for why). |local_path| is
144 // preferred. See comment in |kShelfAlignment| as to why we consider two
145 // prefs and why |local_path| is preferred.
146 // * A value recommended by policy. This is a single value that applies to all
148 // * The default value for |local_path| if the value is not recommended by
150 std::string GetPrefForRootWindow(PrefService* pref_service,
151 aura::Window* root_window,
152 const char* local_path,
154 const PrefService::Preference* local_pref =
155 pref_service->FindPreference(local_path);
156 const std::string value(pref_service->GetString(local_path));
157 if (local_pref->IsManaged())
160 std::string pref_key = GetPrefKeyForRootWindow(root_window);
161 bool has_per_display_prefs = false;
162 if (!pref_key.empty()) {
163 const base::DictionaryValue* shelf_prefs = pref_service->GetDictionary(
164 prefs::kShelfPreferences);
165 const base::DictionaryValue* display_pref = NULL;
166 std::string per_display_value;
167 if (shelf_prefs->GetDictionary(pref_key, &display_pref) &&
168 display_pref->GetString(path, &per_display_value))
169 return per_display_value;
171 // If the pref for the specified display is not found, scan the whole prefs
172 // and check if the prefs for other display is already specified.
173 std::string unused_value;
174 for (base::DictionaryValue::Iterator iter(*shelf_prefs);
175 !iter.IsAtEnd(); iter.Advance()) {
176 const base::DictionaryValue* display_pref = NULL;
177 if (iter.value().GetAsDictionary(&display_pref) &&
178 display_pref->GetString(path, &unused_value)) {
179 has_per_display_prefs = true;
185 if (local_pref->IsRecommended() || !has_per_display_prefs)
188 const base::Value* default_value =
189 pref_service->GetDefaultPrefValue(local_path);
190 std::string default_string;
191 default_value->GetAsString(&default_string);
192 return default_string;
195 // If prefs have synced and no user-set value exists at |local_path|, the value
196 // from |synced_path| is copied to |local_path|.
197 void MaybePropagatePrefToLocal(PrefServiceSyncable* pref_service,
198 const char* local_path,
199 const char* synced_path) {
200 if (!pref_service->FindPreference(local_path)->HasUserSetting() &&
201 pref_service->IsSyncing()) {
202 // First time the user is using this machine, propagate from remote to
204 pref_service->SetString(local_path, pref_service->GetString(synced_path));
208 std::string GetSourceFromAppListSource(ash::LaunchSource source) {
210 case ash::LAUNCH_FROM_APP_LIST:
211 return std::string(extension_urls::kLaunchSourceAppList);
212 case ash::LAUNCH_FROM_APP_LIST_SEARCH:
213 return std::string(extension_urls::kLaunchSourceAppListSearch);
214 default: return std::string();
220 #if defined(OS_CHROMEOS)
221 // A class to get events from ChromeOS when a user gets changed or added.
222 class ChromeLauncherControllerUserSwitchObserverChromeOS
223 : public ChromeLauncherControllerUserSwitchObserver,
224 public chromeos::UserManager::UserSessionStateObserver,
225 content::NotificationObserver {
227 ChromeLauncherControllerUserSwitchObserverChromeOS(
228 ChromeLauncherController* controller)
229 : controller_(controller) {
230 DCHECK(chromeos::UserManager::IsInitialized());
231 chromeos::UserManager::Get()->AddSessionStateObserver(this);
232 // A UserAddedToSession notification can be sent before a profile is loaded.
233 // Since our observers require that we have already a profile, we might have
234 // to postpone the notification until the ProfileManager lets us know that
235 // the profile for that newly added user was added to the ProfileManager.
236 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_ADDED,
237 content::NotificationService::AllSources());
239 virtual ~ChromeLauncherControllerUserSwitchObserverChromeOS() {
240 chromeos::UserManager::Get()->RemoveSessionStateObserver(this);
243 // chromeos::UserManager::UserSessionStateObserver overrides:
244 virtual void ActiveUserChanged(const chromeos::User* active_user) OVERRIDE;
245 virtual void UserAddedToSession(const chromeos::User* added_user) OVERRIDE;
247 // content::NotificationObserver overrides:
248 virtual void Observe(int type,
249 const content::NotificationSource& source,
250 const content::NotificationDetails& details) OVERRIDE;
253 // Add a user to the session.
254 void AddUser(Profile* profile);
256 // The owning ChromeLauncherController.
257 ChromeLauncherController* controller_;
259 // The notification registrar to track the Profile creations after a user got
260 // added to the session (if required).
261 content::NotificationRegistrar registrar_;
263 // Users which were just added to the system, but which profiles were not yet
265 std::set<std::string> added_user_ids_waiting_for_profiles_;
267 DISALLOW_COPY_AND_ASSIGN(ChromeLauncherControllerUserSwitchObserverChromeOS);
270 void ChromeLauncherControllerUserSwitchObserverChromeOS::ActiveUserChanged(
271 const chromeos::User* active_user) {
272 const std::string& user_email = active_user->email();
273 // Forward the OS specific event to the ChromeLauncherController.
274 controller_->ActiveUserChanged(user_email);
275 // TODO(skuhne): At the moment the login screen does the wallpaper management
276 // and wallpapers are not synchronized across multiple desktops.
277 if (chromeos::WallpaperManager::Get())
278 chromeos::WallpaperManager::Get()->SetUserWallpaper(user_email);
281 void ChromeLauncherControllerUserSwitchObserverChromeOS::UserAddedToSession(
282 const chromeos::User* active_user) {
283 Profile* profile = chrome::MultiUserWindowManager::GetProfileFromUserID(
284 active_user->email());
285 // If we do not have a profile yet, we postpone forwarding the notification
286 // until it is loaded.
288 added_user_ids_waiting_for_profiles_.insert(active_user->email());
293 void ChromeLauncherControllerUserSwitchObserverChromeOS::Observe(
295 const content::NotificationSource& source,
296 const content::NotificationDetails& details) {
297 if (type == chrome::NOTIFICATION_PROFILE_ADDED &&
298 !added_user_ids_waiting_for_profiles_.empty()) {
299 // Check if the profile is from a user which was on the waiting list.
300 Profile* profile = content::Source<Profile>(source).ptr();
301 std::string user_id = chrome::MultiUserWindowManager::GetUserIDFromProfile(
303 std::set<std::string>::iterator it = std::find(
304 added_user_ids_waiting_for_profiles_.begin(),
305 added_user_ids_waiting_for_profiles_.end(),
307 if (it != added_user_ids_waiting_for_profiles_.end()) {
308 added_user_ids_waiting_for_profiles_.erase(it);
309 AddUser(profile->GetOriginalProfile());
314 void ChromeLauncherControllerUserSwitchObserverChromeOS::AddUser(
316 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
317 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED)
318 chrome::MultiUserWindowManager::GetInstance()->AddUser(profile);
319 controller_->AdditionalUserAddedToSession(profile->GetOriginalProfile());
323 ChromeLauncherController::ChromeLauncherController(
325 ash::LauncherModel* model)
327 item_delegate_manager_(NULL),
329 app_sync_ui_state_(NULL),
330 ignore_persist_pinned_state_change_(false) {
332 // Use the original profile as on chromeos we may get a temporary off the
334 profile_ = ProfileManager::GetDefaultProfile()->GetOriginalProfile();
336 app_sync_ui_state_ = AppSyncUIState::Get(profile_);
337 if (app_sync_ui_state_)
338 app_sync_ui_state_->AddObserver(this);
341 // All profile relevant settings get bound to the current profile.
342 AttachProfile(profile_);
343 model_->AddObserver(this);
345 #if defined(OS_CHROMEOS)
346 // In multi profile mode we might have a window manager. We try to create it
347 // here. If the instantiation fails, the manager is not needed.
348 chrome::MultiUserWindowManager::CreateInstance();
350 // On Chrome OS using multi profile we want to switch the content of the shelf
351 // with a user change. Note that for unit tests the instance can be NULL.
352 if (chrome::MultiUserWindowManager::GetMultiProfileMode() !=
353 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_OFF) {
354 user_switch_observer_.reset(
355 new ChromeLauncherControllerUserSwitchObserverChromeOS(this));
358 // Create our v1/v2 application / browser monitors which will inform the
359 // launcher of status changes.
360 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
361 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED) {
362 // If running in separated destkop mode, we create the multi profile version
363 // of status monitor.
364 browser_status_monitor_.reset(new MultiProfileBrowserStatusMonitor(this));
365 shell_window_controller_.reset(
366 new MultiProfileShellWindowLauncherController(this));
368 // Create our v1/v2 application / browser monitors which will inform the
369 // launcher of status changes.
370 browser_status_monitor_.reset(new BrowserStatusMonitor(this));
371 shell_window_controller_.reset(new ShellWindowLauncherController(this));
374 // Create our v1/v2 application / browser monitors which will inform the
375 // launcher of status changes.
376 browser_status_monitor_.reset(new BrowserStatusMonitor(this));
377 shell_window_controller_.reset(new ShellWindowLauncherController(this));
380 // Right now ash::Shell isn't created for tests.
381 // TODO(mukai): Allows it to observe display change and write tests.
382 if (ash::Shell::HasInstance()) {
383 ash::Shell::GetInstance()->display_controller()->AddObserver(this);
384 item_delegate_manager_ =
385 ash::Shell::GetInstance()->launcher_item_delegate_manager();
388 notification_registrar_.Add(this,
389 chrome::NOTIFICATION_EXTENSION_LOADED,
390 content::Source<Profile>(profile_));
391 notification_registrar_.Add(this,
392 chrome::NOTIFICATION_EXTENSION_UNLOADED,
393 content::Source<Profile>(profile_));
396 ChromeLauncherController::~ChromeLauncherController() {
397 // Reset the BrowserStatusMonitor as it has a weak pointer to this.
398 browser_status_monitor_.reset();
400 // Reset the shell window controller here since it has a weak pointer to this.
401 shell_window_controller_.reset();
403 for (std::set<ash::Launcher*>::iterator iter = launchers_.begin();
404 iter != launchers_.end();
406 (*iter)->shelf_widget()->shelf_layout_manager()->RemoveObserver(this);
408 model_->RemoveObserver(this);
409 if (ash::Shell::HasInstance())
410 ash::Shell::GetInstance()->display_controller()->RemoveObserver(this);
411 for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin();
412 i != id_to_item_controller_map_.end(); ++i) {
413 int index = model_->ItemIndexByID(i->first);
414 // A "browser proxy" is not known to the model and this removal does
415 // therefore not need to be propagated to the model.
417 model_->items()[index].type != ash::TYPE_BROWSER_SHORTCUT)
418 model_->RemoveItemAt(index);
421 if (ash::Shell::HasInstance())
422 ash::Shell::GetInstance()->RemoveShellObserver(this);
424 // Release all profile dependent resources.
426 if (instance_ == this)
428 #if defined(OS_CHROMEOS)
429 // Get rid of the multi user window manager instance.
430 chrome::MultiUserWindowManager::DeleteInstance();
435 ChromeLauncherController* ChromeLauncherController::CreateInstance(
437 ash::LauncherModel* model) {
438 // We do not check here for re-creation of the ChromeLauncherController since
439 // it appears that it might be intentional that the ChromeLauncherController
440 // can be re-created.
441 instance_ = new ChromeLauncherController(profile, model);
445 void ChromeLauncherController::Init() {
446 CreateBrowserShortcutLauncherItem();
447 UpdateAppLaunchersFromPref();
449 // TODO(sky): update unit test so that this test isn't necessary.
450 if (ash::Shell::HasInstance()) {
451 SetShelfAutoHideBehaviorFromPrefs();
452 SetShelfAlignmentFromPrefs();
453 PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_);
454 if (!prefs->FindPreference(prefs::kShelfAlignmentLocal)->HasUserSetting() ||
455 !prefs->FindPreference(prefs::kShelfAutoHideBehaviorLocal)->
457 // This causes OnIsSyncingChanged to be called when the value of
458 // PrefService::IsSyncing() changes.
459 prefs->AddObserver(this);
461 ash::Shell::GetInstance()->AddShellObserver(this);
465 ash::LauncherID ChromeLauncherController::CreateAppLauncherItem(
466 LauncherItemController* controller,
467 const std::string& app_id,
468 ash::LauncherItemStatus status) {
471 // Panels are inserted on the left so as not to push all existing panels over.
472 if (controller->GetLauncherItemType() != ash::TYPE_APP_PANEL)
473 index = model_->item_count();
474 return InsertAppLauncherItem(controller,
478 controller->GetLauncherItemType());
481 void ChromeLauncherController::SetItemStatus(
483 ash::LauncherItemStatus status) {
484 int index = model_->ItemIndexByID(id);
485 ash::LauncherItemStatus old_status = model_->items()[index].status;
486 // Since ordinary browser windows are not registered, we might get a negative
488 if (index >= 0 && old_status != status) {
489 ash::LauncherItem item = model_->items()[index];
490 item.status = status;
491 model_->Set(index, item);
495 void ChromeLauncherController::SetItemController(
497 LauncherItemController* controller) {
499 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
500 CHECK(iter != id_to_item_controller_map_.end());
501 controller->set_launcher_id(id);
502 iter->second = controller;
503 // Existing controller is destroyed and replaced by registering again.
504 SetLauncherItemDelegate(id, controller);
507 void ChromeLauncherController::CloseLauncherItem(ash::LauncherID id) {
510 // Create a new shortcut controller.
511 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
512 CHECK(iter != id_to_item_controller_map_.end());
513 SetItemStatus(id, ash::STATUS_CLOSED);
514 std::string app_id = iter->second->app_id();
515 iter->second = new AppShortcutLauncherItemController(app_id, this);
516 iter->second->set_launcher_id(id);
517 // Existing controller is destroyed and replaced by registering again.
518 SetLauncherItemDelegate(id, iter->second);
520 LauncherItemClosed(id);
524 void ChromeLauncherController::Pin(ash::LauncherID id) {
525 DCHECK(HasItemController(id));
527 int index = model_->ItemIndexByID(id);
530 ash::LauncherItem item = model_->items()[index];
532 if (item.type == ash::TYPE_PLATFORM_APP ||
533 item.type == ash::TYPE_WINDOWED_APP) {
534 item.type = ash::TYPE_APP_SHORTCUT;
535 model_->Set(index, item);
536 } else if (item.type != ash::TYPE_APP_SHORTCUT) {
541 PersistPinnedState();
544 void ChromeLauncherController::Unpin(ash::LauncherID id) {
545 DCHECK(HasItemController(id));
547 LauncherItemController* controller = id_to_item_controller_map_[id];
548 if (controller->type() == LauncherItemController::TYPE_APP ||
549 controller->locked()) {
550 UnpinRunningAppInternal(model_->ItemIndexByID(id));
552 LauncherItemClosed(id);
555 PersistPinnedState();
558 bool ChromeLauncherController::IsPinned(ash::LauncherID id) {
559 int index = model_->ItemIndexByID(id);
562 ash::LauncherItemType type = model_->items()[index].type;
563 return (type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_BROWSER_SHORTCUT);
566 void ChromeLauncherController::TogglePinned(ash::LauncherID id) {
567 if (!HasItemController(id))
568 return; // May happen if item closed with menu open.
576 bool ChromeLauncherController::IsPinnable(ash::LauncherID id) const {
577 int index = model_->ItemIndexByID(id);
581 ash::LauncherItemType type = model_->items()[index].type;
582 return ((type == ash::TYPE_APP_SHORTCUT ||
583 type == ash::TYPE_PLATFORM_APP ||
584 type == ash::TYPE_WINDOWED_APP) &&
588 void ChromeLauncherController::LockV1AppWithID(
589 const std::string& app_id) {
590 ash::LauncherID id = GetLauncherIDForAppID(app_id);
591 if (!IsPinned(id) && !IsWindowedAppInLauncher(app_id)) {
592 CreateAppShortcutLauncherItemWithType(app_id,
593 model_->item_count(),
594 ash::TYPE_WINDOWED_APP);
595 id = GetLauncherIDForAppID(app_id);
598 id_to_item_controller_map_[id]->lock();
601 void ChromeLauncherController::UnlockV1AppWithID(
602 const std::string& app_id) {
603 ash::LauncherID id = GetLauncherIDForAppID(app_id);
604 CHECK(IsPinned(id) || IsWindowedAppInLauncher(app_id));
606 LauncherItemController* controller = id_to_item_controller_map_[id];
607 controller->unlock();
608 if (!controller->locked() && !IsPinned(id))
609 CloseLauncherItem(id);
612 void ChromeLauncherController::Launch(ash::LauncherID id,
614 if (!HasItemController(id))
615 return; // In case invoked from menu and item closed while menu up.
616 id_to_item_controller_map_[id]->Launch(ash::LAUNCH_FROM_UNKNOWN, event_flags);
619 void ChromeLauncherController::Close(ash::LauncherID id) {
620 if (!HasItemController(id))
621 return; // May happen if menu closed.
622 id_to_item_controller_map_[id]->Close();
625 bool ChromeLauncherController::IsOpen(ash::LauncherID id) {
626 if (!HasItemController(id))
628 return id_to_item_controller_map_[id]->IsOpen();
631 bool ChromeLauncherController::IsPlatformApp(ash::LauncherID id) {
632 if (!HasItemController(id))
635 std::string app_id = GetAppIDForLauncherID(id);
636 const Extension* extension = GetExtensionForAppID(app_id);
637 // An extension can be synced / updated at any time and therefore not be
639 return extension ? extension->is_platform_app() : false;
642 void ChromeLauncherController::LaunchApp(const std::string& app_id,
643 ash::LaunchSource source,
645 // |extension| could be NULL when it is being unloaded for updating.
646 const Extension* extension = GetExtensionForAppID(app_id);
650 const ExtensionService* service =
651 extensions::ExtensionSystem::Get(profile_)->extension_service();
652 if (!service->IsExtensionEnabledForLauncher(app_id)) {
653 // Do nothing if there is already a running enable flow.
654 if (extension_enable_flow_)
657 extension_enable_flow_.reset(
658 new ExtensionEnableFlow(profile_, app_id, this));
659 extension_enable_flow_->StartForNativeWindow(NULL);
663 AppLaunchParams params(
664 GetProfileForNewWindows(),
667 chrome::HOST_DESKTOP_TYPE_ASH);
668 if (source != ash::LAUNCH_FROM_UNKNOWN &&
669 app_id == extension_misc::kWebStoreAppId) {
670 // Get the corresponding source string.
671 std::string source_value = GetSourceFromAppListSource(source);
673 // Set an override URL to include the source.
674 GURL extension_url = extensions::AppLaunchInfo::GetFullLaunchURL(extension);
675 params.override_url = net::AppendQueryParameter(
676 extension_url, extension_urls::kWebstoreSourceField, source_value);
679 OpenApplication(params);
682 void ChromeLauncherController::ActivateApp(const std::string& app_id,
683 ash::LaunchSource source,
685 // If there is an existing non-shortcut controller for this app, open it.
686 ash::LauncherID id = GetLauncherIDForAppID(app_id);
688 LauncherItemController* controller = id_to_item_controller_map_[id];
689 controller->Activate(source);
693 // Create a temporary application launcher item and use it to see if there are
694 // running instances.
695 scoped_ptr<AppShortcutLauncherItemController> app_controller(
696 new AppShortcutLauncherItemController(app_id, this));
697 if (!app_controller->GetRunningApplications().empty())
698 app_controller->Activate(source);
700 LaunchApp(app_id, source, event_flags);
703 extensions::ExtensionPrefs::LaunchType
704 ChromeLauncherController::GetLaunchType(ash::LauncherID id) {
705 DCHECK(HasItemController(id));
707 const Extension* extension = GetExtensionForAppID(
708 id_to_item_controller_map_[id]->app_id());
710 // An extension can be unloaded/updated/unavailable at any time.
712 return extensions::ExtensionPrefs::LAUNCH_DEFAULT;
714 return profile_->GetExtensionService()->extension_prefs()->GetLaunchType(
716 extensions::ExtensionPrefs::LAUNCH_DEFAULT);
719 ash::LauncherID ChromeLauncherController::GetLauncherIDForAppID(
720 const std::string& app_id) {
721 for (IDToItemControllerMap::const_iterator i =
722 id_to_item_controller_map_.begin();
723 i != id_to_item_controller_map_.end(); ++i) {
724 if (i->second->type() == LauncherItemController::TYPE_APP_PANEL)
725 continue; // Don't include panels
726 if (i->second->app_id() == app_id)
732 const std::string& ChromeLauncherController::GetAppIDForLauncherID(
733 ash::LauncherID id) {
734 CHECK(HasItemController(id));
735 return id_to_item_controller_map_[id]->app_id();
738 void ChromeLauncherController::SetAppImage(const std::string& id,
739 const gfx::ImageSkia& image) {
740 // TODO: need to get this working for shortcuts.
741 for (IDToItemControllerMap::const_iterator i =
742 id_to_item_controller_map_.begin();
743 i != id_to_item_controller_map_.end(); ++i) {
744 LauncherItemController* controller = i->second;
745 if (controller->app_id() != id)
747 if (controller->image_set_by_controller())
749 int index = model_->ItemIndexByID(i->first);
752 ash::LauncherItem item = model_->items()[index];
754 model_->Set(index, item);
755 // It's possible we're waiting on more than one item, so don't break.
759 void ChromeLauncherController::OnAutoHideBehaviorChanged(
760 aura::Window* root_window,
761 ash::ShelfAutoHideBehavior new_behavior) {
762 SetShelfAutoHideBehaviorPrefs(new_behavior, root_window);
765 void ChromeLauncherController::SetLauncherItemImage(
766 ash::LauncherID launcher_id,
767 const gfx::ImageSkia& image) {
768 int index = model_->ItemIndexByID(launcher_id);
771 ash::LauncherItem item = model_->items()[index];
773 model_->Set(index, item);
776 bool ChromeLauncherController::CanPin() const {
777 const PrefService::Preference* pref =
778 profile_->GetPrefs()->FindPreference(prefs::kPinnedLauncherApps);
779 return pref && pref->IsUserModifiable();
782 bool ChromeLauncherController::IsAppPinned(const std::string& app_id) {
783 for (IDToItemControllerMap::const_iterator i =
784 id_to_item_controller_map_.begin();
785 i != id_to_item_controller_map_.end(); ++i) {
786 if (IsPinned(i->first) && i->second->app_id() == app_id)
792 bool ChromeLauncherController::IsWindowedAppInLauncher(
793 const std::string& app_id) {
794 int index = model_->ItemIndexByID(GetLauncherIDForAppID(app_id));
798 ash::LauncherItemType type = model_->items()[index].type;
799 return type == ash::TYPE_WINDOWED_APP;
802 void ChromeLauncherController::PinAppWithID(const std::string& app_id) {
804 DoPinAppWithID(app_id);
809 void ChromeLauncherController::SetLaunchType(
811 extensions::ExtensionPrefs::LaunchType launch_type) {
812 if (!HasItemController(id))
815 profile_->GetExtensionService()->extension_prefs()->SetLaunchType(
816 id_to_item_controller_map_[id]->app_id(), launch_type);
819 void ChromeLauncherController::UnpinAppWithID(const std::string& app_id) {
821 DoUnpinAppWithID(app_id);
826 bool ChromeLauncherController::IsLoggedInAsGuest() {
827 return ProfileManager::GetDefaultProfileOrOffTheRecord()->IsOffTheRecord();
830 void ChromeLauncherController::CreateNewWindow() {
831 chrome::NewEmptyWindow(
832 GetProfileForNewWindows(), chrome::HOST_DESKTOP_TYPE_ASH);
835 void ChromeLauncherController::CreateNewIncognitoWindow() {
836 chrome::NewEmptyWindow(GetProfileForNewWindows()->GetOffTheRecordProfile(),
837 chrome::HOST_DESKTOP_TYPE_ASH);
840 void ChromeLauncherController::PersistPinnedState() {
841 if (ignore_persist_pinned_state_change_)
843 // It is a coding error to call PersistPinnedState() if the pinned apps are
844 // not user-editable. The code should check earlier and not perform any
845 // modification actions that trigger persisting the state.
847 NOTREACHED() << "Can't pin but pinned state being updated";
850 // Mutating kPinnedLauncherApps is going to notify us and trigger us to
851 // process the change. We don't want that to happen so remove ourselves as a
853 pref_change_registrar_.Remove(prefs::kPinnedLauncherApps);
855 ListPrefUpdate updater(profile_->GetPrefs(), prefs::kPinnedLauncherApps);
857 for (size_t i = 0; i < model_->items().size(); ++i) {
858 if (model_->items()[i].type == ash::TYPE_APP_SHORTCUT) {
859 ash::LauncherID id = model_->items()[i].id;
860 if (HasItemController(id) && IsPinned(id)) {
861 base::DictionaryValue* app_value = ash::CreateAppDict(
862 id_to_item_controller_map_[id]->app_id());
864 updater->Append(app_value);
866 } else if (model_->items()[i].type == ash::TYPE_BROWSER_SHORTCUT) {
867 PersistChromeItemIndex(i);
868 } else if (model_->items()[i].type == ash::TYPE_APP_LIST) {
869 base::DictionaryValue* app_value = ash::CreateAppDict(
870 kAppLauncherIdPlaceholder);
872 updater->Append(app_value);
876 pref_change_registrar_.Add(
877 prefs::kPinnedLauncherApps,
878 base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref,
879 base::Unretained(this)));
882 ash::LauncherModel* ChromeLauncherController::model() {
886 Profile* ChromeLauncherController::profile() {
890 ash::ShelfAutoHideBehavior ChromeLauncherController::GetShelfAutoHideBehavior(
891 aura::Window* root_window) const {
892 // Don't show the shelf in app mode.
893 if (chrome::IsRunningInAppMode())
894 return ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN;
896 // See comment in |kShelfAlignment| as to why we consider two prefs.
897 const std::string behavior_value(
898 GetPrefForRootWindow(profile_->GetPrefs(),
900 prefs::kShelfAutoHideBehaviorLocal,
901 prefs::kShelfAutoHideBehavior));
903 // Note: To maintain sync compatibility with old images of chrome/chromeos
904 // the set of values that may be encountered includes the now-extinct
905 // "Default" as well as "Never" and "Always", "Default" should now
906 // be treated as "Never" (http://crbug.com/146773).
907 if (behavior_value == ash::kShelfAutoHideBehaviorAlways)
908 return ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS;
909 return ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER;
912 bool ChromeLauncherController::CanUserModifyShelfAutoHideBehavior(
913 aura::Window* root_window) const {
914 return profile_->GetPrefs()->
915 FindPreference(prefs::kShelfAutoHideBehaviorLocal)->IsUserModifiable();
918 void ChromeLauncherController::ToggleShelfAutoHideBehavior(
919 aura::Window* root_window) {
920 ash::ShelfAutoHideBehavior behavior = GetShelfAutoHideBehavior(root_window) ==
921 ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS ?
922 ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER :
923 ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS;
924 SetShelfAutoHideBehaviorPrefs(behavior, root_window);
928 void ChromeLauncherController::RemoveTabFromRunningApp(
930 const std::string& app_id) {
931 web_contents_to_app_id_.erase(tab);
932 // BrowserShortcutLauncherItemController::UpdateBrowserItemState() will update
933 // the state when no application is associated with the tab.
937 AppIDToWebContentsListMap::iterator i_app_id =
938 app_id_to_web_contents_list_.find(app_id);
939 if (i_app_id != app_id_to_web_contents_list_.end()) {
940 WebContentsList* tab_list = &i_app_id->second;
941 tab_list->remove(tab);
942 ash::LauncherItemStatus status = ash::STATUS_RUNNING;
943 if (tab_list->empty()) {
944 app_id_to_web_contents_list_.erase(i_app_id);
945 status = ash::STATUS_CLOSED;
947 ash::LauncherID id = GetLauncherIDForAppID(app_id);
949 SetItemStatus(id, status);
953 void ChromeLauncherController::UpdateAppState(content::WebContents* contents,
954 AppState app_state) {
955 std::string app_id = app_tab_helper_->GetAppID(contents);
957 // Check if the gMail app is loaded and it matches the given content.
958 // This special treatment is needed to address crbug.com/234268.
959 if (app_id.empty() && ContentCanBeHandledByGmailApp(contents))
960 app_id = kGmailAppId;
962 // Check the old |app_id| for a tab. If the contents has changed we need to
963 // remove it from the previous app.
964 if (web_contents_to_app_id_.find(contents) != web_contents_to_app_id_.end()) {
965 std::string last_app_id = web_contents_to_app_id_[contents];
966 if (last_app_id != app_id)
967 RemoveTabFromRunningApp(contents, last_app_id);
970 web_contents_to_app_id_[contents] = app_id;
972 if (app_state == APP_STATE_REMOVED) {
973 // The tab has gone away.
974 RemoveTabFromRunningApp(contents, app_id);
975 } else if (!app_id.empty()) {
976 WebContentsList& tab_list(app_id_to_web_contents_list_[app_id]);
977 WebContentsList::const_iterator i_tab =
978 std::find(tab_list.begin(), tab_list.end(), contents);
980 if (i_tab == tab_list.end())
981 tab_list.push_back(contents);
983 if (app_state == APP_STATE_INACTIVE || app_state == APP_STATE_ACTIVE) {
984 if (i_tab != tab_list.begin()) {
985 // Going to running state, but wasn't the front tab, indicating that a
986 // new tab has already become active.
991 if (app_state == APP_STATE_ACTIVE || app_state == APP_STATE_WINDOW_ACTIVE) {
992 tab_list.remove(contents);
993 tab_list.push_front(contents);
996 ash::LauncherID id = GetLauncherIDForAppID(app_id);
998 // If the window is active, mark the app as active.
999 SetItemStatus(id, app_state == APP_STATE_WINDOW_ACTIVE ?
1000 ash::STATUS_ACTIVE : ash::STATUS_RUNNING);
1005 void ChromeLauncherController::SetRefocusURLPatternForTest(ash::LauncherID id,
1007 DCHECK(HasItemController(id));
1008 LauncherItemController* controller = id_to_item_controller_map_[id];
1010 int index = model_->ItemIndexByID(id);
1012 NOTREACHED() << "Invalid launcher id";
1016 ash::LauncherItemType type = model_->items()[index].type;
1017 if (type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_WINDOWED_APP) {
1018 AppShortcutLauncherItemController* app_controller =
1019 static_cast<AppShortcutLauncherItemController*>(controller);
1020 app_controller->set_refocus_url(url);
1022 NOTREACHED() << "Invalid launcher type";
1026 const Extension* ChromeLauncherController::GetExtensionForAppID(
1027 const std::string& app_id) const {
1028 // Some unit tests do not have a real extension.
1029 return (profile_->GetExtensionService()) ?
1030 profile_->GetExtensionService()->GetInstalledExtension(app_id) : NULL;
1033 void ChromeLauncherController::ActivateWindowOrMinimizeIfActive(
1034 ui::BaseWindow* window,
1035 bool allow_minimize) {
1036 #if defined(OS_CHROMEOS)
1037 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
1038 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED) {
1039 chrome::MultiUserWindowManager* manager =
1040 chrome::MultiUserWindowManager::GetInstance();
1041 aura::Window* native_window = window->GetNativeWindow();
1042 const std::string& current_user =
1043 manager->GetUserIDFromProfile(profile());
1044 if (!manager->IsWindowOnDesktopOfUser(native_window, current_user)) {
1045 ash::MultiProfileUMA::RecordTeleportAction(
1046 ash::MultiProfileUMA::TELEPORT_WINDOW_RETURN_BY_LAUNCHER);
1047 manager->ShowWindowForUser(native_window, current_user);
1053 if (window->IsActive() && allow_minimize) {
1054 if (CommandLine::ForCurrentProcess()->HasSwitch(
1055 switches::kDisableMinimizeOnSecondLauncherItemClick)) {
1056 AnimateWindow(window->GetNativeWindow(),
1057 views::corewm::WINDOW_ANIMATION_TYPE_BOUNCE);
1067 ash::LauncherID ChromeLauncherController::GetIDByWindow(aura::Window* window) {
1069 ash::GetShelfItemIndexForType(ash::TYPE_BROWSER_SHORTCUT, *model_);
1070 DCHECK_GE(browser_index, 0);
1071 ash::LauncherID browser_id = model_->items()[browser_index].id;
1073 IDToItemControllerMap::const_iterator i = id_to_item_controller_map_.begin();
1074 for (; i != id_to_item_controller_map_.end(); ++i) {
1075 // Since a |window| can be used by multiple applications, an explicit
1076 // application always gets chosen over the generic browser.
1077 if (i->first != browser_id && i->second->IsCurrentlyShownInWindow(window))
1081 if (i == id_to_item_controller_map_.end() &&
1082 GetBrowserShortcutLauncherItemController()->
1083 IsCurrentlyShownInWindow(window))
1089 void ChromeLauncherController::OnLauncherCreated(ash::Launcher* launcher) {
1090 launchers_.insert(launcher);
1091 launcher->shelf_widget()->shelf_layout_manager()->AddObserver(this);
1094 void ChromeLauncherController::OnLauncherDestroyed(ash::Launcher* launcher) {
1095 launchers_.erase(launcher);
1096 // RemoveObserver is not called here, since by the time this method is called
1097 // Launcher is already in its destructor.
1100 void ChromeLauncherController::LauncherItemAdded(int index) {
1101 // The app list launcher can get added to the shelf after we applied the
1102 // preferences. In that case the item might be at the wrong spot. As such we
1103 // call the function again.
1104 if (model_->items()[index].type == ash::TYPE_APP_LIST &&
1105 ash::switches::UseAlternateShelfLayout())
1106 UpdateAppLaunchersFromPref();
1109 void ChromeLauncherController::LauncherItemRemoved(int index,
1110 ash::LauncherID id) {
1113 void ChromeLauncherController::LauncherItemMoved(int start_index,
1115 const ash::LauncherItem& item = model_->items()[target_index];
1116 // We remember the moved item position if it is either pinnable or
1117 // it is the app list with the alternate shelf layout.
1118 if ((HasItemController(item.id) && IsPinned(item.id)) ||
1119 (ash::switches::UseAlternateShelfLayout() &&
1120 item.type == ash::TYPE_APP_LIST))
1121 PersistPinnedState();
1124 void ChromeLauncherController::LauncherItemChanged(
1126 const ash::LauncherItem& old_item) {
1129 void ChromeLauncherController::LauncherStatusChanged() {
1132 void ChromeLauncherController::ActiveUserChanged(
1133 const std::string& user_email) {
1134 // Coming here the default profile is already switched. All profile specific
1135 // resources get released and the new profile gets attached instead.
1137 AttachProfile(ProfileManager::GetDefaultProfile());
1138 // Update the V1 applications.
1139 browser_status_monitor_->ActiveUserChanged(user_email);
1140 // Switch the running applications to the new user.
1141 shell_window_controller_->ActiveUserChanged(user_email);
1142 // Update the user specific shell properties from the new user profile.
1143 UpdateAppLaunchersFromPref();
1144 SetShelfAlignmentFromPrefs();
1145 SetShelfAutoHideBehaviorFromPrefs();
1146 SetShelfBehaviorsFromPrefs();
1147 UpdateV1AppStatesAfterUserSwitch();
1150 void ChromeLauncherController::AdditionalUserAddedToSession(Profile* profile) {
1151 // Switch the running applications to the new user.
1152 shell_window_controller_->AdditionalUserAddedToSession(profile);
1155 void ChromeLauncherController::Observe(
1157 const content::NotificationSource& source,
1158 const content::NotificationDetails& details) {
1160 case chrome::NOTIFICATION_EXTENSION_LOADED: {
1161 const Extension* extension =
1162 content::Details<const Extension>(details).ptr();
1163 if (IsAppPinned(extension->id())) {
1164 // Clear and re-fetch to ensure icon is up-to-date.
1165 app_icon_loader_->ClearImage(extension->id());
1166 app_icon_loader_->FetchImage(extension->id());
1169 UpdateAppLaunchersFromPref();
1172 case chrome::NOTIFICATION_EXTENSION_UNLOADED: {
1173 const content::Details<UnloadedExtensionInfo>& unload_info(details);
1174 const Extension* extension = unload_info->extension;
1175 const std::string& id = extension->id();
1176 // Since we might have windowed apps of this type which might have
1177 // outstanding locks which needs to be removed.
1178 if (GetLauncherIDForAppID(id) &&
1179 unload_info->reason == UnloadedExtensionInfo::REASON_UNINSTALL) {
1180 CloseWindowedAppsFromRemovedExtension(id);
1183 if (IsAppPinned(id)) {
1184 if (unload_info->reason == UnloadedExtensionInfo::REASON_UNINSTALL) {
1185 DoUnpinAppWithID(id);
1186 app_icon_loader_->ClearImage(id);
1188 app_icon_loader_->UpdateImage(id);
1194 NOTREACHED() << "Unexpected notification type=" << type;
1198 void ChromeLauncherController::OnShelfAlignmentChanged(
1199 aura::Window* root_window) {
1200 const char* pref_value = NULL;
1201 switch (ash::Shell::GetInstance()->GetShelfAlignment(root_window)) {
1202 case ash::SHELF_ALIGNMENT_BOTTOM:
1203 pref_value = ash::kShelfAlignmentBottom;
1205 case ash::SHELF_ALIGNMENT_LEFT:
1206 pref_value = ash::kShelfAlignmentLeft;
1208 case ash::SHELF_ALIGNMENT_RIGHT:
1209 pref_value = ash::kShelfAlignmentRight;
1211 case ash::SHELF_ALIGNMENT_TOP:
1212 pref_value = ash::kShelfAlignmentTop;
1215 UpdatePerDisplayPref(
1216 profile_->GetPrefs(), root_window, prefs::kShelfAlignment, pref_value);
1218 if (root_window == ash::Shell::GetPrimaryRootWindow()) {
1219 // See comment in |kShelfAlignment| about why we have two prefs here.
1220 profile_->GetPrefs()->SetString(prefs::kShelfAlignmentLocal, pref_value);
1221 profile_->GetPrefs()->SetString(prefs::kShelfAlignment, pref_value);
1225 void ChromeLauncherController::OnDisplayConfigurationChanging() {
1228 void ChromeLauncherController::OnDisplayConfigurationChanged() {
1229 SetShelfBehaviorsFromPrefs();
1232 void ChromeLauncherController::OnIsSyncingChanged() {
1233 PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_);
1234 MaybePropagatePrefToLocal(prefs,
1235 prefs::kShelfAlignmentLocal,
1236 prefs::kShelfAlignment);
1237 MaybePropagatePrefToLocal(prefs,
1238 prefs::kShelfAutoHideBehaviorLocal,
1239 prefs::kShelfAutoHideBehavior);
1242 void ChromeLauncherController::OnAppSyncUIStatusChanged() {
1243 if (app_sync_ui_state_->status() == AppSyncUIState::STATUS_SYNCING)
1244 model_->SetStatus(ash::LauncherModel::STATUS_LOADING);
1246 model_->SetStatus(ash::LauncherModel::STATUS_NORMAL);
1249 void ChromeLauncherController::ExtensionEnableFlowFinished() {
1250 LaunchApp(extension_enable_flow_->extension_id(),
1251 ash::LAUNCH_FROM_UNKNOWN,
1253 extension_enable_flow_.reset();
1256 void ChromeLauncherController::ExtensionEnableFlowAborted(bool user_initiated) {
1257 extension_enable_flow_.reset();
1260 ChromeLauncherAppMenuItems ChromeLauncherController::GetApplicationList(
1261 const ash::LauncherItem& item,
1263 // Make sure that there is a controller associated with the id and that the
1264 // extension itself is a valid application and not a panel.
1265 if (!HasItemController(item.id) ||
1266 !GetLauncherIDForAppID(id_to_item_controller_map_[item.id]->app_id()))
1267 return ChromeLauncherAppMenuItems().Pass();
1269 return id_to_item_controller_map_[item.id]->GetApplicationList(event_flags);
1272 std::vector<content::WebContents*>
1273 ChromeLauncherController::GetV1ApplicationsFromAppId(std::string app_id) {
1274 ash::LauncherID id = GetLauncherIDForAppID(app_id);
1276 // If there is no such an item pinned to the launcher, no menu gets created.
1278 LauncherItemController* controller = id_to_item_controller_map_[id];
1280 if (controller->type() == LauncherItemController::TYPE_SHORTCUT)
1281 return GetV1ApplicationsFromController(controller);
1283 return std::vector<content::WebContents*>();
1286 void ChromeLauncherController::ActivateShellApp(const std::string& app_id,
1288 ash::LauncherID id = GetLauncherIDForAppID(app_id);
1290 LauncherItemController* controller = id_to_item_controller_map_[id];
1291 if (controller->type() == LauncherItemController::TYPE_APP) {
1292 ShellWindowLauncherItemController* shell_window_controller =
1293 static_cast<ShellWindowLauncherItemController*>(controller);
1294 shell_window_controller->ActivateIndexedApp(index);
1299 bool ChromeLauncherController::IsWebContentHandledByApplication(
1300 content::WebContents* web_contents,
1301 const std::string& app_id) {
1302 if ((web_contents_to_app_id_.find(web_contents) !=
1303 web_contents_to_app_id_.end()) &&
1304 (web_contents_to_app_id_[web_contents] == app_id))
1306 return (app_id == kGmailAppId && ContentCanBeHandledByGmailApp(web_contents));
1309 bool ChromeLauncherController::ContentCanBeHandledByGmailApp(
1310 content::WebContents* web_contents) {
1311 ash::LauncherID id = GetLauncherIDForAppID(kGmailAppId);
1313 const GURL url = web_contents->GetURL();
1314 // We need to extend the application matching for the gMail app beyond the
1315 // manifest file's specification. This is required because of the namespace
1316 // overlap with the offline app ("/mail/mu/").
1317 if (!MatchPattern(url.path(), "/mail/mu/*") &&
1318 MatchPattern(url.path(), "/mail/*") &&
1319 GetExtensionForAppID(kGmailAppId) &&
1320 GetExtensionForAppID(kGmailAppId)->OverlapsWithOrigin(url))
1326 gfx::Image ChromeLauncherController::GetAppListIcon(
1327 content::WebContents* web_contents) const {
1328 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
1329 if (IsIncognito(web_contents))
1330 return rb.GetImageNamed(IDR_AURA_LAUNCHER_LIST_INCOGNITO_BROWSER);
1331 FaviconTabHelper* favicon_tab_helper =
1332 FaviconTabHelper::FromWebContents(web_contents);
1333 gfx::Image result = favicon_tab_helper->GetFavicon();
1334 if (result.IsEmpty())
1335 return rb.GetImageNamed(IDR_DEFAULT_FAVICON);
1339 string16 ChromeLauncherController::GetAppListTitle(
1340 content::WebContents* web_contents) const {
1341 string16 title = web_contents->GetTitle();
1344 WebContentsToAppIDMap::const_iterator iter =
1345 web_contents_to_app_id_.find(web_contents);
1346 if (iter != web_contents_to_app_id_.end()) {
1347 std::string app_id = iter->second;
1348 const extensions::Extension* extension = GetExtensionForAppID(app_id);
1350 return UTF8ToUTF16(extension->name());
1352 return l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE);
1355 ash::LauncherID ChromeLauncherController::CreateAppShortcutLauncherItem(
1356 const std::string& app_id,
1358 return CreateAppShortcutLauncherItemWithType(app_id,
1360 ash::TYPE_APP_SHORTCUT);
1363 void ChromeLauncherController::SetAppTabHelperForTest(AppTabHelper* helper) {
1364 app_tab_helper_.reset(helper);
1367 void ChromeLauncherController::SetAppIconLoaderForTest(
1368 extensions::AppIconLoader* loader) {
1369 app_icon_loader_.reset(loader);
1372 const std::string& ChromeLauncherController::GetAppIdFromLauncherIdForTest(
1373 ash::LauncherID id) {
1374 return id_to_item_controller_map_[id]->app_id();
1377 void ChromeLauncherController::SetLauncherItemDelegateManagerForTest(
1378 ash::LauncherItemDelegateManager* manager) {
1379 item_delegate_manager_ = manager;
1382 ash::LauncherID ChromeLauncherController::CreateAppShortcutLauncherItemWithType(
1383 const std::string& app_id,
1385 ash::LauncherItemType launcher_item_type) {
1386 AppShortcutLauncherItemController* controller =
1387 new AppShortcutLauncherItemController(app_id, this);
1388 ash::LauncherID launcher_id = InsertAppLauncherItem(
1389 controller, app_id, ash::STATUS_CLOSED, index, launcher_item_type);
1393 LauncherItemController* ChromeLauncherController::GetLauncherItemController(
1394 const ash::LauncherID id) {
1395 if (!HasItemController(id))
1397 return id_to_item_controller_map_[id];
1400 bool ChromeLauncherController::IsBrowserFromActiveUser(Browser* browser) {
1401 #if defined(OS_CHROMEOS)
1402 // If running multi user mode with separate desktops, we have to check if the
1403 // browser is from the active user.
1404 if (chrome::MultiUserWindowManager::GetMultiProfileMode() !=
1405 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED)
1407 chromeos::UserManager* manager = chromeos::UserManager::Get();
1408 return manager->GetActiveUser() ==
1409 manager->GetUserByProfile(browser->profile()->GetOriginalProfile());
1415 Profile* ChromeLauncherController::GetProfileForNewWindows() {
1416 return ProfileManager::GetDefaultProfileOrOffTheRecord();
1419 void ChromeLauncherController::LauncherItemClosed(ash::LauncherID id) {
1420 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
1421 CHECK(iter != id_to_item_controller_map_.end());
1422 CHECK(iter->second);
1423 app_icon_loader_->ClearImage(iter->second->app_id());
1424 id_to_item_controller_map_.erase(iter);
1425 int index = model_->ItemIndexByID(id);
1426 // A "browser proxy" is not known to the model and this removal does
1427 // therefore not need to be propagated to the model.
1429 model_->RemoveItemAt(index);
1432 void ChromeLauncherController::DoPinAppWithID(const std::string& app_id) {
1433 // If there is an item, do nothing and return.
1434 if (IsAppPinned(app_id))
1437 ash::LauncherID launcher_id = GetLauncherIDForAppID(app_id);
1439 // App item exists, pin it
1442 // Otherwise, create a shortcut item for it.
1443 CreateAppShortcutLauncherItem(app_id, model_->item_count());
1445 PersistPinnedState();
1449 void ChromeLauncherController::DoUnpinAppWithID(const std::string& app_id) {
1450 ash::LauncherID launcher_id = GetLauncherIDForAppID(app_id);
1451 if (launcher_id && IsPinned(launcher_id))
1455 int ChromeLauncherController::PinRunningAppInternal(
1457 ash::LauncherID launcher_id) {
1458 int running_index = model_->ItemIndexByID(launcher_id);
1459 ash::LauncherItem item = model_->items()[running_index];
1460 DCHECK(item.type == ash::TYPE_WINDOWED_APP ||
1461 item.type == ash::TYPE_PLATFORM_APP);
1462 item.type = ash::TYPE_APP_SHORTCUT;
1463 model_->Set(running_index, item);
1464 // The |LauncherModel|'s weight system might reposition the item to a
1465 // new index, so we get the index again.
1466 running_index = model_->ItemIndexByID(launcher_id);
1467 if (running_index < index)
1469 if (running_index != index)
1470 model_->Move(running_index, index);
1474 void ChromeLauncherController::UnpinRunningAppInternal(int index) {
1475 DCHECK_GE(index, 0);
1476 ash::LauncherItem item = model_->items()[index];
1477 DCHECK_EQ(item.type, ash::TYPE_APP_SHORTCUT);
1478 item.type = ash::TYPE_WINDOWED_APP;
1479 // A platform app and a windowed app are sharing TYPE_APP_SHORTCUT. As such
1480 // we have to check here what this was before it got a shortcut.
1481 if (HasItemController(item.id) &&
1482 id_to_item_controller_map_[item.id]->type() ==
1483 LauncherItemController::TYPE_APP)
1484 item.type = ash::TYPE_PLATFORM_APP;
1485 model_->Set(index, item);
1488 void ChromeLauncherController::UpdateAppLaunchersFromPref() {
1489 // There are various functions which will trigger a |PersistPinnedState| call
1490 // like a direct call to |DoPinAppWithID|, or an indirect call to the menu
1491 // model which will use weights to re-arrange the icons to new positions.
1492 // Since this function is meant to synchronize the "is state" with the
1493 // "sync state", it makes no sense to store any changes by this function back
1494 // into the pref state. Therefore we tell |persistPinnedState| to ignore any
1495 // invocations while we are running.
1496 base::AutoReset<bool> auto_reset(&ignore_persist_pinned_state_change_, true);
1497 std::vector<std::string> pinned_apps = GetListOfPinnedAppsAndBrowser();
1500 int max_index = model_->item_count();
1502 // When one of the two special items cannot be moved (and we do not know where
1503 // yet), we remember the current location in one of these variables.
1504 int chrome_index = -1;
1505 int app_list_index = -1;
1507 // Walk the model and |pinned_apps| from the pref lockstep, adding and
1508 // removing items as necessary. NB: This code uses plain old indexing instead
1509 // of iterators because of model mutations as part of the loop.
1510 std::vector<std::string>::const_iterator pref_app_id(pinned_apps.begin());
1511 for (; index < max_index && pref_app_id != pinned_apps.end(); ++index) {
1512 // Check if we have an item which we need to handle.
1513 if (*pref_app_id == extension_misc::kChromeAppId ||
1514 *pref_app_id == kAppLauncherIdPlaceholder ||
1515 IsAppPinned(*pref_app_id)) {
1516 for (; index < max_index; ++index) {
1517 const ash::LauncherItem& item(model_->items()[index]);
1518 bool is_app_list = item.type == ash::TYPE_APP_LIST;
1519 bool is_chrome = item.type == ash::TYPE_BROWSER_SHORTCUT;
1520 if (item.type != ash::TYPE_APP_SHORTCUT && !is_app_list && !is_chrome)
1522 IDToItemControllerMap::const_iterator entry =
1523 id_to_item_controller_map_.find(item.id);
1524 if ((kAppLauncherIdPlaceholder == *pref_app_id && is_app_list) ||
1525 (extension_misc::kChromeAppId == *pref_app_id && is_chrome) ||
1526 (entry != id_to_item_controller_map_.end() &&
1527 entry->second->app_id() == *pref_app_id)) {
1528 // Check if an item needs to be moved here.
1529 MoveChromeOrApplistToFinalPosition(
1530 is_chrome, is_app_list, index, &chrome_index, &app_list_index);
1534 if (is_chrome || is_app_list) {
1535 // We cannot delete any of these shortcuts. As such we remember
1536 // their positions and move them later where they belong.
1538 chrome_index = index;
1540 app_list_index = index;
1541 // And skip the item - or exit the loop if end is reached (note that
1542 // in that case we will reduce the index again by one and this only
1543 // compensates for it).
1544 if (index >= max_index - 1)
1548 // Check if this is a platform or a windowed app.
1549 if (item.type == ash::TYPE_APP_SHORTCUT &&
1550 (id_to_item_controller_map_[item.id]->locked() ||
1551 id_to_item_controller_map_[item.id]->type() ==
1552 LauncherItemController::TYPE_APP)) {
1553 // Note: This will not change the amount of items (|max_index|).
1554 // Even changes to the actual |index| due to item weighting
1555 // changes should be fine.
1556 UnpinRunningAppInternal(index);
1558 LauncherItemClosed(item.id);
1565 // If the item wasn't found, that means id_to_item_controller_map_
1567 DCHECK(index <= max_index);
1569 // Check if the item was already running but not yet pinned.
1570 ash::LauncherID launcher_id = GetLauncherIDForAppID(*pref_app_id);
1572 // This app is running but not yet pinned. So pin and move it.
1573 index = PinRunningAppInternal(index, launcher_id);
1575 // This app wasn't pinned before, insert a new entry.
1576 launcher_id = CreateAppShortcutLauncherItem(*pref_app_id, index);
1578 index = model_->ItemIndexByID(launcher_id);
1584 // Remove any trailing existing items.
1585 while (index < model_->item_count()) {
1586 const ash::LauncherItem& item(model_->items()[index]);
1587 if (item.type == ash::TYPE_APP_SHORTCUT) {
1588 if (id_to_item_controller_map_[item.id]->locked() ||
1589 id_to_item_controller_map_[item.id]->type() ==
1590 LauncherItemController::TYPE_APP)
1591 UnpinRunningAppInternal(index);
1593 LauncherItemClosed(item.id);
1595 if (item.type == ash::TYPE_BROWSER_SHORTCUT)
1596 chrome_index = index;
1597 else if (item.type == ash::TYPE_APP_LIST)
1598 app_list_index = index;
1603 // Append unprocessed items from the pref to the end of the model.
1604 for (; pref_app_id != pinned_apps.end(); ++pref_app_id) {
1605 // All items but the chrome and / or app list shortcut needs to be added.
1606 bool is_chrome = *pref_app_id == extension_misc::kChromeAppId;
1607 bool is_app_list = *pref_app_id == kAppLauncherIdPlaceholder;
1608 // Coming here we know the next item which can be finalized, either the
1609 // chrome item or the app launcher. The final position is the end of the
1610 // list. The menu model will make sure that the item is grouped according
1611 // to its weight (which we do not know here).
1612 if (!is_chrome && !is_app_list) {
1613 DoPinAppWithID(*pref_app_id);
1614 int target_index = FindInsertionPoint(false);
1615 ash::LauncherID id = GetLauncherIDForAppID(*pref_app_id);
1616 int source_index = model_->ItemIndexByID(id);
1617 if (source_index != target_index)
1618 model_->Move(source_index, target_index);
1620 // Needed for the old layout - the weight might force it to be lower in
1622 if (app_list_index != -1 && target_index <= app_list_index)
1625 int target_index = FindInsertionPoint(is_app_list);
1626 MoveChromeOrApplistToFinalPosition(
1627 is_chrome, is_app_list, target_index, &chrome_index, &app_list_index);
1632 void ChromeLauncherController::SetShelfAutoHideBehaviorPrefs(
1633 ash::ShelfAutoHideBehavior behavior,
1634 aura::Window* root_window) {
1635 const char* value = NULL;
1637 case ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS:
1638 value = ash::kShelfAutoHideBehaviorAlways;
1640 case ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER:
1641 value = ash::kShelfAutoHideBehaviorNever;
1643 case ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN:
1644 // This one should not be a valid preference option for now. We only want
1645 // to completely hide it when we run app mode.
1650 UpdatePerDisplayPref(
1651 profile_->GetPrefs(), root_window, prefs::kShelfAutoHideBehavior, value);
1653 if (root_window == ash::Shell::GetPrimaryRootWindow()) {
1654 // See comment in |kShelfAlignment| about why we have two prefs here.
1655 profile_->GetPrefs()->SetString(prefs::kShelfAutoHideBehaviorLocal, value);
1656 profile_->GetPrefs()->SetString(prefs::kShelfAutoHideBehavior, value);
1660 void ChromeLauncherController::SetShelfAutoHideBehaviorFromPrefs() {
1661 ash::Shell::RootWindowList root_windows = ash::Shell::GetAllRootWindows();
1663 for (ash::Shell::RootWindowList::const_iterator iter = root_windows.begin();
1664 iter != root_windows.end(); ++iter) {
1665 ash::Shell::GetInstance()->SetShelfAutoHideBehavior(
1666 GetShelfAutoHideBehavior(*iter), *iter);
1670 void ChromeLauncherController::SetShelfAlignmentFromPrefs() {
1671 if (!ash::ShelfWidget::ShelfAlignmentAllowed())
1674 ash::Shell::RootWindowList root_windows = ash::Shell::GetAllRootWindows();
1676 for (ash::Shell::RootWindowList::const_iterator iter = root_windows.begin();
1677 iter != root_windows.end(); ++iter) {
1678 // See comment in |kShelfAlignment| as to why we consider two prefs.
1679 const std::string alignment_value(
1680 GetPrefForRootWindow(profile_->GetPrefs(),
1682 prefs::kShelfAlignmentLocal,
1683 prefs::kShelfAlignment));
1684 ash::ShelfAlignment alignment = ash::SHELF_ALIGNMENT_BOTTOM;
1685 if (alignment_value == ash::kShelfAlignmentLeft)
1686 alignment = ash::SHELF_ALIGNMENT_LEFT;
1687 else if (alignment_value == ash::kShelfAlignmentRight)
1688 alignment = ash::SHELF_ALIGNMENT_RIGHT;
1689 else if (alignment_value == ash::kShelfAlignmentTop)
1690 alignment = ash::SHELF_ALIGNMENT_TOP;
1691 ash::Shell::GetInstance()->SetShelfAlignment(alignment, *iter);
1695 void ChromeLauncherController::SetShelfBehaviorsFromPrefs() {
1696 SetShelfAutoHideBehaviorFromPrefs();
1697 SetShelfAlignmentFromPrefs();
1700 WebContents* ChromeLauncherController::GetLastActiveWebContents(
1701 const std::string& app_id) {
1702 AppIDToWebContentsListMap::const_iterator i =
1703 app_id_to_web_contents_list_.find(app_id);
1704 if (i == app_id_to_web_contents_list_.end())
1706 DCHECK_GT(i->second.size(), 0u);
1707 return *i->second.begin();
1710 ash::LauncherID ChromeLauncherController::InsertAppLauncherItem(
1711 LauncherItemController* controller,
1712 const std::string& app_id,
1713 ash::LauncherItemStatus status,
1715 ash::LauncherItemType launcher_item_type) {
1716 ash::LauncherID id = model_->next_id();
1717 CHECK(!HasItemController(id));
1719 id_to_item_controller_map_[id] = controller;
1720 controller->set_launcher_id(id);
1722 ash::LauncherItem item;
1723 item.type = launcher_item_type;
1724 item.image = extensions::IconsInfo::GetDefaultAppIcon();
1726 WebContents* active_tab = GetLastActiveWebContents(app_id);
1728 Browser* browser = chrome::FindBrowserWithWebContents(active_tab);
1730 if (browser->window()->IsActive())
1731 status = ash::STATUS_ACTIVE;
1733 status = ash::STATUS_RUNNING;
1735 item.status = status;
1737 model_->AddAt(index, item);
1739 app_icon_loader_->FetchImage(app_id);
1741 SetLauncherItemDelegate(id, controller);
1746 bool ChromeLauncherController::HasItemController(ash::LauncherID id) const {
1747 return id_to_item_controller_map_.find(id) !=
1748 id_to_item_controller_map_.end();
1751 std::vector<content::WebContents*>
1752 ChromeLauncherController::GetV1ApplicationsFromController(
1753 LauncherItemController* controller) {
1754 DCHECK(controller->type() == LauncherItemController::TYPE_SHORTCUT);
1755 AppShortcutLauncherItemController* app_controller =
1756 static_cast<AppShortcutLauncherItemController*>(controller);
1757 return app_controller->GetRunningApplications();
1760 BrowserShortcutLauncherItemController*
1761 ChromeLauncherController::GetBrowserShortcutLauncherItemController() {
1762 for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin();
1763 i != id_to_item_controller_map_.end(); ++i) {
1764 int index = model_->ItemIndexByID(i->first);
1765 const ash::LauncherItem& item = model_->items()[index];
1766 if (item.type == ash::TYPE_BROWSER_SHORTCUT)
1767 return static_cast<BrowserShortcutLauncherItemController*>(i->second);
1769 // Create a LauncherItemController for the Browser shortcut if it does not
1771 ash::LauncherID id = CreateBrowserShortcutLauncherItem();
1772 DCHECK(id_to_item_controller_map_[id]);
1773 return static_cast<BrowserShortcutLauncherItemController*>(
1774 id_to_item_controller_map_[id]);
1777 ash::LauncherID ChromeLauncherController::CreateBrowserShortcutLauncherItem() {
1778 ash::LauncherItem browser_shortcut;
1779 browser_shortcut.type = ash::TYPE_BROWSER_SHORTCUT;
1780 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
1781 browser_shortcut.image = *rb.GetImageSkiaNamed(IDR_PRODUCT_LOGO_32);
1782 ash::LauncherID id = model_->next_id();
1783 size_t index = GetChromeIconIndexForCreation();
1784 model_->AddAt(index, browser_shortcut);
1785 id_to_item_controller_map_[id] =
1786 new BrowserShortcutLauncherItemController(this);
1787 id_to_item_controller_map_[id]->set_launcher_id(id);
1788 // LauncherItemDelegateManager owns BrowserShortcutLauncherItemController.
1789 SetLauncherItemDelegate(id, id_to_item_controller_map_[id]);
1793 void ChromeLauncherController::PersistChromeItemIndex(int index) {
1794 profile_->GetPrefs()->SetInteger(prefs::kShelfChromeIconIndex, index);
1797 int ChromeLauncherController::GetChromeIconIndexFromPref() const {
1798 size_t index = profile_->GetPrefs()->GetInteger(prefs::kShelfChromeIconIndex);
1799 const base::ListValue* pinned_apps_pref =
1800 profile_->GetPrefs()->GetList(prefs::kPinnedLauncherApps);
1801 return std::max(static_cast<size_t>(0),
1802 std::min(pinned_apps_pref->GetSize(), index));
1805 void ChromeLauncherController::MoveChromeOrApplistToFinalPosition(
1810 int* app_list_index) {
1811 if (is_chrome && *chrome_index != -1) {
1812 model_->Move(*chrome_index, target_index);
1813 if (*app_list_index != -1 &&
1814 *chrome_index < *app_list_index &&
1815 target_index > *app_list_index)
1816 --(*app_list_index);
1818 } else if (is_app_list && *app_list_index != -1) {
1819 model_->Move(*app_list_index, target_index);
1820 if (*chrome_index != -1 &&
1821 *app_list_index < *chrome_index &&
1822 target_index > *chrome_index)
1824 *app_list_index = -1;
1828 int ChromeLauncherController::FindInsertionPoint(bool is_app_list) {
1829 bool alternate = ash::switches::UseAlternateShelfLayout();
1830 for (int i = model_->item_count() - 1; i > 0; --i) {
1831 ash::LauncherItemType type = model_->items()[i].type;
1832 if (type == ash::TYPE_APP_SHORTCUT ||
1833 ((is_app_list || alternate) && type == ash::TYPE_APP_LIST) ||
1834 type == ash::TYPE_BROWSER_SHORTCUT ||
1835 type == ash::TYPE_WINDOWED_APP)
1841 int ChromeLauncherController::GetChromeIconIndexForCreation() {
1842 // We get the list of pinned apps as they currently would get pinned.
1843 // Within this list the chrome icon will be the correct location.
1844 std::vector<std::string> pinned_apps = GetListOfPinnedAppsAndBrowser();
1846 std::vector<std::string>::iterator it =
1847 std::find(pinned_apps.begin(),
1849 std::string(extension_misc::kChromeAppId));
1850 DCHECK(it != pinned_apps.end());
1851 int index = it - pinned_apps.begin();
1853 // We should do here a comparison between the is state and the "want to be"
1854 // state since some apps might be able to pin but are not yet. Instead - for
1855 // the time being we clamp against the amount of known items and wait for the
1856 // next |UpdateAppLaunchersFromPref()| call to correct it - it will come since
1857 // the pinning will be done then.
1858 return std::min(model_->item_count(), index);
1861 std::vector<std::string>
1862 ChromeLauncherController::GetListOfPinnedAppsAndBrowser() {
1863 // Adding the app list item to the list of items requires that the ID is not
1864 // a valid and known ID for the extension system. The ID was constructed that
1865 // way - but just to make sure...
1866 DCHECK(!app_tab_helper_->IsValidIDForCurrentUser(kAppLauncherIdPlaceholder));
1868 std::vector<std::string> pinned_apps;
1870 // Get the new incarnation of the list.
1871 const base::ListValue* pinned_apps_pref =
1872 profile_->GetPrefs()->GetList(prefs::kPinnedLauncherApps);
1874 // Keep track of the addition of the chrome and the app list icon.
1875 bool chrome_icon_added = false;
1876 bool app_list_icon_added = false;
1877 size_t chrome_icon_index = GetChromeIconIndexFromPref();
1879 // See if the chrome string is already in the pinned list and remove it if
1881 base::Value* chrome_app = ash::CreateAppDict(extension_misc::kChromeAppId);
1883 chrome_icon_added = pinned_apps_pref->Find(*chrome_app) !=
1884 pinned_apps_pref->end();
1888 for (size_t index = 0; index < pinned_apps_pref->GetSize(); ++index) {
1889 // We need to position the chrome icon relative to it's place in the pinned
1890 // preference list - even if an item of that list isn't shown yet.
1891 if (index == chrome_icon_index && !chrome_icon_added) {
1892 pinned_apps.push_back(extension_misc::kChromeAppId);
1893 chrome_icon_added = true;
1895 const DictionaryValue* app = NULL;
1897 if (pinned_apps_pref->GetDictionary(index, &app) &&
1898 app->GetString(ash::kPinnedAppsPrefAppIDPath, &app_id) &&
1899 (std::find(pinned_apps.begin(), pinned_apps.end(), app_id) ==
1900 pinned_apps.end())) {
1901 if (app_id == extension_misc::kChromeAppId) {
1902 chrome_icon_added = true;
1903 pinned_apps.push_back(extension_misc::kChromeAppId);
1904 } else if (app_id == kAppLauncherIdPlaceholder) {
1905 app_list_icon_added = true;
1906 pinned_apps.push_back(kAppLauncherIdPlaceholder);
1907 } else if (app_tab_helper_->IsValidIDForCurrentUser(app_id)) {
1908 // Note: In multi profile scenarios we only want to show pinnable apps
1909 // here which is correct. Running applications from the other users will
1910 // continue to run. So no need for multi profile modifications.
1911 pinned_apps.push_back(app_id);
1916 // If not added yet, the chrome item will be the last item in the list.
1917 if (!chrome_icon_added)
1918 pinned_apps.push_back(extension_misc::kChromeAppId);
1920 // If not added yet, add the app list item either at the end or at the
1921 // beginning - depending on the shelf layout.
1922 if (!app_list_icon_added) {
1923 if (ash::switches::UseAlternateShelfLayout())
1924 pinned_apps.insert(pinned_apps.begin(), kAppLauncherIdPlaceholder);
1926 pinned_apps.push_back(kAppLauncherIdPlaceholder);
1931 bool ChromeLauncherController::IsIncognito(
1932 const content::WebContents* web_contents) const {
1933 const Profile* profile =
1934 Profile::FromBrowserContext(web_contents->GetBrowserContext());
1935 return profile->IsOffTheRecord() && !profile->IsGuestSession();
1938 void ChromeLauncherController::CloseWindowedAppsFromRemovedExtension(
1939 const std::string& app_id) {
1940 // This function cannot rely on the controller's enumeration functionality
1941 // since the extension has already be unloaded.
1942 const BrowserList* ash_browser_list =
1943 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
1944 std::vector<Browser*> browser_to_close;
1945 for (BrowserList::const_reverse_iterator
1946 it = ash_browser_list->begin_last_active();
1947 it != ash_browser_list->end_last_active(); ++it) {
1948 Browser* browser = *it;
1949 if (!browser->is_type_tabbed() &&
1950 browser->is_type_popup() &&
1951 browser->is_app() &&
1952 app_id == web_app::GetExtensionIdFromApplicationName(
1953 browser->app_name())) {
1954 browser_to_close.push_back(browser);
1957 while (!browser_to_close.empty()) {
1958 TabStripModel* tab_strip = browser_to_close.back()->tab_strip_model();
1959 tab_strip->CloseWebContentsAt(0, TabStripModel::CLOSE_NONE);
1960 browser_to_close.pop_back();
1964 void ChromeLauncherController::SetLauncherItemDelegate(
1966 ash::LauncherItemDelegate* item_delegate) {
1968 DCHECK(item_delegate);
1969 DCHECK(item_delegate_manager_);
1970 item_delegate_manager_->SetLauncherItemDelegate(id,
1971 scoped_ptr<ash::LauncherItemDelegate>(item_delegate).Pass());
1974 void ChromeLauncherController::AttachProfile(Profile* profile) {
1976 // Either add the profile to the list of known profiles and make it the active
1977 // one for some functions of AppTabHelper or create a new one.
1978 if (!app_tab_helper_.get())
1979 app_tab_helper_.reset(new LauncherAppTabHelper(profile_));
1981 app_tab_helper_->SetCurrentUser(profile_);
1982 // TODO(skuhne): The AppIconLoaderImpl has the same problem. Each loaded
1983 // image is associated with a profile (it's loader requires the profile).
1984 // Since icon size changes are possible, the icon could be requested to be
1985 // reloaded. However - having it not multi profile aware would cause problems
1986 // if the icon cache gets deleted upon user switch.
1987 app_icon_loader_.reset(new extensions::AppIconLoaderImpl(
1988 profile_, extension_misc::EXTENSION_ICON_SMALL, this));
1990 pref_change_registrar_.Init(profile_->GetPrefs());
1991 pref_change_registrar_.Add(
1992 prefs::kPinnedLauncherApps,
1993 base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref,
1994 base::Unretained(this)));
1995 pref_change_registrar_.Add(
1996 prefs::kShelfAlignmentLocal,
1997 base::Bind(&ChromeLauncherController::SetShelfAlignmentFromPrefs,
1998 base::Unretained(this)));
1999 pref_change_registrar_.Add(
2000 prefs::kShelfAutoHideBehaviorLocal,
2001 base::Bind(&ChromeLauncherController::
2002 SetShelfAutoHideBehaviorFromPrefs,
2003 base::Unretained(this)));
2004 pref_change_registrar_.Add(
2005 prefs::kShelfPreferences,
2006 base::Bind(&ChromeLauncherController::SetShelfBehaviorsFromPrefs,
2007 base::Unretained(this)));
2010 void ChromeLauncherController::ReleaseProfile() {
2011 if (app_sync_ui_state_)
2012 app_sync_ui_state_->RemoveObserver(this);
2014 PrefServiceSyncable::FromProfile(profile_)->RemoveObserver(this);
2016 pref_change_registrar_.RemoveAll();
2019 void ChromeLauncherController::UpdateV1AppStatesAfterUserSwitch() {
2020 #if defined(OS_CHROMEOS)
2021 if (!ash::switches::UseFullMultiProfileMode() &&
2022 ChromeShellDelegate::instance() &&
2023 ChromeShellDelegate::instance()->IsMultiProfilesEnabled()) {
2024 // First we add the new applications.
2025 BrowserList* browser_list =
2026 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
2027 chromeos::UserManager* user_manager = chromeos::UserManager::Get();
2028 chromeos::User* active_user = user_manager->GetActiveUser();
2030 // Remove old (tabbed V1) applications.
2031 for (BrowserList::const_iterator it = browser_list->begin();
2032 it != browser_list->end(); ++it) {
2033 Browser* browser = *it;
2034 if (!browser->is_app() &&
2035 browser->is_type_tabbed() &&
2036 active_user != user_manager->GetUserByProfile(
2037 browser->profile()->GetOriginalProfile())) {
2038 for (int i = 0; i < browser->tab_strip_model()->count(); ++i) {
2039 UpdateAppState(browser->tab_strip_model()->GetWebContentsAt(i),
2045 // Add new (tabbed V1) applications.
2046 for (BrowserList::const_iterator it = browser_list->begin();
2047 it != browser_list->end(); ++it) {
2048 Browser* browser = *it;
2049 if (!browser->is_app() &&
2050 browser->is_type_tabbed() &&
2051 active_user == user_manager->GetUserByProfile(
2052 browser->profile()->GetOriginalProfile())) {
2053 int active_index = browser->tab_strip_model()->active_index();
2054 for (int i = 0; i < browser->tab_strip_model()->count(); ++i) {
2055 UpdateAppState(browser->tab_strip_model()->GetWebContentsAt(i),
2056 browser->window()->IsActive() && i == active_index ?
2057 APP_STATE_WINDOW_ACTIVE : APP_STATE_INACTIVE);
2063 // Finally we update the browser state itself.
2064 browser_status_monitor_->UpdateBrowserItemState();