Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / ash / launcher / chrome_launcher_controller.cc
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.
4
5 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
6
7 #include <vector>
8
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"
91
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"
98 #endif
99
100 using extensions::Extension;
101 using extensions::UnloadedExtensionInfo;
102 using extension_misc::kGmailAppId;
103 using content::WebContents;
104
105 // static
106 ChromeLauncherController* ChromeLauncherController::instance_ = NULL;
107
108 namespace {
109
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
112 // will ignore it.
113 const char kAppShelfIdPlaceholder[] = "AppShelfIDPlaceholder--------";
114
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());
119
120   return base::Int64ToString(display.id());
121 }
122
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);
128   if (key.empty())
129     return;
130
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);
137   }
138   prefs->SetStringWithoutPathExpansion(pref_key, value);
139 }
140
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
144 // the priority:
145 //  * A value managed by policy. This is a single value that applies to all
146 //    displays.
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
153 //    root windows.
154 //  * The default value for |local_path| if the value is not recommended by
155 //    policy.
156 std::string GetPrefForRootWindow(PrefService* pref_service,
157                                  aura::Window* root_window,
158                                  const char* local_path,
159                                  const char* 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())
164     return value;
165
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;
176
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;
186         break;
187       }
188     }
189   }
190
191   if (local_pref->IsRecommended() || !has_per_display_prefs)
192     return value;
193
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;
199 }
200
201 // Gets the shelf auto hide behavior from prefs for a root window.
202 ash::ShelfAutoHideBehavior GetShelfAutoHideBehaviorFromPrefs(
203     Profile* profile,
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;
208
209   // See comment in |kShelfAlignment| as to why we consider two prefs.
210   const std::string behavior_value(
211       GetPrefForRootWindow(profile->GetPrefs(),
212                            root_window,
213                            prefs::kShelfAutoHideBehaviorLocal,
214                            prefs::kShelfAutoHideBehavior));
215
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;
223 }
224
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(),
231                            root_window,
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;
241 }
242
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
251     // local.
252     pref_service->SetString(local_path, pref_service->GetString(synced_path));
253   }
254 }
255
256 std::string GetSourceFromAppListSource(ash::LaunchSource source) {
257   switch (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();
263   }
264 }
265
266 }  // namespace
267
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 {
274  public:
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());
286   }
287   virtual ~ChromeLauncherControllerUserSwitchObserverChromeOS() {
288     user_manager::UserManager::Get()->RemoveSessionStateObserver(this);
289   }
290
291   // user_manager::UserManager::UserSessionStateObserver overrides:
292   virtual void UserAddedToSession(
293       const user_manager::User* added_user) override;
294
295   // content::NotificationObserver overrides:
296   virtual void Observe(int type,
297                const content::NotificationSource& source,
298                const content::NotificationDetails& details) override;
299
300  private:
301   // Add a user to the session.
302   void AddUser(Profile* profile);
303
304   // The owning ChromeLauncherController.
305   ChromeLauncherController* controller_;
306
307   // The notification registrar to track the Profile creations after a user got
308   // added to the session (if required).
309   content::NotificationRegistrar registrar_;
310
311   // Users which were just added to the system, but which profiles were not yet
312   // (fully) loaded.
313   std::set<std::string> added_user_ids_waiting_for_profiles_;
314
315   DISALLOW_COPY_AND_ASSIGN(ChromeLauncherControllerUserSwitchObserverChromeOS);
316 };
317
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.
324   if (!profile)
325     added_user_ids_waiting_for_profiles_.insert(active_user->email());
326   else
327     AddUser(profile);
328 }
329
330 void ChromeLauncherControllerUserSwitchObserverChromeOS::Observe(
331     int type,
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(),
342         user_id);
343     if (it != added_user_ids_waiting_for_profiles_.end()) {
344       added_user_ids_waiting_for_profiles_.erase(it);
345       AddUser(profile->GetOriginalProfile());
346     }
347   }
348 }
349
350 void ChromeLauncherControllerUserSwitchObserverChromeOS::AddUser(
351     Profile* profile) {
352   if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
353           chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED)
354     chrome::MultiUserWindowManager::GetInstance()->AddUser(profile);
355   controller_->AdditionalUserAddedToSession(profile->GetOriginalProfile());
356 }
357 #endif
358
359 ChromeLauncherController::ChromeLauncherController(Profile* profile,
360                                                    ash::ShelfModel* model)
361     : model_(model),
362       item_delegate_manager_(NULL),
363       profile_(profile),
364       app_sync_ui_state_(NULL),
365       ignore_persist_pinned_state_change_(false) {
366   if (!profile_) {
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
371     // the right one).
372     Profile* active_profile = ProfileManager::GetActiveUserProfile();
373     profile_ = active_profile->IsGuestSession() ? active_profile :
374         active_profile->GetOriginalProfile();
375
376     app_sync_ui_state_ = AppSyncUIState::Get(profile_);
377     if (app_sync_ui_state_)
378       app_sync_ui_state_->AddObserver(this);
379   }
380
381   // All profile relevant settings get bound to the current profile.
382   AttachProfile(profile_);
383   model_->AddObserver(this);
384
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();
388
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));
396   }
397
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));
407   } else {
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));
412   }
413 #else
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));
418 #endif
419
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();
426   }
427 }
428
429 ChromeLauncherController::~ChromeLauncherController() {
430   // Reset the BrowserStatusMonitor as it has a weak pointer to this.
431   browser_status_monitor_.reset();
432
433   // Reset the app window controller here since it has a weak pointer to this.
434   app_window_controller_.reset();
435
436   for (std::set<ash::Shelf*>::iterator iter = shelves_.begin();
437        iter != shelves_.end();
438        ++iter)
439     (*iter)->shelf_widget()->shelf_layout_manager()->RemoveObserver(this);
440
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.
449     if (index != -1 &&
450         model_->items()[index].type != ash::TYPE_BROWSER_SHORTCUT)
451       model_->RemoveItemAt(index);
452   }
453
454   if (ash::Shell::HasInstance())
455     ash::Shell::GetInstance()->RemoveShellObserver(this);
456
457   // Release all profile dependent resources.
458   ReleaseProfile();
459   if (instance_ == this)
460     instance_ = NULL;
461
462   // Get rid of the multi user window manager instance.
463   chrome::MultiUserWindowManager::DeleteInstance();
464 }
465
466 // static
467 ChromeLauncherController* ChromeLauncherController::CreateInstance(
468     Profile* profile,
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);
474   return instance_;
475 }
476
477 void ChromeLauncherController::Init() {
478   CreateBrowserShortcutLauncherItem();
479   UpdateAppLaunchersFromPref();
480
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)->
491             HasUserSetting()) {
492       // This causes OnIsSyncingChanged to be called when the value of
493       // PrefService::IsSyncing() changes.
494       prefs->AddObserver(this);
495     }
496     ash::Shell::GetInstance()->AddShellObserver(this);
497   }
498 }
499
500 ash::ShelfID ChromeLauncherController::CreateAppLauncherItem(
501     LauncherItemController* controller,
502     const std::string& app_id,
503     ash::ShelfItemStatus status) {
504   CHECK(controller);
505   int index = 0;
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,
510                                app_id,
511                                status,
512                                index,
513                                controller->GetShelfItemType());
514 }
515
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
521   // index here.
522   if (index >= 0 && old_status != status) {
523     ash::ShelfItem item = model_->items()[index];
524     item.status = status;
525     model_->Set(index, item);
526   }
527 }
528
529 void ChromeLauncherController::SetItemController(
530     ash::ShelfID id,
531     LauncherItemController* controller) {
532   CHECK(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);
539 }
540
541 void ChromeLauncherController::CloseLauncherItem(ash::ShelfID id) {
542   CHECK(id);
543   if (IsPinned(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);
553   } else {
554     LauncherItemClosed(id);
555   }
556 }
557
558 void ChromeLauncherController::Pin(ash::ShelfID id) {
559   DCHECK(HasItemController(id));
560
561   int index = model_->ItemIndexByID(id);
562   DCHECK_GE(index, 0);
563
564   ash::ShelfItem item = model_->items()[index];
565
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) {
571     return;
572   }
573
574   if (CanPin())
575     PersistPinnedState();
576 }
577
578 void ChromeLauncherController::Unpin(ash::ShelfID id) {
579   DCHECK(HasItemController(id));
580
581   LauncherItemController* controller = id_to_item_controller_map_[id];
582   if (controller->type() == LauncherItemController::TYPE_APP ||
583       controller->locked()) {
584     UnpinRunningAppInternal(model_->ItemIndexByID(id));
585   } else {
586     LauncherItemClosed(id);
587   }
588   if (CanPin())
589     PersistPinnedState();
590 }
591
592 bool ChromeLauncherController::IsPinned(ash::ShelfID id) {
593   int index = model_->ItemIndexByID(id);
594   if (index < 0)
595     return false;
596   ash::ShelfItemType type = model_->items()[index].type;
597   return (type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_BROWSER_SHORTCUT);
598 }
599
600 void ChromeLauncherController::TogglePinned(ash::ShelfID id) {
601   if (!HasItemController(id))
602     return;  // May happen if item closed with menu open.
603
604   if (IsPinned(id))
605     Unpin(id);
606   else
607     Pin(id);
608 }
609
610 bool ChromeLauncherController::IsPinnable(ash::ShelfID id) const {
611   int index = model_->ItemIndexByID(id);
612   if (index == -1)
613     return false;
614
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) &&
619           CanPin());
620 }
621
622 void ChromeLauncherController::Install(ash::ShelfID id) {
623   if (!HasItemController(id))
624     return;
625
626   std::string app_id = GetAppIDForShelfID(id);
627   if (extensions::util::IsExtensionInstalledPermanently(app_id, profile_))
628     return;
629
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();
635   }
636 }
637
638 bool ChromeLauncherController::CanInstall(ash::ShelfID id) {
639   int index = model_->ItemIndexByID(id);
640   if (index == -1)
641     return false;
642
643   ash::ShelfItemType type = model_->items()[index].type;
644   if (type != ash::TYPE_PLATFORM_APP)
645     return false;
646
647   return extensions::util::IsEphemeralApp(GetAppIDForShelfID(id), profile_);
648 }
649
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);
658   }
659   CHECK(id);
660   id_to_item_controller_map_[id]->lock();
661 }
662
663 void ChromeLauncherController::UnlockV1AppWithID(const std::string& app_id) {
664   ash::ShelfID id = GetShelfIDForAppID(app_id);
665   CHECK(IsPinned(id) || IsWindowedAppInLauncher(app_id));
666   CHECK(id);
667   LauncherItemController* controller = id_to_item_controller_map_[id];
668   controller->unlock();
669   if (!controller->locked() && !IsPinned(id))
670     CloseLauncherItem(id);
671 }
672
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);
677 }
678
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();
683 }
684
685 bool ChromeLauncherController::IsOpen(ash::ShelfID id) {
686   if (!HasItemController(id))
687     return false;
688   return id_to_item_controller_map_[id]->IsOpen();
689 }
690
691 bool ChromeLauncherController::IsPlatformApp(ash::ShelfID id) {
692   if (!HasItemController(id))
693     return false;
694
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
698   // available.
699   return extension ? extension->is_platform_app() : false;
700 }
701
702 void ChromeLauncherController::LaunchApp(const std::string& app_id,
703                                          ash::LaunchSource source,
704                                          int event_flags) {
705   // |extension| could be NULL when it is being unloaded for updating.
706   const Extension* extension = GetExtensionForAppID(app_id);
707   if (!extension)
708     return;
709
710   if (!extensions::util::IsAppLaunchableWithoutEnabling(app_id, profile_)) {
711     // Do nothing if there is already a running enable flow.
712     if (extension_enable_flow_)
713       return;
714
715     extension_enable_flow_.reset(
716         new ExtensionEnableFlow(profile_, app_id, this));
717     extension_enable_flow_->StartForNativeWindow(NULL);
718     return;
719   }
720
721 #if defined(OS_WIN)
722   if (LaunchedInNativeDesktop(app_id))
723     return;
724 #endif
725
726   // The app will be created for the currently active profile.
727   AppLaunchParams params(profile_,
728                          extension,
729                          event_flags,
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);
735
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);
740   }
741
742   params.source = (source == ash::LAUNCH_FROM_UNKNOWN)
743                       ? extensions::SOURCE_UNTRACKED
744                       : extensions::SOURCE_APP_LAUNCHER;
745
746   OpenApplication(params);
747 }
748
749 void ChromeLauncherController::ActivateApp(const std::string& app_id,
750                                            ash::LaunchSource source,
751                                            int event_flags) {
752   // If there is an existing non-shortcut controller for this app, open it.
753   ash::ShelfID id = GetShelfIDForAppID(app_id);
754   if (id) {
755     LauncherItemController* controller = id_to_item_controller_map_[id];
756     controller->Activate(source);
757     return;
758   }
759
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);
766   else
767     LaunchApp(app_id, source, event_flags);
768 }
769
770 extensions::LaunchType ChromeLauncherController::GetLaunchType(
771     ash::ShelfID id) {
772   DCHECK(HasItemController(id));
773
774   const Extension* extension = GetExtensionForAppID(
775       id_to_item_controller_map_[id]->app_id());
776
777   // An extension can be unloaded/updated/unavailable at any time.
778   if (!extension)
779     return extensions::LAUNCH_TYPE_DEFAULT;
780
781   return extensions::GetLaunchType(extensions::ExtensionPrefs::Get(profile_),
782                                    extension);
783 }
784
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)
793       return i->first;
794   }
795   return 0;
796 }
797
798 const std::string& ChromeLauncherController::GetAppIDForShelfID(
799     ash::ShelfID id) {
800   CHECK(HasItemController(id));
801   return id_to_item_controller_map_[id]->app_id();
802 }
803
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)
812       continue;
813     if (controller->image_set_by_controller())
814       continue;
815     int index = model_->ItemIndexByID(i->first);
816     if (index == -1)
817       continue;
818     ash::ShelfItem item = model_->items()[index];
819     item.image = image;
820     model_->Set(index, item);
821     // It's possible we're waiting on more than one item, so don't break.
822   }
823 }
824
825 void ChromeLauncherController::OnAutoHideBehaviorChanged(
826     aura::Window* root_window,
827     ash::ShelfAutoHideBehavior new_behavior) {
828   SetShelfAutoHideBehaviorPrefs(new_behavior, root_window);
829 }
830
831 void ChromeLauncherController::SetLauncherItemImage(
832     ash::ShelfID shelf_id,
833     const gfx::ImageSkia& image) {
834   int index = model_->ItemIndexByID(shelf_id);
835   if (index == -1)
836     return;
837   ash::ShelfItem item = model_->items()[index];
838   item.image = image;
839   model_->Set(index, item);
840 }
841
842 bool ChromeLauncherController::CanPin() const {
843   const PrefService::Preference* pref =
844       profile_->GetPrefs()->FindPreference(prefs::kPinnedLauncherApps);
845   return pref && pref->IsUserModifiable();
846 }
847
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)
853       return true;
854   }
855   return false;
856 }
857
858 bool ChromeLauncherController::IsWindowedAppInLauncher(
859     const std::string& app_id) {
860   int index = model_->ItemIndexByID(GetShelfIDForAppID(app_id));
861   if (index < 0)
862     return false;
863
864   ash::ShelfItemType type = model_->items()[index].type;
865   return type == ash::TYPE_WINDOWED_APP;
866 }
867
868 void ChromeLauncherController::PinAppWithID(const std::string& app_id) {
869   if (CanPin())
870     DoPinAppWithID(app_id);
871   else
872     NOTREACHED();
873 }
874
875 void ChromeLauncherController::SetLaunchType(
876     ash::ShelfID id,
877     extensions::LaunchType launch_type) {
878   if (!HasItemController(id))
879     return;
880
881   extensions::SetLaunchType(
882       extensions::ExtensionSystem::Get(profile_)->extension_service(),
883       id_to_item_controller_map_[id]->app_id(),
884       launch_type);
885 }
886
887 void ChromeLauncherController::UnpinAppWithID(const std::string& app_id) {
888   if (CanPin())
889     DoUnpinAppWithID(app_id);
890   else
891     NOTREACHED();
892 }
893
894 bool ChromeLauncherController::IsLoggedInAsGuest() {
895   return profile_->IsGuestSession();
896 }
897
898 void ChromeLauncherController::CreateNewWindow() {
899   // Use the currently active user.
900   chrome::NewEmptyWindow(profile_, chrome::HOST_DESKTOP_TYPE_ASH);
901 }
902
903 void ChromeLauncherController::CreateNewIncognitoWindow() {
904   // Use the currently active user.
905   chrome::NewEmptyWindow(profile_->GetOffTheRecordProfile(),
906                          chrome::HOST_DESKTOP_TYPE_ASH);
907 }
908
909 void ChromeLauncherController::PersistPinnedState() {
910   if (ignore_persist_pinned_state_change_)
911     return;
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.
915   if (!CanPin()) {
916     NOTREACHED() << "Can't pin but pinned state being updated";
917     return;
918   }
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
921   // listener.
922   pref_change_registrar_.Remove(prefs::kPinnedLauncherApps);
923   {
924     ListPrefUpdate updater(profile_->GetPrefs(), prefs::kPinnedLauncherApps);
925     updater->Clear();
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());
932           if (app_value)
933             updater->Append(app_value);
934         }
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);
940         if (app_value)
941           updater->Append(app_value);
942       }
943     }
944   }
945   pref_change_registrar_.Add(
946       prefs::kPinnedLauncherApps,
947       base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref,
948                  base::Unretained(this)));
949 }
950
951 ash::ShelfModel* ChromeLauncherController::model() {
952   return model_;
953 }
954
955 Profile* ChromeLauncherController::profile() {
956   return profile_;
957 }
958
959 ash::ShelfAutoHideBehavior ChromeLauncherController::GetShelfAutoHideBehavior(
960     aura::Window* root_window) const {
961   return GetShelfAutoHideBehaviorFromPrefs(profile_, root_window);
962 }
963
964 bool ChromeLauncherController::CanUserModifyShelfAutoHideBehavior(
965     aura::Window* root_window) const {
966   return profile_->GetPrefs()->
967       FindPreference(prefs::kShelfAutoHideBehaviorLocal)->IsUserModifiable();
968 }
969
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);
977   return;
978 }
979
980 void ChromeLauncherController::UpdateAppState(content::WebContents* contents,
981                                               AppState app_state) {
982   std::string app_id = app_tab_helper_->GetAppID(contents);
983
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;
988
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);
995       if (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));
1000       }
1001     }
1002   }
1003
1004   if (app_state == APP_STATE_REMOVED)
1005     web_contents_to_app_id_.erase(contents);
1006   else
1007     web_contents_to_app_id_[contents] = app_id;
1008
1009   ash::ShelfID id = GetShelfIDForAppID(app_id);
1010   if (id) {
1011     SetItemStatus(id, (app_state == APP_STATE_WINDOW_ACTIVE ||
1012                        app_state == APP_STATE_ACTIVE) ? ash::STATUS_ACTIVE :
1013                                                         GetAppState(app_id));
1014   }
1015 }
1016
1017 ash::ShelfID ChromeLauncherController::GetShelfIDForWebContents(
1018     content::WebContents* contents) {
1019   DCHECK(contents);
1020
1021   std::string app_id = app_tab_helper_->GetAppID(contents);
1022
1023   if (app_id.empty() && ContentCanBeHandledByGmailApp(contents))
1024     app_id = kGmailAppId;
1025
1026   ash::ShelfID id = GetShelfIDForAppID(app_id);
1027
1028   if (app_id.empty() || !id) {
1029     int browser_index = model_->GetItemIndexForType(ash::TYPE_BROWSER_SHORTCUT);
1030     return model_->items()[browser_index].id;
1031   }
1032
1033   return id;
1034 }
1035
1036 void ChromeLauncherController::SetRefocusURLPatternForTest(ash::ShelfID id,
1037                                                            const GURL& url) {
1038   DCHECK(HasItemController(id));
1039   LauncherItemController* controller = id_to_item_controller_map_[id];
1040
1041   int index = model_->ItemIndexByID(id);
1042   if (index == -1) {
1043     NOTREACHED() << "Invalid launcher id";
1044     return;
1045   }
1046
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);
1052   } else {
1053     NOTREACHED() << "Invalid launcher type";
1054   }
1055 }
1056
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);
1061 }
1062
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
1067   // current user.
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);
1079       window->Activate();
1080       return;
1081     }
1082   }
1083
1084   if (window->IsActive() && allow_minimize) {
1085     if (CommandLine::ForCurrentProcess()->HasSwitch(
1086             switches::kDisableMinimizeOnSecondLauncherItemClick)) {
1087       AnimateWindow(window->GetNativeWindow(),
1088                     wm::WINDOW_ANIMATION_TYPE_BOUNCE);
1089     } else {
1090       window->Minimize();
1091     }
1092   } else {
1093     window->Show();
1094     window->Activate();
1095   }
1096 }
1097
1098 void ChromeLauncherController::OnShelfCreated(ash::Shelf* shelf) {
1099   shelves_.insert(shelf);
1100   shelf->shelf_widget()->shelf_layout_manager()->AddObserver(this);
1101 }
1102
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.
1107 }
1108
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();
1115 }
1116
1117 void ChromeLauncherController::ShelfItemRemoved(int index, ash::ShelfID id) {
1118 }
1119
1120 void ChromeLauncherController::ShelfItemMoved(int start_index,
1121                                               int target_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();
1128 }
1129
1130 void ChromeLauncherController::ShelfItemChanged(
1131     int index,
1132     const ash::ShelfItem& old_item) {
1133 }
1134
1135 void ChromeLauncherController::ShelfStatusChanged() {
1136 }
1137
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.
1144   ReleaseProfile();
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
1161   // user.
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();
1168 }
1169
1170 void ChromeLauncherController::AdditionalUserAddedToSession(Profile* profile) {
1171   // Switch the running applications to the new user.
1172   app_window_controller_->AdditionalUserAddedToSession(profile);
1173 }
1174
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());
1182   }
1183
1184   UpdateAppLaunchersFromPref();
1185 }
1186
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);
1193
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);
1199   }
1200
1201   if (IsAppPinned(id)) {
1202     if (reason == UnloadedExtensionInfo::REASON_UNINSTALL) {
1203       if (profile == profile_) {
1204         DoUnpinAppWithID(id);
1205       }
1206       app_icon_loader_->ClearImage(id);
1207     } else {
1208       app_icon_loader_->UpdateImage(id);
1209     }
1210   }
1211 }
1212
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;
1219       break;
1220     case ash::SHELF_ALIGNMENT_LEFT:
1221       pref_value = ash::kShelfAlignmentLeft;
1222       break;
1223     case ash::SHELF_ALIGNMENT_RIGHT:
1224       pref_value = ash::kShelfAlignmentRight;
1225       break;
1226     case ash::SHELF_ALIGNMENT_TOP:
1227       pref_value = ash::kShelfAlignmentTop;
1228   }
1229
1230   UpdatePerDisplayPref(
1231       profile_->GetPrefs(), root_window, prefs::kShelfAlignment, pref_value);
1232
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);
1237   }
1238 }
1239
1240 void ChromeLauncherController::OnDisplayConfigurationChanged() {
1241   SetShelfBehaviorsFromPrefs();
1242 }
1243
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);
1252 }
1253
1254 void ChromeLauncherController::OnAppSyncUIStatusChanged() {
1255   if (app_sync_ui_state_->status() == AppSyncUIState::STATUS_SYNCING)
1256     model_->SetStatus(ash::ShelfModel::STATUS_LOADING);
1257   else
1258     model_->SetStatus(ash::ShelfModel::STATUS_NORMAL);
1259 }
1260
1261 void ChromeLauncherController::ExtensionEnableFlowFinished() {
1262   LaunchApp(extension_enable_flow_->extension_id(),
1263             ash::LAUNCH_FROM_UNKNOWN,
1264             ui::EF_NONE);
1265   extension_enable_flow_.reset();
1266 }
1267
1268 void ChromeLauncherController::ExtensionEnableFlowAborted(bool user_initiated) {
1269   extension_enable_flow_.reset();
1270 }
1271
1272 ChromeLauncherAppMenuItems ChromeLauncherController::GetApplicationList(
1273     const ash::ShelfItem& item,
1274     int event_flags) {
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();
1280
1281   return id_to_item_controller_map_[item.id]->GetApplicationList(event_flags);
1282 }
1283
1284 std::vector<content::WebContents*>
1285 ChromeLauncherController::GetV1ApplicationsFromAppId(std::string app_id) {
1286   ash::ShelfID id = GetShelfIDForAppID(app_id);
1287
1288   // If there is no such an item pinned to the launcher, no menu gets created.
1289   if (id) {
1290     LauncherItemController* controller = id_to_item_controller_map_[id];
1291     DCHECK(controller);
1292     if (controller->type() == LauncherItemController::TYPE_SHORTCUT)
1293       return GetV1ApplicationsFromController(controller);
1294   }
1295   return std::vector<content::WebContents*>();
1296 }
1297
1298 void ChromeLauncherController::ActivateShellApp(const std::string& app_id,
1299                                                 int index) {
1300   ash::ShelfID id = GetShelfIDForAppID(app_id);
1301   if (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);
1307     }
1308   }
1309 }
1310
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))
1317     return true;
1318   return (app_id == kGmailAppId && ContentCanBeHandledByGmailApp(web_contents));
1319 }
1320
1321 bool ChromeLauncherController::ContentCanBeHandledByGmailApp(
1322     content::WebContents* web_contents) {
1323   ash::ShelfID id = GetShelfIDForAppID(kGmailAppId);
1324   if (id) {
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))
1333       return true;
1334   }
1335   return false;
1336 }
1337
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);
1348   return result;
1349 }
1350
1351 base::string16 ChromeLauncherController::GetAppListTitle(
1352     content::WebContents* web_contents) const {
1353   base::string16 title = web_contents->GetTitle();
1354   if (!title.empty())
1355     return title;
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);
1361     if (extension)
1362       return base::UTF8ToUTF16(extension->name());
1363   }
1364   return l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE);
1365 }
1366
1367 ash::ShelfID ChromeLauncherController::CreateAppShortcutLauncherItem(
1368     const std::string& app_id,
1369     int index) {
1370   return CreateAppShortcutLauncherItemWithType(app_id,
1371                                                index,
1372                                                ash::TYPE_APP_SHORTCUT);
1373 }
1374
1375 void ChromeLauncherController::SetAppTabHelperForTest(AppTabHelper* helper) {
1376   app_tab_helper_.reset(helper);
1377 }
1378
1379 void ChromeLauncherController::SetAppIconLoaderForTest(
1380     extensions::AppIconLoader* loader) {
1381   app_icon_loader_.reset(loader);
1382 }
1383
1384 const std::string& ChromeLauncherController::GetAppIdFromShelfIdForTest(
1385     ash::ShelfID id) {
1386   return id_to_item_controller_map_[id]->app_id();
1387 }
1388
1389 void ChromeLauncherController::SetShelfItemDelegateManagerForTest(
1390     ash::ShelfItemDelegateManager* manager) {
1391   item_delegate_manager_ = manager;
1392 }
1393
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));
1400   }
1401   last_used_running_application_order_[
1402       multi_user_util::GetUserIDFromProfile(profile_)] = list;
1403 }
1404
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())
1410     return;
1411
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);
1417     if (shelf_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);
1424         running_index++;
1425       }
1426     }
1427   }
1428 }
1429
1430 ash::ShelfID ChromeLauncherController::CreateAppShortcutLauncherItemWithType(
1431     const std::string& app_id,
1432     int index,
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);
1438   return shelf_id;
1439 }
1440
1441 LauncherItemController* ChromeLauncherController::GetLauncherItemController(
1442     const ash::ShelfID id) {
1443   if (!HasItemController(id))
1444     return NULL;
1445   return id_to_item_controller_map_[id];
1446 }
1447
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)
1453     return true;
1454   return multi_user_util::IsProfileFromActiveUser(browser->profile());
1455 }
1456
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_);
1462
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);
1470
1471   return currently_shown != other_shown ||
1472          GetShelfAlignmentFromPrefs(profile_, root_window) !=
1473              GetShelfAlignmentFromPrefs(other_profile, root_window);
1474 }
1475
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.
1485   if (index != -1)
1486     model_->RemoveItemAt(index);
1487 }
1488
1489 void ChromeLauncherController::DoPinAppWithID(const std::string& app_id) {
1490   // If there is an item, do nothing and return.
1491   if (IsAppPinned(app_id))
1492     return;
1493
1494   ash::ShelfID shelf_id = GetShelfIDForAppID(app_id);
1495   if (shelf_id) {
1496     // App item exists, pin it
1497     Pin(shelf_id);
1498   } else {
1499     // Otherwise, create a shortcut item for it.
1500     CreateAppShortcutLauncherItem(app_id, model_->item_count());
1501     if (CanPin())
1502       PersistPinnedState();
1503   }
1504 }
1505
1506 void ChromeLauncherController::DoUnpinAppWithID(const std::string& app_id) {
1507   ash::ShelfID shelf_id = GetShelfIDForAppID(app_id);
1508   if (shelf_id && IsPinned(shelf_id))
1509     Unpin(shelf_id);
1510 }
1511
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)
1524     --index;
1525   if (running_index != index)
1526     model_->Move(running_index, index);
1527   return index;
1528 }
1529
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);
1542 }
1543
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();
1554
1555   int index = 0;
1556   int max_index = model_->item_count();
1557
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;
1562
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)
1577           continue;
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);
1587           ++pref_app_id;
1588           break;
1589         } else {
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.
1593             if (is_chrome)
1594               chrome_index = index;
1595             else
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)
1601               break;
1602             ++index;
1603           } else {
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);
1613             } else {
1614               LauncherItemClosed(item.id);
1615               --max_index;
1616             }
1617           }
1618           --index;
1619         }
1620       }
1621       // If the item wasn't found, that means id_to_item_controller_map_
1622       // is out of sync.
1623       DCHECK(index <= max_index);
1624     } else {
1625       // Check if the item was already running but not yet pinned.
1626       ash::ShelfID shelf_id = GetShelfIDForAppID(*pref_app_id);
1627       if (shelf_id) {
1628         // This app is running but not yet pinned. So pin and move it.
1629         index = PinRunningAppInternal(index, shelf_id);
1630       } else {
1631         // This app wasn't pinned before, insert a new entry.
1632         shelf_id = CreateAppShortcutLauncherItem(*pref_app_id, index);
1633         ++max_index;
1634         index = model_->ItemIndexByID(shelf_id);
1635       }
1636       ++pref_app_id;
1637     }
1638   }
1639
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);
1648       else
1649         LauncherItemClosed(item.id);
1650     } else {
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;
1655       ++index;
1656     }
1657   }
1658
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);
1675
1676       // Needed for the old layout - the weight might force it to be lower in
1677       // rank.
1678       if (app_list_index != -1 && target_index <= app_list_index)
1679         ++app_list_index;
1680     } else {
1681       int target_index = FindInsertionPoint(is_app_list);
1682       MoveChromeOrApplistToFinalPosition(
1683           is_chrome, is_app_list, target_index, &chrome_index, &app_list_index);
1684     }
1685   }
1686 }
1687
1688 void ChromeLauncherController::SetShelfAutoHideBehaviorPrefs(
1689     ash::ShelfAutoHideBehavior behavior,
1690     aura::Window* root_window) {
1691   const char* value = NULL;
1692   switch (behavior) {
1693     case ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS:
1694       value = ash::kShelfAutoHideBehaviorAlways;
1695       break;
1696     case ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER:
1697       value = ash::kShelfAutoHideBehaviorNever;
1698       break;
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).
1703       return;
1704   }
1705
1706   UpdatePerDisplayPref(
1707       profile_->GetPrefs(), root_window, prefs::kShelfAutoHideBehavior, value);
1708
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);
1713   }
1714 }
1715
1716 void ChromeLauncherController::SetShelfAutoHideBehaviorFromPrefs() {
1717   aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
1718
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);
1723   }
1724 }
1725
1726 void ChromeLauncherController::SetShelfAlignmentFromPrefs() {
1727   if (!ash::ShelfWidget::ShelfAlignmentAllowed())
1728     return;
1729
1730   aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
1731
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);
1736   }
1737 }
1738
1739 void ChromeLauncherController::SetShelfBehaviorsFromPrefs() {
1740   SetShelfAutoHideBehaviorFromPrefs();
1741   SetShelfAlignmentFromPrefs();
1742 }
1743
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);
1750   } else {
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);
1756   }
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();
1762 }
1763 #endif  // defined(OS_CHROMEOS)
1764
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();
1770        ++it) {
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.
1776       if (!browser)
1777         continue;
1778       if (browser->window()->IsActive()) {
1779         return browser->tab_strip_model()->GetActiveWebContents() == it->first ?
1780             ash::STATUS_ACTIVE : ash::STATUS_RUNNING;
1781       } else {
1782         status = ash::STATUS_RUNNING;
1783       }
1784     }
1785   }
1786   return status;
1787 }
1788
1789 ash::ShelfID ChromeLauncherController::InsertAppLauncherItem(
1790     LauncherItemController* controller,
1791     const std::string& app_id,
1792     ash::ShelfItemStatus status,
1793     int index,
1794     ash::ShelfItemType shelf_item_type) {
1795   ash::ShelfID id = model_->next_id();
1796   CHECK(!HasItemController(id));
1797   CHECK(controller);
1798   id_to_item_controller_map_[id] = controller;
1799   controller->set_shelf_id(id);
1800
1801   ash::ShelfItem item;
1802   item.type = shelf_item_type;
1803   item.image = extensions::util::GetDefaultAppIcon();
1804
1805   ash::ShelfItemStatus new_state = GetAppState(app_id);
1806   if (new_state != ash::STATUS_CLOSED)
1807     status = new_state;
1808
1809   item.status = status;
1810
1811   model_->AddAt(index, item);
1812
1813   app_icon_loader_->FetchImage(app_id);
1814
1815   SetShelfItemDelegate(id, controller);
1816
1817   return id;
1818 }
1819
1820 bool ChromeLauncherController::HasItemController(ash::ShelfID id) const {
1821   return id_to_item_controller_map_.find(id) !=
1822          id_to_item_controller_map_.end();
1823 }
1824
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();
1832 }
1833
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);
1842   }
1843   // Create a LauncherItemController for the Browser shortcut if it does not
1844   // exist yet.
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]);
1849 }
1850
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]);
1864   return id;
1865 }
1866
1867 void ChromeLauncherController::PersistChromeItemIndex(int index) {
1868   profile_->GetPrefs()->SetInteger(prefs::kShelfChromeIconIndex, index);
1869 }
1870
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));
1877 }
1878
1879 void ChromeLauncherController::MoveChromeOrApplistToFinalPosition(
1880     bool is_chrome,
1881     bool is_app_list,
1882     int target_index,
1883     int* chrome_index,
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);
1891     *chrome_index = -1;
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)
1897       --(*chrome_index);
1898     *app_list_index = -1;
1899   }
1900 }
1901
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.
1907   if (is_app_list)
1908     return 0;
1909
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) {
1915       return i;
1916     }
1917   }
1918   return 0;
1919 }
1920
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();
1925
1926   std::vector<std::string>::iterator it =
1927       std::find(pinned_apps.begin(),
1928                 pinned_apps.end(),
1929                 std::string(extension_misc::kChromeAppId));
1930   DCHECK(it != pinned_apps.end());
1931   int index = it - pinned_apps.begin();
1932
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);
1939 }
1940
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));
1947
1948   std::vector<std::string> pinned_apps;
1949
1950   // Get the new incarnation of the list.
1951   const base::ListValue* pinned_apps_pref =
1952       profile_->GetPrefs()->GetList(prefs::kPinnedLauncherApps);
1953
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();
1958
1959   // See if the chrome string is already in the pinned list and remove it if
1960   // needed.
1961   base::Value* chrome_app = ash::CreateAppDict(extension_misc::kChromeAppId);
1962   if (chrome_app) {
1963     chrome_icon_added = pinned_apps_pref->Find(*chrome_app) !=
1964         pinned_apps_pref->end();
1965     delete chrome_app;
1966   }
1967
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;
1974     }
1975     const base::DictionaryValue* app = NULL;
1976     std::string app_id;
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);
1992       }
1993     }
1994   }
1995
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);
1999
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);
2004   }
2005   return pinned_apps;
2006 }
2007
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();
2013 }
2014
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() &&
2029         app_id ==
2030             web_app::GetExtensionIdFromApplicationName(browser->app_name()) &&
2031         profile == browser->profile()) {
2032       browser_to_close.push_back(browser);
2033     }
2034   }
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();
2039   }
2040 }
2041
2042 void ChromeLauncherController::SetShelfItemDelegate(
2043     ash::ShelfID id,
2044     ash::ShelfItemDelegate* item_delegate) {
2045   DCHECK_GT(id, 0);
2046   DCHECK(item_delegate);
2047   DCHECK(item_delegate_manager_);
2048   item_delegate_manager_->SetShelfItemDelegate(
2049       id, scoped_ptr<ash::ShelfItemDelegate>(item_delegate).Pass());
2050 }
2051
2052 void ChromeLauncherController::AttachProfile(Profile* profile) {
2053   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_));
2058   else
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));
2067
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)
2092
2093   extensions::ExtensionRegistry::Get(profile_)->AddObserver(this);
2094 }
2095
2096 void ChromeLauncherController::ReleaseProfile() {
2097   if (app_sync_ui_state_)
2098     app_sync_ui_state_->RemoveObserver(this);
2099
2100   extensions::ExtensionRegistry::Get(profile_)->RemoveObserver(this);
2101
2102   PrefServiceSyncable::FromProfile(profile_)->RemoveObserver(this);
2103
2104   pref_change_registrar_.RemoveAll();
2105 }