Home | History | Annotate | Download | only in launcher
      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/utf_string_conversions.h"
     25 #include "base/values.h"
     26 #include "chrome/browser/app_mode/app_mode_utils.h"
     27 #include "chrome/browser/chrome_notification_types.h"
     28 #include "chrome/browser/defaults.h"
     29 #include "chrome/browser/extensions/app_icon_loader_impl.h"
     30 #include "chrome/browser/extensions/extension_service.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 "content/public/browser/navigation_entry.h"
     69 #include "content/public/browser/notification_registrar.h"
     70 #include "content/public/browser/notification_service.h"
     71 #include "content/public/browser/web_contents.h"
     72 #include "extensions/browser/extension_prefs.h"
     73 #include "extensions/browser/extension_system.h"
     74 #include "extensions/browser/extension_util.h"
     75 #include "extensions/common/extension.h"
     76 #include "extensions/common/extension_resource.h"
     77 #include "extensions/common/manifest_handlers/icons_handler.h"
     78 #include "extensions/common/url_pattern.h"
     79 #include "grit/ash_resources.h"
     80 #include "grit/chromium_strings.h"
     81 #include "grit/generated_resources.h"
     82 #include "grit/theme_resources.h"
     83 #include "grit/ui_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/wm/core/window_animations.h"
     90 
     91 #if defined(OS_CHROMEOS)
     92 #include "chrome/browser/browser_process.h"
     93 #include "chrome/browser/chromeos/login/users/user_manager.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 #endif
     98 
     99 using extensions::Extension;
    100 using extensions::UnloadedExtensionInfo;
    101 using extension_misc::kGmailAppId;
    102 using content::WebContents;
    103 
    104 // static
    105 ChromeLauncherController* ChromeLauncherController::instance_ = NULL;
    106 
    107 namespace {
    108 
    109 // This will be used as placeholder in the list of the pinned applciatons.
    110 // Note that this is NOT a valid extension identifier so that pre M31 versions
    111 // will ignore it.
    112 const char kAppShelfIdPlaceholder[] = "AppShelfIDPlaceholder--------";
    113 
    114 std::string GetPrefKeyForRootWindow(aura::Window* root_window) {
    115   gfx::Display display = gfx::Screen::GetScreenFor(
    116       root_window)->GetDisplayNearestWindow(root_window);
    117   DCHECK(display.is_valid());
    118 
    119   return base::Int64ToString(display.id());
    120 }
    121 
    122 void UpdatePerDisplayPref(PrefService* pref_service,
    123                           aura::Window* root_window,
    124                           const char* pref_key,
    125                           const std::string& value) {
    126   std::string key = GetPrefKeyForRootWindow(root_window);
    127   if (key.empty())
    128     return;
    129 
    130   DictionaryPrefUpdate update(pref_service, prefs::kShelfPreferences);
    131   base::DictionaryValue* shelf_prefs = update.Get();
    132   base::DictionaryValue* prefs = NULL;
    133   if (!shelf_prefs->GetDictionary(key, &prefs)) {
    134     prefs = new base::DictionaryValue();
    135     shelf_prefs->Set(key, prefs);
    136   }
    137   prefs->SetStringWithoutPathExpansion(pref_key, value);
    138 }
    139 
    140 // Returns a pref value in |pref_service| for the display of |root_window|. The
    141 // pref value is stored in |local_path| and |path|, but |pref_service| may have
    142 // per-display preferences and the value can be specified by policy. Here is
    143 // the priority:
    144 //  * A value managed by policy. This is a single value that applies to all
    145 //    displays.
    146 //  * A user-set value for the specified display.
    147 //  * A user-set value in |local_path| or |path|, if no per-display settings are
    148 //    ever specified (see http://crbug.com/173719 for why). |local_path| is
    149 //    preferred. See comment in |kShelfAlignment| as to why we consider two
    150 //    prefs and why |local_path| is preferred.
    151 //  * A value recommended by policy. This is a single value that applies to all
    152 //    root windows.
    153 //  * The default value for |local_path| if the value is not recommended by
    154 //    policy.
    155 std::string GetPrefForRootWindow(PrefService* pref_service,
    156                                  aura::Window* root_window,
    157                                  const char* local_path,
    158                                  const char* path) {
    159   const PrefService::Preference* local_pref =
    160       pref_service->FindPreference(local_path);
    161   const std::string value(pref_service->GetString(local_path));
    162   if (local_pref->IsManaged())
    163     return value;
    164 
    165   std::string pref_key = GetPrefKeyForRootWindow(root_window);
    166   bool has_per_display_prefs = false;
    167   if (!pref_key.empty()) {
    168     const base::DictionaryValue* shelf_prefs = pref_service->GetDictionary(
    169         prefs::kShelfPreferences);
    170     const base::DictionaryValue* display_pref = NULL;
    171     std::string per_display_value;
    172     if (shelf_prefs->GetDictionary(pref_key, &display_pref) &&
    173         display_pref->GetString(path, &per_display_value))
    174       return per_display_value;
    175 
    176     // If the pref for the specified display is not found, scan the whole prefs
    177     // and check if the prefs for other display is already specified.
    178     std::string unused_value;
    179     for (base::DictionaryValue::Iterator iter(*shelf_prefs);
    180          !iter.IsAtEnd(); iter.Advance()) {
    181       const base::DictionaryValue* display_pref = NULL;
    182       if (iter.value().GetAsDictionary(&display_pref) &&
    183           display_pref->GetString(path, &unused_value)) {
    184         has_per_display_prefs = true;
    185         break;
    186       }
    187     }
    188   }
    189 
    190   if (local_pref->IsRecommended() || !has_per_display_prefs)
    191     return value;
    192 
    193   const base::Value* default_value =
    194       pref_service->GetDefaultPrefValue(local_path);
    195   std::string default_string;
    196   default_value->GetAsString(&default_string);
    197   return default_string;
    198 }
    199 
    200 // If prefs have synced and no user-set value exists at |local_path|, the value
    201 // from |synced_path| is copied to |local_path|.
    202 void MaybePropagatePrefToLocal(PrefServiceSyncable* pref_service,
    203                                const char* local_path,
    204                                const char* synced_path) {
    205   if (!pref_service->FindPreference(local_path)->HasUserSetting() &&
    206       pref_service->IsSyncing()) {
    207     // First time the user is using this machine, propagate from remote to
    208     // local.
    209     pref_service->SetString(local_path, pref_service->GetString(synced_path));
    210   }
    211 }
    212 
    213 std::string GetSourceFromAppListSource(ash::LaunchSource source) {
    214   switch (source) {
    215     case ash::LAUNCH_FROM_APP_LIST:
    216       return std::string(extension_urls::kLaunchSourceAppList);
    217     case ash::LAUNCH_FROM_APP_LIST_SEARCH:
    218       return std::string(extension_urls::kLaunchSourceAppListSearch);
    219     default: return std::string();
    220   }
    221 }
    222 
    223 }  // namespace
    224 
    225 #if defined(OS_CHROMEOS)
    226 // A class to get events from ChromeOS when a user gets changed or added.
    227 class ChromeLauncherControllerUserSwitchObserverChromeOS
    228     : public ChromeLauncherControllerUserSwitchObserver,
    229       public chromeos::UserManager::UserSessionStateObserver,
    230       content::NotificationObserver {
    231  public:
    232   ChromeLauncherControllerUserSwitchObserverChromeOS(
    233       ChromeLauncherController* controller)
    234       : controller_(controller) {
    235     DCHECK(chromeos::UserManager::IsInitialized());
    236     chromeos::UserManager::Get()->AddSessionStateObserver(this);
    237     // A UserAddedToSession notification can be sent before a profile is loaded.
    238     // Since our observers require that we have already a profile, we might have
    239     // to postpone the notification until the ProfileManager lets us know that
    240     // the profile for that newly added user was added to the ProfileManager.
    241     registrar_.Add(this, chrome::NOTIFICATION_PROFILE_ADDED,
    242                    content::NotificationService::AllSources());
    243   }
    244   virtual ~ChromeLauncherControllerUserSwitchObserverChromeOS() {
    245     chromeos::UserManager::Get()->RemoveSessionStateObserver(this);
    246   }
    247 
    248   // chromeos::UserManager::UserSessionStateObserver overrides:
    249   virtual void UserAddedToSession(const chromeos::User* added_user) OVERRIDE;
    250 
    251   // content::NotificationObserver overrides:
    252   virtual void Observe(int type,
    253                const content::NotificationSource& source,
    254                const content::NotificationDetails& details) OVERRIDE;
    255 
    256  private:
    257   // Add a user to the session.
    258   void AddUser(Profile* profile);
    259 
    260   // The owning ChromeLauncherController.
    261   ChromeLauncherController* controller_;
    262 
    263   // The notification registrar to track the Profile creations after a user got
    264   // added to the session (if required).
    265   content::NotificationRegistrar registrar_;
    266 
    267   // Users which were just added to the system, but which profiles were not yet
    268   // (fully) loaded.
    269   std::set<std::string> added_user_ids_waiting_for_profiles_;
    270 
    271   DISALLOW_COPY_AND_ASSIGN(ChromeLauncherControllerUserSwitchObserverChromeOS);
    272 };
    273 
    274 void ChromeLauncherControllerUserSwitchObserverChromeOS::UserAddedToSession(
    275     const chromeos::User* active_user) {
    276   Profile* profile = multi_user_util::GetProfileFromUserID(
    277       active_user->email());
    278   // If we do not have a profile yet, we postpone forwarding the notification
    279   // until it is loaded.
    280   if (!profile)
    281     added_user_ids_waiting_for_profiles_.insert(active_user->email());
    282   else
    283     AddUser(profile);
    284 }
    285 
    286 void ChromeLauncherControllerUserSwitchObserverChromeOS::Observe(
    287     int type,
    288     const content::NotificationSource& source,
    289     const content::NotificationDetails& details) {
    290   if (type == chrome::NOTIFICATION_PROFILE_ADDED &&
    291       !added_user_ids_waiting_for_profiles_.empty()) {
    292     // Check if the profile is from a user which was on the waiting list.
    293     Profile* profile = content::Source<Profile>(source).ptr();
    294     std::string user_id = multi_user_util::GetUserIDFromProfile(profile);
    295     std::set<std::string>::iterator it = std::find(
    296         added_user_ids_waiting_for_profiles_.begin(),
    297         added_user_ids_waiting_for_profiles_.end(),
    298         user_id);
    299     if (it != added_user_ids_waiting_for_profiles_.end()) {
    300       added_user_ids_waiting_for_profiles_.erase(it);
    301       AddUser(profile->GetOriginalProfile());
    302     }
    303   }
    304 }
    305 
    306 void ChromeLauncherControllerUserSwitchObserverChromeOS::AddUser(
    307     Profile* profile) {
    308   if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
    309           chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED)
    310     chrome::MultiUserWindowManager::GetInstance()->AddUser(profile);
    311   controller_->AdditionalUserAddedToSession(profile->GetOriginalProfile());
    312 }
    313 #endif
    314 
    315 ChromeLauncherController::ChromeLauncherController(Profile* profile,
    316                                                    ash::ShelfModel* model)
    317     : model_(model),
    318       item_delegate_manager_(NULL),
    319       profile_(profile),
    320       app_sync_ui_state_(NULL),
    321       ignore_persist_pinned_state_change_(false) {
    322   if (!profile_) {
    323     // If no profile was passed, we take the currently active profile and use it
    324     // as the owner of the current desktop.
    325     // Use the original profile as on chromeos we may get a temporary off the
    326     // record profile, unless in guest session (where off the record profile is
    327     // the right one).
    328     Profile* active_profile = ProfileManager::GetActiveUserProfile();
    329     profile_ = active_profile->IsGuestSession() ? active_profile :
    330         active_profile->GetOriginalProfile();
    331 
    332     app_sync_ui_state_ = AppSyncUIState::Get(profile_);
    333     if (app_sync_ui_state_)
    334       app_sync_ui_state_->AddObserver(this);
    335   }
    336 
    337   // All profile relevant settings get bound to the current profile.
    338   AttachProfile(profile_);
    339   model_->AddObserver(this);
    340 
    341   // In multi profile mode we might have a window manager. We try to create it
    342   // here. If the instantiation fails, the manager is not needed.
    343   chrome::MultiUserWindowManager::CreateInstance();
    344 
    345 #if defined(OS_CHROMEOS)
    346   // On Chrome OS using multi profile we want to switch the content of the shelf
    347   // with a user change. Note that for unit tests the instance can be NULL.
    348   if (chrome::MultiUserWindowManager::GetMultiProfileMode() !=
    349           chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_OFF) {
    350     user_switch_observer_.reset(
    351         new ChromeLauncherControllerUserSwitchObserverChromeOS(this));
    352   }
    353 
    354   // Create our v1/v2 application / browser monitors which will inform the
    355   // launcher of status changes.
    356   if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
    357           chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED) {
    358     // If running in separated destkop mode, we create the multi profile version
    359     // of status monitor.
    360     browser_status_monitor_.reset(new MultiProfileBrowserStatusMonitor(this));
    361     app_window_controller_.reset(
    362         new MultiProfileAppWindowLauncherController(this));
    363   } else {
    364     // Create our v1/v2 application / browser monitors which will inform the
    365     // launcher of status changes.
    366     browser_status_monitor_.reset(new BrowserStatusMonitor(this));
    367     app_window_controller_.reset(new AppWindowLauncherController(this));
    368   }
    369 #else
    370   // Create our v1/v2 application / browser monitors which will inform the
    371   // launcher of status changes.
    372   browser_status_monitor_.reset(new BrowserStatusMonitor(this));
    373   app_window_controller_.reset(new AppWindowLauncherController(this));
    374 #endif
    375 
    376   // Right now ash::Shell isn't created for tests.
    377   // TODO(mukai): Allows it to observe display change and write tests.
    378   if (ash::Shell::HasInstance()) {
    379     ash::Shell::GetInstance()->display_controller()->AddObserver(this);
    380     item_delegate_manager_ =
    381         ash::Shell::GetInstance()->shelf_item_delegate_manager();
    382   }
    383 
    384   notification_registrar_.Add(this,
    385                               chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED,
    386                               content::Source<Profile>(profile_));
    387   notification_registrar_.Add(
    388       this,
    389       chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
    390       content::Source<Profile>(profile_));
    391 }
    392 
    393 ChromeLauncherController::~ChromeLauncherController() {
    394   // Reset the BrowserStatusMonitor as it has a weak pointer to this.
    395   browser_status_monitor_.reset();
    396 
    397   // Reset the app window controller here since it has a weak pointer to this.
    398   app_window_controller_.reset();
    399 
    400   for (std::set<ash::Shelf*>::iterator iter = shelves_.begin();
    401        iter != shelves_.end();
    402        ++iter)
    403     (*iter)->shelf_widget()->shelf_layout_manager()->RemoveObserver(this);
    404 
    405   model_->RemoveObserver(this);
    406   if (ash::Shell::HasInstance())
    407     ash::Shell::GetInstance()->display_controller()->RemoveObserver(this);
    408   for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin();
    409        i != id_to_item_controller_map_.end(); ++i) {
    410     int index = model_->ItemIndexByID(i->first);
    411     // A "browser proxy" is not known to the model and this removal does
    412     // therefore not need to be propagated to the model.
    413     if (index != -1 &&
    414         model_->items()[index].type != ash::TYPE_BROWSER_SHORTCUT)
    415       model_->RemoveItemAt(index);
    416   }
    417 
    418   if (ash::Shell::HasInstance())
    419     ash::Shell::GetInstance()->RemoveShellObserver(this);
    420 
    421   // Release all profile dependent resources.
    422   ReleaseProfile();
    423   if (instance_ == this)
    424     instance_ = NULL;
    425 
    426   // Get rid of the multi user window manager instance.
    427   chrome::MultiUserWindowManager::DeleteInstance();
    428 }
    429 
    430 // static
    431 ChromeLauncherController* ChromeLauncherController::CreateInstance(
    432     Profile* profile,
    433     ash::ShelfModel* model) {
    434   // We do not check here for re-creation of the ChromeLauncherController since
    435   // it appears that it might be intentional that the ChromeLauncherController
    436   // can be re-created.
    437   instance_ = new ChromeLauncherController(profile, model);
    438   return instance_;
    439 }
    440 
    441 void ChromeLauncherController::Init() {
    442   CreateBrowserShortcutLauncherItem();
    443   UpdateAppLaunchersFromPref();
    444 
    445   // TODO(sky): update unit test so that this test isn't necessary.
    446   if (ash::Shell::HasInstance()) {
    447     SetShelfAutoHideBehaviorFromPrefs();
    448     SetShelfAlignmentFromPrefs();
    449 #if defined(OS_CHROMEOS)
    450     SetVirtualKeyboardBehaviorFromPrefs();
    451 #endif  // defined(OS_CHROMEOS)
    452     PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_);
    453     if (!prefs->FindPreference(prefs::kShelfAlignmentLocal)->HasUserSetting() ||
    454         !prefs->FindPreference(prefs::kShelfAutoHideBehaviorLocal)->
    455             HasUserSetting()) {
    456       // This causes OnIsSyncingChanged to be called when the value of
    457       // PrefService::IsSyncing() changes.
    458       prefs->AddObserver(this);
    459     }
    460     ash::Shell::GetInstance()->AddShellObserver(this);
    461   }
    462 }
    463 
    464 ash::ShelfID ChromeLauncherController::CreateAppLauncherItem(
    465     LauncherItemController* controller,
    466     const std::string& app_id,
    467     ash::ShelfItemStatus status) {
    468   CHECK(controller);
    469   int index = 0;
    470   // Panels are inserted on the left so as not to push all existing panels over.
    471   if (controller->GetShelfItemType() != ash::TYPE_APP_PANEL)
    472     index = model_->item_count();
    473   return InsertAppLauncherItem(controller,
    474                                app_id,
    475                                status,
    476                                index,
    477                                controller->GetShelfItemType());
    478 }
    479 
    480 void ChromeLauncherController::SetItemStatus(ash::ShelfID id,
    481                                              ash::ShelfItemStatus status) {
    482   int index = model_->ItemIndexByID(id);
    483   ash::ShelfItemStatus old_status = model_->items()[index].status;
    484   // Since ordinary browser windows are not registered, we might get a negative
    485   // index here.
    486   if (index >= 0 && old_status != status) {
    487     ash::ShelfItem item = model_->items()[index];
    488     item.status = status;
    489     model_->Set(index, item);
    490   }
    491 }
    492 
    493 void ChromeLauncherController::SetItemController(
    494     ash::ShelfID id,
    495     LauncherItemController* controller) {
    496   CHECK(controller);
    497   IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
    498   CHECK(iter != id_to_item_controller_map_.end());
    499   controller->set_shelf_id(id);
    500   iter->second = controller;
    501   // Existing controller is destroyed and replaced by registering again.
    502   SetShelfItemDelegate(id, controller);
    503 }
    504 
    505 void ChromeLauncherController::CloseLauncherItem(ash::ShelfID id) {
    506   CHECK(id);
    507   if (IsPinned(id)) {
    508     // Create a new shortcut controller.
    509     IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
    510     CHECK(iter != id_to_item_controller_map_.end());
    511     SetItemStatus(id, ash::STATUS_CLOSED);
    512     std::string app_id = iter->second->app_id();
    513     iter->second = new AppShortcutLauncherItemController(app_id, this);
    514     iter->second->set_shelf_id(id);
    515     // Existing controller is destroyed and replaced by registering again.
    516     SetShelfItemDelegate(id, iter->second);
    517   } else {
    518     LauncherItemClosed(id);
    519   }
    520 }
    521 
    522 void ChromeLauncherController::Pin(ash::ShelfID id) {
    523   DCHECK(HasItemController(id));
    524 
    525   int index = model_->ItemIndexByID(id);
    526   DCHECK_GE(index, 0);
    527 
    528   ash::ShelfItem item = model_->items()[index];
    529 
    530   if (item.type == ash::TYPE_PLATFORM_APP ||
    531       item.type == ash::TYPE_WINDOWED_APP) {
    532     item.type = ash::TYPE_APP_SHORTCUT;
    533     model_->Set(index, item);
    534   } else if (item.type != ash::TYPE_APP_SHORTCUT) {
    535     return;
    536   }
    537 
    538   if (CanPin())
    539     PersistPinnedState();
    540 }
    541 
    542 void ChromeLauncherController::Unpin(ash::ShelfID id) {
    543   DCHECK(HasItemController(id));
    544 
    545   LauncherItemController* controller = id_to_item_controller_map_[id];
    546   if (controller->type() == LauncherItemController::TYPE_APP ||
    547       controller->locked()) {
    548     UnpinRunningAppInternal(model_->ItemIndexByID(id));
    549   } else {
    550     LauncherItemClosed(id);
    551   }
    552   if (CanPin())
    553     PersistPinnedState();
    554 }
    555 
    556 bool ChromeLauncherController::IsPinned(ash::ShelfID id) {
    557   int index = model_->ItemIndexByID(id);
    558   if (index < 0)
    559     return false;
    560   ash::ShelfItemType type = model_->items()[index].type;
    561   return (type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_BROWSER_SHORTCUT);
    562 }
    563 
    564 void ChromeLauncherController::TogglePinned(ash::ShelfID id) {
    565   if (!HasItemController(id))
    566     return;  // May happen if item closed with menu open.
    567 
    568   if (IsPinned(id))
    569     Unpin(id);
    570   else
    571     Pin(id);
    572 }
    573 
    574 bool ChromeLauncherController::IsPinnable(ash::ShelfID id) const {
    575   int index = model_->ItemIndexByID(id);
    576   if (index == -1)
    577     return false;
    578 
    579   ash::ShelfItemType type = model_->items()[index].type;
    580   return ((type == ash::TYPE_APP_SHORTCUT ||
    581            type == ash::TYPE_PLATFORM_APP ||
    582            type == ash::TYPE_WINDOWED_APP) &&
    583           CanPin());
    584 }
    585 
    586 void ChromeLauncherController::Install(ash::ShelfID id) {
    587   if (!HasItemController(id))
    588     return;
    589 
    590   std::string app_id = GetAppIDForShelfID(id);
    591   if (extensions::util::IsExtensionInstalledPermanently(app_id, profile_))
    592     return;
    593 
    594   LauncherItemController* controller = id_to_item_controller_map_[id];
    595   if (controller->type() == LauncherItemController::TYPE_APP) {
    596     AppWindowLauncherItemController* app_window_controller =
    597         static_cast<AppWindowLauncherItemController*>(controller);
    598     app_window_controller->InstallApp();
    599   }
    600 }
    601 
    602 bool ChromeLauncherController::CanInstall(ash::ShelfID id) {
    603   int index = model_->ItemIndexByID(id);
    604   if (index == -1)
    605     return false;
    606 
    607   ash::ShelfItemType type = model_->items()[index].type;
    608   if (type != ash::TYPE_PLATFORM_APP)
    609     return false;
    610 
    611   return extensions::util::IsEphemeralApp(GetAppIDForShelfID(id), profile_);
    612 }
    613 
    614 void ChromeLauncherController::LockV1AppWithID(
    615     const std::string& app_id) {
    616   ash::ShelfID id = GetShelfIDForAppID(app_id);
    617   if (!IsPinned(id) && !IsWindowedAppInLauncher(app_id)) {
    618     CreateAppShortcutLauncherItemWithType(app_id,
    619                                           model_->item_count(),
    620                                           ash::TYPE_WINDOWED_APP);
    621     id = GetShelfIDForAppID(app_id);
    622   }
    623   CHECK(id);
    624   id_to_item_controller_map_[id]->lock();
    625 }
    626 
    627 void ChromeLauncherController::UnlockV1AppWithID(const std::string& app_id) {
    628   ash::ShelfID id = GetShelfIDForAppID(app_id);
    629   CHECK(IsPinned(id) || IsWindowedAppInLauncher(app_id));
    630   CHECK(id);
    631   LauncherItemController* controller = id_to_item_controller_map_[id];
    632   controller->unlock();
    633   if (!controller->locked() && !IsPinned(id))
    634     CloseLauncherItem(id);
    635 }
    636 
    637 void ChromeLauncherController::Launch(ash::ShelfID id, int event_flags) {
    638   if (!HasItemController(id))
    639     return;  // In case invoked from menu and item closed while menu up.
    640   id_to_item_controller_map_[id]->Launch(ash::LAUNCH_FROM_UNKNOWN, event_flags);
    641 }
    642 
    643 void ChromeLauncherController::Close(ash::ShelfID id) {
    644   if (!HasItemController(id))
    645     return;  // May happen if menu closed.
    646   id_to_item_controller_map_[id]->Close();
    647 }
    648 
    649 bool ChromeLauncherController::IsOpen(ash::ShelfID id) {
    650   if (!HasItemController(id))
    651     return false;
    652   return id_to_item_controller_map_[id]->IsOpen();
    653 }
    654 
    655 bool ChromeLauncherController::IsPlatformApp(ash::ShelfID id) {
    656   if (!HasItemController(id))
    657     return false;
    658 
    659   std::string app_id = GetAppIDForShelfID(id);
    660   const Extension* extension = GetExtensionForAppID(app_id);
    661   // An extension can be synced / updated at any time and therefore not be
    662   // available.
    663   return extension ? extension->is_platform_app() : false;
    664 }
    665 
    666 void ChromeLauncherController::LaunchApp(const std::string& app_id,
    667                                          ash::LaunchSource source,
    668                                          int event_flags) {
    669   // |extension| could be NULL when it is being unloaded for updating.
    670   const Extension* extension = GetExtensionForAppID(app_id);
    671   if (!extension)
    672     return;
    673 
    674   if (!extensions::util::IsAppLaunchableWithoutEnabling(app_id, profile_)) {
    675     // Do nothing if there is already a running enable flow.
    676     if (extension_enable_flow_)
    677       return;
    678 
    679     extension_enable_flow_.reset(
    680         new ExtensionEnableFlow(profile_, app_id, this));
    681     extension_enable_flow_->StartForNativeWindow(NULL);
    682     return;
    683   }
    684 
    685 #if defined(OS_WIN)
    686   if (LaunchedInNativeDesktop(app_id))
    687     return;
    688 #endif
    689 
    690   // The app will be created for the currently active profile.
    691   AppLaunchParams params(profile_,
    692                          extension,
    693                          event_flags,
    694                          chrome::HOST_DESKTOP_TYPE_ASH);
    695   if (source != ash::LAUNCH_FROM_UNKNOWN &&
    696       app_id == extension_misc::kWebStoreAppId) {
    697     // Get the corresponding source string.
    698     std::string source_value = GetSourceFromAppListSource(source);
    699 
    700     // Set an override URL to include the source.
    701     GURL extension_url = extensions::AppLaunchInfo::GetFullLaunchURL(extension);
    702     params.override_url = net::AppendQueryParameter(
    703         extension_url, extension_urls::kWebstoreSourceField, source_value);
    704   }
    705 
    706   OpenApplication(params);
    707 }
    708 
    709 void ChromeLauncherController::ActivateApp(const std::string& app_id,
    710                                            ash::LaunchSource source,
    711                                            int event_flags) {
    712   // If there is an existing non-shortcut controller for this app, open it.
    713   ash::ShelfID id = GetShelfIDForAppID(app_id);
    714   if (id) {
    715     LauncherItemController* controller = id_to_item_controller_map_[id];
    716     controller->Activate(source);
    717     return;
    718   }
    719 
    720   // Create a temporary application launcher item and use it to see if there are
    721   // running instances.
    722   scoped_ptr<AppShortcutLauncherItemController> app_controller(
    723       new AppShortcutLauncherItemController(app_id, this));
    724   if (!app_controller->GetRunningApplications().empty())
    725     app_controller->Activate(source);
    726   else
    727     LaunchApp(app_id, source, event_flags);
    728 }
    729 
    730 extensions::LaunchType ChromeLauncherController::GetLaunchType(
    731     ash::ShelfID id) {
    732   DCHECK(HasItemController(id));
    733 
    734   const Extension* extension = GetExtensionForAppID(
    735       id_to_item_controller_map_[id]->app_id());
    736 
    737   // An extension can be unloaded/updated/unavailable at any time.
    738   if (!extension)
    739     return extensions::LAUNCH_TYPE_DEFAULT;
    740 
    741   return extensions::GetLaunchType(extensions::ExtensionPrefs::Get(profile_),
    742                                    extension);
    743 }
    744 
    745 ash::ShelfID ChromeLauncherController::GetShelfIDForAppID(
    746     const std::string& app_id) {
    747   for (IDToItemControllerMap::const_iterator i =
    748            id_to_item_controller_map_.begin();
    749        i != id_to_item_controller_map_.end(); ++i) {
    750     if (i->second->type() == LauncherItemController::TYPE_APP_PANEL)
    751       continue;  // Don't include panels
    752     if (i->second->app_id() == app_id)
    753       return i->first;
    754   }
    755   return 0;
    756 }
    757 
    758 const std::string& ChromeLauncherController::GetAppIDForShelfID(
    759     ash::ShelfID id) {
    760   CHECK(HasItemController(id));
    761   return id_to_item_controller_map_[id]->app_id();
    762 }
    763 
    764 void ChromeLauncherController::SetAppImage(const std::string& id,
    765                                            const gfx::ImageSkia& image) {
    766   // TODO: need to get this working for shortcuts.
    767   for (IDToItemControllerMap::const_iterator i =
    768            id_to_item_controller_map_.begin();
    769        i != id_to_item_controller_map_.end(); ++i) {
    770     LauncherItemController* controller = i->second;
    771     if (controller->app_id() != id)
    772       continue;
    773     if (controller->image_set_by_controller())
    774       continue;
    775     int index = model_->ItemIndexByID(i->first);
    776     if (index == -1)
    777       continue;
    778     ash::ShelfItem item = model_->items()[index];
    779     item.image = image;
    780     model_->Set(index, item);
    781     // It's possible we're waiting on more than one item, so don't break.
    782   }
    783 }
    784 
    785 void ChromeLauncherController::OnAutoHideBehaviorChanged(
    786     aura::Window* root_window,
    787     ash::ShelfAutoHideBehavior new_behavior) {
    788   SetShelfAutoHideBehaviorPrefs(new_behavior, root_window);
    789 }
    790 
    791 void ChromeLauncherController::SetLauncherItemImage(
    792     ash::ShelfID shelf_id,
    793     const gfx::ImageSkia& image) {
    794   int index = model_->ItemIndexByID(shelf_id);
    795   if (index == -1)
    796     return;
    797   ash::ShelfItem item = model_->items()[index];
    798   item.image = image;
    799   model_->Set(index, item);
    800 }
    801 
    802 bool ChromeLauncherController::CanPin() const {
    803   const PrefService::Preference* pref =
    804       profile_->GetPrefs()->FindPreference(prefs::kPinnedLauncherApps);
    805   return pref && pref->IsUserModifiable();
    806 }
    807 
    808 bool ChromeLauncherController::IsAppPinned(const std::string& app_id) {
    809   for (IDToItemControllerMap::const_iterator i =
    810            id_to_item_controller_map_.begin();
    811        i != id_to_item_controller_map_.end(); ++i) {
    812     if (IsPinned(i->first) && i->second->app_id() == app_id)
    813       return true;
    814   }
    815   return false;
    816 }
    817 
    818 bool ChromeLauncherController::IsWindowedAppInLauncher(
    819     const std::string& app_id) {
    820   int index = model_->ItemIndexByID(GetShelfIDForAppID(app_id));
    821   if (index < 0)
    822     return false;
    823 
    824   ash::ShelfItemType type = model_->items()[index].type;
    825   return type == ash::TYPE_WINDOWED_APP;
    826 }
    827 
    828 void ChromeLauncherController::PinAppWithID(const std::string& app_id) {
    829   if (CanPin())
    830     DoPinAppWithID(app_id);
    831   else
    832     NOTREACHED();
    833 }
    834 
    835 void ChromeLauncherController::SetLaunchType(
    836     ash::ShelfID id,
    837     extensions::LaunchType launch_type) {
    838   if (!HasItemController(id))
    839     return;
    840 
    841   extensions::SetLaunchType(profile_->GetExtensionService(),
    842                             id_to_item_controller_map_[id]->app_id(),
    843                             launch_type);
    844 }
    845 
    846 void ChromeLauncherController::UnpinAppWithID(const std::string& app_id) {
    847   if (CanPin())
    848     DoUnpinAppWithID(app_id);
    849   else
    850     NOTREACHED();
    851 }
    852 
    853 bool ChromeLauncherController::IsLoggedInAsGuest() {
    854   return profile_->IsGuestSession();
    855 }
    856 
    857 void ChromeLauncherController::CreateNewWindow() {
    858   // Use the currently active user.
    859   chrome::NewEmptyWindow(profile_, chrome::HOST_DESKTOP_TYPE_ASH);
    860 }
    861 
    862 void ChromeLauncherController::CreateNewIncognitoWindow() {
    863   // Use the currently active user.
    864   chrome::NewEmptyWindow(profile_->GetOffTheRecordProfile(),
    865                          chrome::HOST_DESKTOP_TYPE_ASH);
    866 }
    867 
    868 void ChromeLauncherController::PersistPinnedState() {
    869   if (ignore_persist_pinned_state_change_)
    870     return;
    871   // It is a coding error to call PersistPinnedState() if the pinned apps are
    872   // not user-editable. The code should check earlier and not perform any
    873   // modification actions that trigger persisting the state.
    874   if (!CanPin()) {
    875     NOTREACHED() << "Can't pin but pinned state being updated";
    876     return;
    877   }
    878   // Mutating kPinnedLauncherApps is going to notify us and trigger us to
    879   // process the change. We don't want that to happen so remove ourselves as a
    880   // listener.
    881   pref_change_registrar_.Remove(prefs::kPinnedLauncherApps);
    882   {
    883     ListPrefUpdate updater(profile_->GetPrefs(), prefs::kPinnedLauncherApps);
    884     updater->Clear();
    885     for (size_t i = 0; i < model_->items().size(); ++i) {
    886       if (model_->items()[i].type == ash::TYPE_APP_SHORTCUT) {
    887         ash::ShelfID id = model_->items()[i].id;
    888         if (HasItemController(id) && IsPinned(id)) {
    889           base::DictionaryValue* app_value = ash::CreateAppDict(
    890               id_to_item_controller_map_[id]->app_id());
    891           if (app_value)
    892             updater->Append(app_value);
    893         }
    894       } else if (model_->items()[i].type == ash::TYPE_BROWSER_SHORTCUT) {
    895         PersistChromeItemIndex(i);
    896       } else if (model_->items()[i].type == ash::TYPE_APP_LIST) {
    897         base::DictionaryValue* app_value = ash::CreateAppDict(
    898             kAppShelfIdPlaceholder);
    899         if (app_value)
    900           updater->Append(app_value);
    901       }
    902     }
    903   }
    904   pref_change_registrar_.Add(
    905       prefs::kPinnedLauncherApps,
    906       base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref,
    907                  base::Unretained(this)));
    908 }
    909 
    910 ash::ShelfModel* ChromeLauncherController::model() {
    911   return model_;
    912 }
    913 
    914 Profile* ChromeLauncherController::profile() {
    915   return profile_;
    916 }
    917 
    918 ash::ShelfAutoHideBehavior ChromeLauncherController::GetShelfAutoHideBehavior(
    919     aura::Window* root_window) const {
    920   // Don't show the shelf in app mode.
    921   if (chrome::IsRunningInAppMode())
    922     return ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN;
    923 
    924   // See comment in |kShelfAlignment| as to why we consider two prefs.
    925   const std::string behavior_value(
    926       GetPrefForRootWindow(profile_->GetPrefs(),
    927                            root_window,
    928                            prefs::kShelfAutoHideBehaviorLocal,
    929                            prefs::kShelfAutoHideBehavior));
    930 
    931   // Note: To maintain sync compatibility with old images of chrome/chromeos
    932   // the set of values that may be encountered includes the now-extinct
    933   // "Default" as well as "Never" and "Always", "Default" should now
    934   // be treated as "Never" (http://crbug.com/146773).
    935   if (behavior_value == ash::kShelfAutoHideBehaviorAlways)
    936     return ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS;
    937   return ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER;
    938 }
    939 
    940 bool ChromeLauncherController::CanUserModifyShelfAutoHideBehavior(
    941     aura::Window* root_window) const {
    942   return profile_->GetPrefs()->
    943       FindPreference(prefs::kShelfAutoHideBehaviorLocal)->IsUserModifiable();
    944 }
    945 
    946 void ChromeLauncherController::ToggleShelfAutoHideBehavior(
    947     aura::Window* root_window) {
    948   ash::ShelfAutoHideBehavior behavior = GetShelfAutoHideBehavior(root_window) ==
    949       ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS ?
    950           ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER :
    951           ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS;
    952   SetShelfAutoHideBehaviorPrefs(behavior, root_window);
    953   return;
    954 }
    955 
    956 void ChromeLauncherController::UpdateAppState(content::WebContents* contents,
    957                                               AppState app_state) {
    958   std::string app_id = app_tab_helper_->GetAppID(contents);
    959 
    960   // Check if the gMail app is loaded and it matches the given content.
    961   // This special treatment is needed to address crbug.com/234268.
    962   if (app_id.empty() && ContentCanBeHandledByGmailApp(contents))
    963     app_id = kGmailAppId;
    964 
    965   // Check the old |app_id| for a tab. If the contents has changed we need to
    966   // remove it from the previous app.
    967   if (web_contents_to_app_id_.find(contents) != web_contents_to_app_id_.end()) {
    968     std::string last_app_id = web_contents_to_app_id_[contents];
    969     if (last_app_id != app_id) {
    970       ash::ShelfID id = GetShelfIDForAppID(last_app_id);
    971       if (id) {
    972         // Since GetAppState() will use |web_contents_to_app_id_| we remove
    973         // the connection before calling it.
    974         web_contents_to_app_id_.erase(contents);
    975         SetItemStatus(id, GetAppState(last_app_id));
    976       }
    977     }
    978   }
    979 
    980   if (app_state == APP_STATE_REMOVED)
    981     web_contents_to_app_id_.erase(contents);
    982   else
    983     web_contents_to_app_id_[contents] = app_id;
    984 
    985   ash::ShelfID id = GetShelfIDForAppID(app_id);
    986   if (id) {
    987     SetItemStatus(id, (app_state == APP_STATE_WINDOW_ACTIVE ||
    988                        app_state == APP_STATE_ACTIVE) ? ash::STATUS_ACTIVE :
    989                                                         GetAppState(app_id));
    990   }
    991 }
    992 
    993 ash::ShelfID ChromeLauncherController::GetShelfIDForWebContents(
    994     content::WebContents* contents) {
    995   DCHECK(contents);
    996 
    997   std::string app_id = app_tab_helper_->GetAppID(contents);
    998 
    999   if (app_id.empty() && ContentCanBeHandledByGmailApp(contents))
   1000     app_id = kGmailAppId;
   1001 
   1002   ash::ShelfID id = GetShelfIDForAppID(app_id);
   1003 
   1004   if (app_id.empty() || !id) {
   1005     int browser_index = model_->GetItemIndexForType(ash::TYPE_BROWSER_SHORTCUT);
   1006     return model_->items()[browser_index].id;
   1007   }
   1008 
   1009   return id;
   1010 }
   1011 
   1012 void ChromeLauncherController::SetRefocusURLPatternForTest(ash::ShelfID id,
   1013                                                            const GURL& url) {
   1014   DCHECK(HasItemController(id));
   1015   LauncherItemController* controller = id_to_item_controller_map_[id];
   1016 
   1017   int index = model_->ItemIndexByID(id);
   1018   if (index == -1) {
   1019     NOTREACHED() << "Invalid launcher id";
   1020     return;
   1021   }
   1022 
   1023   ash::ShelfItemType type = model_->items()[index].type;
   1024   if (type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_WINDOWED_APP) {
   1025     AppShortcutLauncherItemController* app_controller =
   1026         static_cast<AppShortcutLauncherItemController*>(controller);
   1027     app_controller->set_refocus_url(url);
   1028   } else {
   1029     NOTREACHED() << "Invalid launcher type";
   1030   }
   1031 }
   1032 
   1033 const Extension* ChromeLauncherController::GetExtensionForAppID(
   1034     const std::string& app_id) const {
   1035   // Some unit tests do not have a real extension.
   1036   return (profile_->GetExtensionService()) ?
   1037       profile_->GetExtensionService()->GetInstalledExtension(app_id) : NULL;
   1038 }
   1039 
   1040 void ChromeLauncherController::ActivateWindowOrMinimizeIfActive(
   1041     ui::BaseWindow* window,
   1042     bool allow_minimize) {
   1043   // In separated desktop mode we might have to teleport a window back to the
   1044   // current user.
   1045   if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
   1046           chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED) {
   1047     aura::Window* native_window = window->GetNativeWindow();
   1048     const std::string& current_user =
   1049         multi_user_util::GetUserIDFromProfile(profile());
   1050     chrome::MultiUserWindowManager* manager =
   1051         chrome::MultiUserWindowManager::GetInstance();
   1052     if (!manager->IsWindowOnDesktopOfUser(native_window, current_user)) {
   1053       ash::MultiProfileUMA::RecordTeleportAction(
   1054           ash::MultiProfileUMA::TELEPORT_WINDOW_RETURN_BY_LAUNCHER);
   1055       manager->ShowWindowForUser(native_window, current_user);
   1056       window->Activate();
   1057       return;
   1058     }
   1059   }
   1060 
   1061   if (window->IsActive() && allow_minimize) {
   1062     if (CommandLine::ForCurrentProcess()->HasSwitch(
   1063             switches::kDisableMinimizeOnSecondLauncherItemClick)) {
   1064       AnimateWindow(window->GetNativeWindow(),
   1065                     wm::WINDOW_ANIMATION_TYPE_BOUNCE);
   1066     } else {
   1067       window->Minimize();
   1068     }
   1069   } else {
   1070     window->Show();
   1071     window->Activate();
   1072   }
   1073 }
   1074 
   1075 void ChromeLauncherController::OnShelfCreated(ash::Shelf* shelf) {
   1076   shelves_.insert(shelf);
   1077   shelf->shelf_widget()->shelf_layout_manager()->AddObserver(this);
   1078 }
   1079 
   1080 void ChromeLauncherController::OnShelfDestroyed(ash::Shelf* shelf) {
   1081   shelves_.erase(shelf);
   1082   // RemoveObserver is not called here, since by the time this method is called
   1083   // Shelf is already in its destructor.
   1084 }
   1085 
   1086 void ChromeLauncherController::ShelfItemAdded(int index) {
   1087   // The app list launcher can get added to the shelf after we applied the
   1088   // preferences. In that case the item might be at the wrong spot. As such we
   1089   // call the function again.
   1090   if (model_->items()[index].type == ash::TYPE_APP_LIST)
   1091     UpdateAppLaunchersFromPref();
   1092 }
   1093 
   1094 void ChromeLauncherController::ShelfItemRemoved(int index, ash::ShelfID id) {
   1095 }
   1096 
   1097 void ChromeLauncherController::ShelfItemMoved(int start_index,
   1098                                               int target_index) {
   1099   const ash::ShelfItem& item = model_->items()[target_index];
   1100   // We remember the moved item position if it is either pinnable or
   1101   // it is the app list with the alternate shelf layout.
   1102   if ((HasItemController(item.id) && IsPinned(item.id)) ||
   1103        item.type == ash::TYPE_APP_LIST)
   1104     PersistPinnedState();
   1105 }
   1106 
   1107 void ChromeLauncherController::ShelfItemChanged(
   1108     int index,
   1109     const ash::ShelfItem& old_item) {
   1110 }
   1111 
   1112 void ChromeLauncherController::ShelfStatusChanged() {
   1113 }
   1114 
   1115 void ChromeLauncherController::ActiveUserChanged(
   1116     const std::string& user_email) {
   1117   // Store the order of running applications for the user which gets inactive.
   1118   RememberUnpinnedRunningApplicationOrder();
   1119   // Coming here the default profile is already switched. All profile specific
   1120   // resources get released and the new profile gets attached instead.
   1121   ReleaseProfile();
   1122   // When coming here, the active user has already be changed so that we can
   1123   // set it as active.
   1124   AttachProfile(ProfileManager::GetActiveUserProfile());
   1125   // Update the V1 applications.
   1126   browser_status_monitor_->ActiveUserChanged(user_email);
   1127   // Switch the running applications to the new user.
   1128   app_window_controller_->ActiveUserChanged(user_email);
   1129   // Update the user specific shell properties from the new user profile.
   1130   UpdateAppLaunchersFromPref();
   1131   SetShelfAlignmentFromPrefs();
   1132   SetShelfAutoHideBehaviorFromPrefs();
   1133   SetShelfBehaviorsFromPrefs();
   1134 #if defined(OS_CHROMEOS)
   1135   SetVirtualKeyboardBehaviorFromPrefs();
   1136 #endif  // defined(OS_CHROMEOS)
   1137   // Restore the order of running, but unpinned applications for the activated
   1138   // user.
   1139   RestoreUnpinnedRunningApplicationOrder(user_email);
   1140   // Inform the system tray of the change.
   1141   ash::Shell::GetInstance()->system_tray_delegate()->ActiveUserWasChanged();
   1142   // Force on-screen keyboard to reset.
   1143   if (keyboard::IsKeyboardEnabled())
   1144     ash::Shell::GetInstance()->CreateKeyboard();
   1145 }
   1146 
   1147 void ChromeLauncherController::AdditionalUserAddedToSession(Profile* profile) {
   1148   // Switch the running applications to the new user.
   1149   app_window_controller_->AdditionalUserAddedToSession(profile);
   1150 }
   1151 
   1152 void ChromeLauncherController::Observe(
   1153     int type,
   1154     const content::NotificationSource& source,
   1155     const content::NotificationDetails& details) {
   1156   switch (type) {
   1157     case chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED: {
   1158       const Extension* extension =
   1159           content::Details<const Extension>(details).ptr();
   1160       if (IsAppPinned(extension->id())) {
   1161         // Clear and re-fetch to ensure icon is up-to-date.
   1162         app_icon_loader_->ClearImage(extension->id());
   1163         app_icon_loader_->FetchImage(extension->id());
   1164       }
   1165 
   1166       UpdateAppLaunchersFromPref();
   1167       break;
   1168     }
   1169     case chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: {
   1170       const content::Details<UnloadedExtensionInfo>& unload_info(details);
   1171       const Extension* extension = unload_info->extension;
   1172       const std::string& id = extension->id();
   1173       // Since we might have windowed apps of this type which might have
   1174       // outstanding locks which needs to be removed.
   1175       if (GetShelfIDForAppID(id) &&
   1176           unload_info->reason == UnloadedExtensionInfo::REASON_UNINSTALL) {
   1177         CloseWindowedAppsFromRemovedExtension(id);
   1178       }
   1179 
   1180       if (IsAppPinned(id)) {
   1181         if (unload_info->reason == UnloadedExtensionInfo::REASON_UNINSTALL) {
   1182           DoUnpinAppWithID(id);
   1183           app_icon_loader_->ClearImage(id);
   1184         } else {
   1185           app_icon_loader_->UpdateImage(id);
   1186         }
   1187       }
   1188       break;
   1189     }
   1190     default:
   1191       NOTREACHED() << "Unexpected notification type=" << type;
   1192   }
   1193 }
   1194 
   1195 void ChromeLauncherController::OnShelfAlignmentChanged(
   1196     aura::Window* root_window) {
   1197   const char* pref_value = NULL;
   1198   switch (ash::Shell::GetInstance()->GetShelfAlignment(root_window)) {
   1199     case ash::SHELF_ALIGNMENT_BOTTOM:
   1200       pref_value = ash::kShelfAlignmentBottom;
   1201       break;
   1202     case ash::SHELF_ALIGNMENT_LEFT:
   1203       pref_value = ash::kShelfAlignmentLeft;
   1204       break;
   1205     case ash::SHELF_ALIGNMENT_RIGHT:
   1206       pref_value = ash::kShelfAlignmentRight;
   1207       break;
   1208     case ash::SHELF_ALIGNMENT_TOP:
   1209       pref_value = ash::kShelfAlignmentTop;
   1210   }
   1211 
   1212   UpdatePerDisplayPref(
   1213       profile_->GetPrefs(), root_window, prefs::kShelfAlignment, pref_value);
   1214 
   1215   if (root_window == ash::Shell::GetPrimaryRootWindow()) {
   1216     // See comment in |kShelfAlignment| about why we have two prefs here.
   1217     profile_->GetPrefs()->SetString(prefs::kShelfAlignmentLocal, pref_value);
   1218     profile_->GetPrefs()->SetString(prefs::kShelfAlignment, pref_value);
   1219   }
   1220 }
   1221 
   1222 void ChromeLauncherController::OnDisplayConfigurationChanged() {
   1223   SetShelfBehaviorsFromPrefs();
   1224 }
   1225 
   1226 void ChromeLauncherController::OnIsSyncingChanged() {
   1227   PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_);
   1228   MaybePropagatePrefToLocal(prefs,
   1229                             prefs::kShelfAlignmentLocal,
   1230                             prefs::kShelfAlignment);
   1231   MaybePropagatePrefToLocal(prefs,
   1232                             prefs::kShelfAutoHideBehaviorLocal,
   1233                             prefs::kShelfAutoHideBehavior);
   1234 }
   1235 
   1236 void ChromeLauncherController::OnAppSyncUIStatusChanged() {
   1237   if (app_sync_ui_state_->status() == AppSyncUIState::STATUS_SYNCING)
   1238     model_->SetStatus(ash::ShelfModel::STATUS_LOADING);
   1239   else
   1240     model_->SetStatus(ash::ShelfModel::STATUS_NORMAL);
   1241 }
   1242 
   1243 void ChromeLauncherController::ExtensionEnableFlowFinished() {
   1244   LaunchApp(extension_enable_flow_->extension_id(),
   1245             ash::LAUNCH_FROM_UNKNOWN,
   1246             ui::EF_NONE);
   1247   extension_enable_flow_.reset();
   1248 }
   1249 
   1250 void ChromeLauncherController::ExtensionEnableFlowAborted(bool user_initiated) {
   1251   extension_enable_flow_.reset();
   1252 }
   1253 
   1254 ChromeLauncherAppMenuItems ChromeLauncherController::GetApplicationList(
   1255     const ash::ShelfItem& item,
   1256     int event_flags) {
   1257   // Make sure that there is a controller associated with the id and that the
   1258   // extension itself is a valid application and not a panel.
   1259   if (!HasItemController(item.id) ||
   1260       !GetShelfIDForAppID(id_to_item_controller_map_[item.id]->app_id()))
   1261     return ChromeLauncherAppMenuItems().Pass();
   1262 
   1263   return id_to_item_controller_map_[item.id]->GetApplicationList(event_flags);
   1264 }
   1265 
   1266 std::vector<content::WebContents*>
   1267 ChromeLauncherController::GetV1ApplicationsFromAppId(std::string app_id) {
   1268   ash::ShelfID id = GetShelfIDForAppID(app_id);
   1269 
   1270   // If there is no such an item pinned to the launcher, no menu gets created.
   1271   if (id) {
   1272     LauncherItemController* controller = id_to_item_controller_map_[id];
   1273     DCHECK(controller);
   1274     if (controller->type() == LauncherItemController::TYPE_SHORTCUT)
   1275       return GetV1ApplicationsFromController(controller);
   1276   }
   1277   return std::vector<content::WebContents*>();
   1278 }
   1279 
   1280 void ChromeLauncherController::ActivateShellApp(const std::string& app_id,
   1281                                                 int index) {
   1282   ash::ShelfID id = GetShelfIDForAppID(app_id);
   1283   if (id) {
   1284     LauncherItemController* controller = id_to_item_controller_map_[id];
   1285     if (controller->type() == LauncherItemController::TYPE_APP) {
   1286       AppWindowLauncherItemController* app_window_controller =
   1287           static_cast<AppWindowLauncherItemController*>(controller);
   1288       app_window_controller->ActivateIndexedApp(index);
   1289     }
   1290   }
   1291 }
   1292 
   1293 bool ChromeLauncherController::IsWebContentHandledByApplication(
   1294     content::WebContents* web_contents,
   1295     const std::string& app_id) {
   1296   if ((web_contents_to_app_id_.find(web_contents) !=
   1297        web_contents_to_app_id_.end()) &&
   1298       (web_contents_to_app_id_[web_contents] == app_id))
   1299     return true;
   1300   return (app_id == kGmailAppId && ContentCanBeHandledByGmailApp(web_contents));
   1301 }
   1302 
   1303 bool ChromeLauncherController::ContentCanBeHandledByGmailApp(
   1304     content::WebContents* web_contents) {
   1305   ash::ShelfID id = GetShelfIDForAppID(kGmailAppId);
   1306   if (id) {
   1307     const GURL url = web_contents->GetURL();
   1308     // We need to extend the application matching for the gMail app beyond the
   1309     // manifest file's specification. This is required because of the namespace
   1310     // overlap with the offline app ("/mail/mu/").
   1311     if (!MatchPattern(url.path(), "/mail/mu/*") &&
   1312         MatchPattern(url.path(), "/mail/*") &&
   1313         GetExtensionForAppID(kGmailAppId) &&
   1314         GetExtensionForAppID(kGmailAppId)->OverlapsWithOrigin(url))
   1315       return true;
   1316   }
   1317   return false;
   1318 }
   1319 
   1320 gfx::Image ChromeLauncherController::GetAppListIcon(
   1321     content::WebContents* web_contents) const {
   1322   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
   1323   if (IsIncognito(web_contents))
   1324     return rb.GetImageNamed(IDR_ASH_SHELF_LIST_INCOGNITO_BROWSER);
   1325   FaviconTabHelper* favicon_tab_helper =
   1326       FaviconTabHelper::FromWebContents(web_contents);
   1327   gfx::Image result = favicon_tab_helper->GetFavicon();
   1328   if (result.IsEmpty())
   1329     return rb.GetImageNamed(IDR_DEFAULT_FAVICON);
   1330   return result;
   1331 }
   1332 
   1333 base::string16 ChromeLauncherController::GetAppListTitle(
   1334     content::WebContents* web_contents) const {
   1335   base::string16 title = web_contents->GetTitle();
   1336   if (!title.empty())
   1337     return title;
   1338   WebContentsToAppIDMap::const_iterator iter =
   1339       web_contents_to_app_id_.find(web_contents);
   1340   if (iter != web_contents_to_app_id_.end()) {
   1341     std::string app_id = iter->second;
   1342     const extensions::Extension* extension = GetExtensionForAppID(app_id);
   1343     if (extension)
   1344       return base::UTF8ToUTF16(extension->name());
   1345   }
   1346   return l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE);
   1347 }
   1348 
   1349 ash::ShelfID ChromeLauncherController::CreateAppShortcutLauncherItem(
   1350     const std::string& app_id,
   1351     int index) {
   1352   return CreateAppShortcutLauncherItemWithType(app_id,
   1353                                                index,
   1354                                                ash::TYPE_APP_SHORTCUT);
   1355 }
   1356 
   1357 void ChromeLauncherController::SetAppTabHelperForTest(AppTabHelper* helper) {
   1358   app_tab_helper_.reset(helper);
   1359 }
   1360 
   1361 void ChromeLauncherController::SetAppIconLoaderForTest(
   1362     extensions::AppIconLoader* loader) {
   1363   app_icon_loader_.reset(loader);
   1364 }
   1365 
   1366 const std::string& ChromeLauncherController::GetAppIdFromShelfIdForTest(
   1367     ash::ShelfID id) {
   1368   return id_to_item_controller_map_[id]->app_id();
   1369 }
   1370 
   1371 void ChromeLauncherController::SetShelfItemDelegateManagerForTest(
   1372     ash::ShelfItemDelegateManager* manager) {
   1373   item_delegate_manager_ = manager;
   1374 }
   1375 
   1376 void ChromeLauncherController::RememberUnpinnedRunningApplicationOrder() {
   1377   RunningAppListIds list;
   1378   for (int i = 0; i < model_->item_count(); i++) {
   1379     ash::ShelfItemType type = model_->items()[i].type;
   1380     if (type == ash::TYPE_WINDOWED_APP || type == ash::TYPE_PLATFORM_APP)
   1381       list.push_back(GetAppIDForShelfID(model_->items()[i].id));
   1382   }
   1383   last_used_running_application_order_[
   1384       multi_user_util::GetUserIDFromProfile(profile_)] = list;
   1385 }
   1386 
   1387 void ChromeLauncherController::RestoreUnpinnedRunningApplicationOrder(
   1388     const std::string& user_id) {
   1389   const RunningAppListIdMap::iterator app_id_list =
   1390       last_used_running_application_order_.find(user_id);
   1391   if (app_id_list == last_used_running_application_order_.end())
   1392     return;
   1393 
   1394   // Find the first insertion point for running applications.
   1395   int running_index = model_->FirstRunningAppIndex();
   1396   for (RunningAppListIds::iterator app_id = app_id_list->second.begin();
   1397        app_id != app_id_list->second.end(); ++app_id) {
   1398     ash::ShelfID shelf_id = GetShelfIDForAppID(*app_id);
   1399     if (shelf_id) {
   1400       int app_index = model_->ItemIndexByID(shelf_id);
   1401       DCHECK_GE(app_index, 0);
   1402       ash::ShelfItemType type = model_->items()[app_index].type;
   1403       if (type == ash::TYPE_WINDOWED_APP || type == ash::TYPE_PLATFORM_APP) {
   1404         if (running_index != app_index)
   1405           model_->Move(running_index, app_index);
   1406         running_index++;
   1407       }
   1408     }
   1409   }
   1410 }
   1411 
   1412 ash::ShelfID ChromeLauncherController::CreateAppShortcutLauncherItemWithType(
   1413     const std::string& app_id,
   1414     int index,
   1415     ash::ShelfItemType shelf_item_type) {
   1416   AppShortcutLauncherItemController* controller =
   1417       new AppShortcutLauncherItemController(app_id, this);
   1418   ash::ShelfID shelf_id = InsertAppLauncherItem(
   1419       controller, app_id, ash::STATUS_CLOSED, index, shelf_item_type);
   1420   return shelf_id;
   1421 }
   1422 
   1423 LauncherItemController* ChromeLauncherController::GetLauncherItemController(
   1424     const ash::ShelfID id) {
   1425   if (!HasItemController(id))
   1426     return NULL;
   1427   return id_to_item_controller_map_[id];
   1428 }
   1429 
   1430 bool ChromeLauncherController::IsBrowserFromActiveUser(Browser* browser) {
   1431   // If running multi user mode with separate desktops, we have to check if the
   1432   // browser is from the active user.
   1433   if (chrome::MultiUserWindowManager::GetMultiProfileMode() !=
   1434           chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED)
   1435     return true;
   1436   return multi_user_util::IsProfileFromActiveUser(browser->profile());
   1437 }
   1438 
   1439 void ChromeLauncherController::LauncherItemClosed(ash::ShelfID id) {
   1440   IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
   1441   CHECK(iter != id_to_item_controller_map_.end());
   1442   CHECK(iter->second);
   1443   app_icon_loader_->ClearImage(iter->second->app_id());
   1444   id_to_item_controller_map_.erase(iter);
   1445   int index = model_->ItemIndexByID(id);
   1446   // A "browser proxy" is not known to the model and this removal does
   1447   // therefore not need to be propagated to the model.
   1448   if (index != -1)
   1449     model_->RemoveItemAt(index);
   1450 }
   1451 
   1452 void ChromeLauncherController::DoPinAppWithID(const std::string& app_id) {
   1453   // If there is an item, do nothing and return.
   1454   if (IsAppPinned(app_id))
   1455     return;
   1456 
   1457   ash::ShelfID shelf_id = GetShelfIDForAppID(app_id);
   1458   if (shelf_id) {
   1459     // App item exists, pin it
   1460     Pin(shelf_id);
   1461   } else {
   1462     // Otherwise, create a shortcut item for it.
   1463     CreateAppShortcutLauncherItem(app_id, model_->item_count());
   1464     if (CanPin())
   1465       PersistPinnedState();
   1466   }
   1467 }
   1468 
   1469 void ChromeLauncherController::DoUnpinAppWithID(const std::string& app_id) {
   1470   ash::ShelfID shelf_id = GetShelfIDForAppID(app_id);
   1471   if (shelf_id && IsPinned(shelf_id))
   1472     Unpin(shelf_id);
   1473 }
   1474 
   1475 int ChromeLauncherController::PinRunningAppInternal(int index,
   1476                                                     ash::ShelfID shelf_id) {
   1477   int running_index = model_->ItemIndexByID(shelf_id);
   1478   ash::ShelfItem item = model_->items()[running_index];
   1479   DCHECK(item.type == ash::TYPE_WINDOWED_APP ||
   1480          item.type == ash::TYPE_PLATFORM_APP);
   1481   item.type = ash::TYPE_APP_SHORTCUT;
   1482   model_->Set(running_index, item);
   1483   // The |ShelfModel|'s weight system might reposition the item to a
   1484   // new index, so we get the index again.
   1485   running_index = model_->ItemIndexByID(shelf_id);
   1486   if (running_index < index)
   1487     --index;
   1488   if (running_index != index)
   1489     model_->Move(running_index, index);
   1490   return index;
   1491 }
   1492 
   1493 void ChromeLauncherController::UnpinRunningAppInternal(int index) {
   1494   DCHECK_GE(index, 0);
   1495   ash::ShelfItem item = model_->items()[index];
   1496   DCHECK_EQ(item.type, ash::TYPE_APP_SHORTCUT);
   1497   item.type = ash::TYPE_WINDOWED_APP;
   1498   // A platform app and a windowed app are sharing TYPE_APP_SHORTCUT. As such
   1499   // we have to check here what this was before it got a shortcut.
   1500   if (HasItemController(item.id) &&
   1501       id_to_item_controller_map_[item.id]->type() ==
   1502           LauncherItemController::TYPE_APP)
   1503     item.type = ash::TYPE_PLATFORM_APP;
   1504   model_->Set(index, item);
   1505 }
   1506 
   1507 void ChromeLauncherController::UpdateAppLaunchersFromPref() {
   1508   // There are various functions which will trigger a |PersistPinnedState| call
   1509   // like a direct call to |DoPinAppWithID|, or an indirect call to the menu
   1510   // model which will use weights to re-arrange the icons to new positions.
   1511   // Since this function is meant to synchronize the "is state" with the
   1512   // "sync state", it makes no sense to store any changes by this function back
   1513   // into the pref state. Therefore we tell |persistPinnedState| to ignore any
   1514   // invocations while we are running.
   1515   base::AutoReset<bool> auto_reset(&ignore_persist_pinned_state_change_, true);
   1516   std::vector<std::string> pinned_apps = GetListOfPinnedAppsAndBrowser();
   1517 
   1518   int index = 0;
   1519   int max_index = model_->item_count();
   1520 
   1521   // When one of the two special items cannot be moved (and we do not know where
   1522   // yet), we remember the current location in one of these variables.
   1523   int chrome_index = -1;
   1524   int app_list_index = -1;
   1525 
   1526   // Walk the model and |pinned_apps| from the pref lockstep, adding and
   1527   // removing items as necessary. NB: This code uses plain old indexing instead
   1528   // of iterators because of model mutations as part of the loop.
   1529   std::vector<std::string>::const_iterator pref_app_id(pinned_apps.begin());
   1530   for (; index < max_index && pref_app_id != pinned_apps.end(); ++index) {
   1531     // Check if we have an item which we need to handle.
   1532     if (*pref_app_id == extension_misc::kChromeAppId ||
   1533         *pref_app_id == kAppShelfIdPlaceholder ||
   1534         IsAppPinned(*pref_app_id)) {
   1535       for (; index < max_index; ++index) {
   1536         const ash::ShelfItem& item(model_->items()[index]);
   1537         bool is_app_list = item.type == ash::TYPE_APP_LIST;
   1538         bool is_chrome = item.type == ash::TYPE_BROWSER_SHORTCUT;
   1539         if (item.type != ash::TYPE_APP_SHORTCUT && !is_app_list && !is_chrome)
   1540           continue;
   1541         IDToItemControllerMap::const_iterator entry =
   1542             id_to_item_controller_map_.find(item.id);
   1543         if ((kAppShelfIdPlaceholder == *pref_app_id && is_app_list) ||
   1544             (extension_misc::kChromeAppId == *pref_app_id && is_chrome) ||
   1545             (entry != id_to_item_controller_map_.end() &&
   1546              entry->second->app_id() == *pref_app_id)) {
   1547           // Check if an item needs to be moved here.
   1548           MoveChromeOrApplistToFinalPosition(
   1549               is_chrome, is_app_list, index, &chrome_index, &app_list_index);
   1550           ++pref_app_id;
   1551           break;
   1552         } else {
   1553           if (is_chrome || is_app_list) {
   1554             // We cannot delete any of these shortcuts. As such we remember
   1555             // their positions and move them later where they belong.
   1556             if (is_chrome)
   1557               chrome_index = index;
   1558             else
   1559               app_list_index = index;
   1560             // And skip the item - or exit the loop if end is reached (note that
   1561             // in that case we will reduce the index again by one and this only
   1562             // compensates for it).
   1563             if (index >= max_index - 1)
   1564               break;
   1565             ++index;
   1566           } else {
   1567             // Check if this is a platform or a windowed app.
   1568             if (item.type == ash::TYPE_APP_SHORTCUT &&
   1569                 (id_to_item_controller_map_[item.id]->locked() ||
   1570                  id_to_item_controller_map_[item.id]->type() ==
   1571                      LauncherItemController::TYPE_APP)) {
   1572               // Note: This will not change the amount of items (|max_index|).
   1573               // Even changes to the actual |index| due to item weighting
   1574               // changes should be fine.
   1575               UnpinRunningAppInternal(index);
   1576             } else {
   1577               LauncherItemClosed(item.id);
   1578               --max_index;
   1579             }
   1580           }
   1581           --index;
   1582         }
   1583       }
   1584       // If the item wasn't found, that means id_to_item_controller_map_
   1585       // is out of sync.
   1586       DCHECK(index <= max_index);
   1587     } else {
   1588       // Check if the item was already running but not yet pinned.
   1589       ash::ShelfID shelf_id = GetShelfIDForAppID(*pref_app_id);
   1590       if (shelf_id) {
   1591         // This app is running but not yet pinned. So pin and move it.
   1592         index = PinRunningAppInternal(index, shelf_id);
   1593       } else {
   1594         // This app wasn't pinned before, insert a new entry.
   1595         shelf_id = CreateAppShortcutLauncherItem(*pref_app_id, index);
   1596         ++max_index;
   1597         index = model_->ItemIndexByID(shelf_id);
   1598       }
   1599       ++pref_app_id;
   1600     }
   1601   }
   1602 
   1603   // Remove any trailing existing items.
   1604   while (index < model_->item_count()) {
   1605     const ash::ShelfItem& item(model_->items()[index]);
   1606     if (item.type == ash::TYPE_APP_SHORTCUT) {
   1607       if (id_to_item_controller_map_[item.id]->locked() ||
   1608           id_to_item_controller_map_[item.id]->type() ==
   1609               LauncherItemController::TYPE_APP)
   1610         UnpinRunningAppInternal(index);
   1611       else
   1612         LauncherItemClosed(item.id);
   1613     } else {
   1614       if (item.type == ash::TYPE_BROWSER_SHORTCUT)
   1615         chrome_index = index;
   1616       else if (item.type == ash::TYPE_APP_LIST)
   1617         app_list_index = index;
   1618       ++index;
   1619     }
   1620   }
   1621 
   1622   // Append unprocessed items from the pref to the end of the model.
   1623   for (; pref_app_id != pinned_apps.end(); ++pref_app_id) {
   1624     // All items but the chrome and / or app list shortcut needs to be added.
   1625     bool is_chrome = *pref_app_id == extension_misc::kChromeAppId;
   1626     bool is_app_list = *pref_app_id == kAppShelfIdPlaceholder;
   1627     // Coming here we know the next item which can be finalized, either the
   1628     // chrome item or the app launcher. The final position is the end of the
   1629     // list. The menu model will make sure that the item is grouped according
   1630     // to its weight (which we do not know here).
   1631     if (!is_chrome && !is_app_list) {
   1632       DoPinAppWithID(*pref_app_id);
   1633       int target_index = FindInsertionPoint(false);
   1634       ash::ShelfID id = GetShelfIDForAppID(*pref_app_id);
   1635       int source_index = model_->ItemIndexByID(id);
   1636       if (source_index != target_index)
   1637         model_->Move(source_index, target_index);
   1638 
   1639       // Needed for the old layout - the weight might force it to be lower in
   1640       // rank.
   1641       if (app_list_index != -1 && target_index <= app_list_index)
   1642         ++app_list_index;
   1643     } else {
   1644       int target_index = FindInsertionPoint(is_app_list);
   1645       MoveChromeOrApplistToFinalPosition(
   1646           is_chrome, is_app_list, target_index, &chrome_index, &app_list_index);
   1647     }
   1648   }
   1649 }
   1650 
   1651 void ChromeLauncherController::SetShelfAutoHideBehaviorPrefs(
   1652     ash::ShelfAutoHideBehavior behavior,
   1653     aura::Window* root_window) {
   1654   const char* value = NULL;
   1655   switch (behavior) {
   1656     case ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS:
   1657       value = ash::kShelfAutoHideBehaviorAlways;
   1658       break;
   1659     case ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER:
   1660       value = ash::kShelfAutoHideBehaviorNever;
   1661       break;
   1662     case ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN:
   1663       // This one should not be a valid preference option for now. We only want
   1664       // to completely hide it when we run in app mode - or while we temporarily
   1665       // hide the shelf as part of an animation (e.g. the multi user change).
   1666       return;
   1667   }
   1668 
   1669   UpdatePerDisplayPref(
   1670       profile_->GetPrefs(), root_window, prefs::kShelfAutoHideBehavior, value);
   1671 
   1672   if (root_window == ash::Shell::GetPrimaryRootWindow()) {
   1673     // See comment in |kShelfAlignment| about why we have two prefs here.
   1674     profile_->GetPrefs()->SetString(prefs::kShelfAutoHideBehaviorLocal, value);
   1675     profile_->GetPrefs()->SetString(prefs::kShelfAutoHideBehavior, value);
   1676   }
   1677 }
   1678 
   1679 void ChromeLauncherController::SetShelfAutoHideBehaviorFromPrefs() {
   1680   aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
   1681 
   1682   for (aura::Window::Windows::const_iterator iter = root_windows.begin();
   1683        iter != root_windows.end(); ++iter) {
   1684     ash::Shell::GetInstance()->SetShelfAutoHideBehavior(
   1685         GetShelfAutoHideBehavior(*iter), *iter);
   1686   }
   1687 }
   1688 
   1689 void ChromeLauncherController::SetShelfAlignmentFromPrefs() {
   1690   if (!ash::ShelfWidget::ShelfAlignmentAllowed())
   1691     return;
   1692 
   1693   aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
   1694 
   1695   for (aura::Window::Windows::const_iterator iter = root_windows.begin();
   1696        iter != root_windows.end(); ++iter) {
   1697     // See comment in |kShelfAlignment| as to why we consider two prefs.
   1698     const std::string alignment_value(
   1699         GetPrefForRootWindow(profile_->GetPrefs(),
   1700                              *iter,
   1701                              prefs::kShelfAlignmentLocal,
   1702                              prefs::kShelfAlignment));
   1703     ash::ShelfAlignment alignment = ash::SHELF_ALIGNMENT_BOTTOM;
   1704     if (alignment_value == ash::kShelfAlignmentLeft)
   1705       alignment = ash::SHELF_ALIGNMENT_LEFT;
   1706     else if (alignment_value == ash::kShelfAlignmentRight)
   1707       alignment = ash::SHELF_ALIGNMENT_RIGHT;
   1708     else if (alignment_value == ash::kShelfAlignmentTop)
   1709       alignment = ash::SHELF_ALIGNMENT_TOP;
   1710     ash::Shell::GetInstance()->SetShelfAlignment(alignment, *iter);
   1711   }
   1712 }
   1713 
   1714 void ChromeLauncherController::SetShelfBehaviorsFromPrefs() {
   1715   SetShelfAutoHideBehaviorFromPrefs();
   1716   SetShelfAlignmentFromPrefs();
   1717 }
   1718 
   1719 #if defined(OS_CHROMEOS)
   1720 void ChromeLauncherController::SetVirtualKeyboardBehaviorFromPrefs() {
   1721   const PrefService* service = profile_->GetPrefs();
   1722   if (!service->HasPrefPath(prefs::kTouchVirtualKeyboardEnabled)) {
   1723     keyboard::SetKeyboardShowOverride(keyboard::KEYBOARD_SHOW_OVERRIDE_NONE);
   1724   } else {
   1725     const bool enabled = service->GetBoolean(
   1726         prefs::kTouchVirtualKeyboardEnabled);
   1727     keyboard::SetKeyboardShowOverride(
   1728         enabled ? keyboard::KEYBOARD_SHOW_OVERRIDE_ENABLED
   1729                 : keyboard::KEYBOARD_SHOW_OVERRIDE_DISABLED);
   1730   }
   1731 }
   1732 #endif //  defined(OS_CHROMEOS)
   1733 
   1734 ash::ShelfItemStatus ChromeLauncherController::GetAppState(
   1735     const::std::string& app_id) {
   1736   ash::ShelfItemStatus status = ash::STATUS_CLOSED;
   1737   for (WebContentsToAppIDMap::iterator it = web_contents_to_app_id_.begin();
   1738        it != web_contents_to_app_id_.end();
   1739        ++it) {
   1740     if (it->second == app_id) {
   1741       Browser* browser = chrome::FindBrowserWithWebContents(it->first);
   1742       // Usually there should never be an item in our |web_contents_to_app_id_|
   1743       // list which got deleted already. However - in some situations e.g.
   1744       // Browser::SwapTabContent there is temporarily no associated browser.
   1745       if (!browser)
   1746         continue;
   1747       if (browser->window()->IsActive()) {
   1748         return browser->tab_strip_model()->GetActiveWebContents() == it->first ?
   1749             ash::STATUS_ACTIVE : ash::STATUS_RUNNING;
   1750       } else {
   1751         status = ash::STATUS_RUNNING;
   1752       }
   1753     }
   1754   }
   1755   return status;
   1756 }
   1757 
   1758 ash::ShelfID ChromeLauncherController::InsertAppLauncherItem(
   1759     LauncherItemController* controller,
   1760     const std::string& app_id,
   1761     ash::ShelfItemStatus status,
   1762     int index,
   1763     ash::ShelfItemType shelf_item_type) {
   1764   ash::ShelfID id = model_->next_id();
   1765   CHECK(!HasItemController(id));
   1766   CHECK(controller);
   1767   id_to_item_controller_map_[id] = controller;
   1768   controller->set_shelf_id(id);
   1769 
   1770   ash::ShelfItem item;
   1771   item.type = shelf_item_type;
   1772   item.image = extensions::util::GetDefaultAppIcon();
   1773 
   1774   ash::ShelfItemStatus new_state = GetAppState(app_id);
   1775   if (new_state != ash::STATUS_CLOSED)
   1776     status = new_state;
   1777 
   1778   item.status = status;
   1779 
   1780   model_->AddAt(index, item);
   1781 
   1782   app_icon_loader_->FetchImage(app_id);
   1783 
   1784   SetShelfItemDelegate(id, controller);
   1785 
   1786   return id;
   1787 }
   1788 
   1789 bool ChromeLauncherController::HasItemController(ash::ShelfID id) const {
   1790   return id_to_item_controller_map_.find(id) !=
   1791          id_to_item_controller_map_.end();
   1792 }
   1793 
   1794 std::vector<content::WebContents*>
   1795 ChromeLauncherController::GetV1ApplicationsFromController(
   1796     LauncherItemController* controller) {
   1797   DCHECK(controller->type() == LauncherItemController::TYPE_SHORTCUT);
   1798   AppShortcutLauncherItemController* app_controller =
   1799       static_cast<AppShortcutLauncherItemController*>(controller);
   1800   return app_controller->GetRunningApplications();
   1801 }
   1802 
   1803 BrowserShortcutLauncherItemController*
   1804 ChromeLauncherController::GetBrowserShortcutLauncherItemController() {
   1805   for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin();
   1806       i != id_to_item_controller_map_.end(); ++i) {
   1807     int index = model_->ItemIndexByID(i->first);
   1808     const ash::ShelfItem& item = model_->items()[index];
   1809     if (item.type == ash::TYPE_BROWSER_SHORTCUT)
   1810       return static_cast<BrowserShortcutLauncherItemController*>(i->second);
   1811   }
   1812   // Create a LauncherItemController for the Browser shortcut if it does not
   1813   // exist yet.
   1814   ash::ShelfID id = CreateBrowserShortcutLauncherItem();
   1815   DCHECK(id_to_item_controller_map_[id]);
   1816   return static_cast<BrowserShortcutLauncherItemController*>(
   1817       id_to_item_controller_map_[id]);
   1818 }
   1819 
   1820 ash::ShelfID ChromeLauncherController::CreateBrowserShortcutLauncherItem() {
   1821   ash::ShelfItem browser_shortcut;
   1822   browser_shortcut.type = ash::TYPE_BROWSER_SHORTCUT;
   1823   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
   1824   browser_shortcut.image = *rb.GetImageSkiaNamed(IDR_PRODUCT_LOGO_32);
   1825   ash::ShelfID id = model_->next_id();
   1826   size_t index = GetChromeIconIndexForCreation();
   1827   model_->AddAt(index, browser_shortcut);
   1828   id_to_item_controller_map_[id] =
   1829       new BrowserShortcutLauncherItemController(this);
   1830   id_to_item_controller_map_[id]->set_shelf_id(id);
   1831   // ShelfItemDelegateManager owns BrowserShortcutLauncherItemController.
   1832   SetShelfItemDelegate(id, id_to_item_controller_map_[id]);
   1833   return id;
   1834 }
   1835 
   1836 void ChromeLauncherController::PersistChromeItemIndex(int index) {
   1837   profile_->GetPrefs()->SetInteger(prefs::kShelfChromeIconIndex, index);
   1838 }
   1839 
   1840 int ChromeLauncherController::GetChromeIconIndexFromPref() const {
   1841   size_t index = profile_->GetPrefs()->GetInteger(prefs::kShelfChromeIconIndex);
   1842   const base::ListValue* pinned_apps_pref =
   1843       profile_->GetPrefs()->GetList(prefs::kPinnedLauncherApps);
   1844   return std::max(static_cast<size_t>(0),
   1845                   std::min(pinned_apps_pref->GetSize(), index));
   1846 }
   1847 
   1848 void ChromeLauncherController::MoveChromeOrApplistToFinalPosition(
   1849     bool is_chrome,
   1850     bool is_app_list,
   1851     int target_index,
   1852     int* chrome_index,
   1853     int* app_list_index) {
   1854   if (is_chrome && *chrome_index != -1) {
   1855     model_->Move(*chrome_index, target_index);
   1856     if (*app_list_index != -1 &&
   1857         *chrome_index < *app_list_index &&
   1858         target_index > *app_list_index)
   1859       --(*app_list_index);
   1860     *chrome_index = -1;
   1861   } else if (is_app_list && *app_list_index != -1) {
   1862     model_->Move(*app_list_index, target_index);
   1863     if (*chrome_index != -1 &&
   1864         *app_list_index < *chrome_index &&
   1865         target_index > *chrome_index)
   1866       --(*chrome_index);
   1867     *app_list_index = -1;
   1868   }
   1869 }
   1870 
   1871 int ChromeLauncherController::FindInsertionPoint(bool is_app_list) {
   1872   // Keeping this change small to backport to M33&32 (see crbug.com/329597).
   1873   // TODO(skuhne): With the removal of the legacy shelf layout we should remove
   1874   // the ability to move the app list item since this was never used. We should
   1875   // instead ask the ShelfModel::ValidateInsertionIndex or similir for an index.
   1876   if (is_app_list)
   1877     return 0;
   1878 
   1879   for (int i = model_->item_count() - 1; i > 0; --i) {
   1880     ash::ShelfItemType type = model_->items()[i].type;
   1881     if (type == ash::TYPE_APP_SHORTCUT ||
   1882         (is_app_list && type == ash::TYPE_APP_LIST) ||
   1883         type == ash::TYPE_BROWSER_SHORTCUT) {
   1884       return i;
   1885     }
   1886   }
   1887   return 0;
   1888 }
   1889 
   1890 int ChromeLauncherController::GetChromeIconIndexForCreation() {
   1891   // We get the list of pinned apps as they currently would get pinned.
   1892   // Within this list the chrome icon will be the correct location.
   1893   std::vector<std::string> pinned_apps = GetListOfPinnedAppsAndBrowser();
   1894 
   1895   std::vector<std::string>::iterator it =
   1896       std::find(pinned_apps.begin(),
   1897                 pinned_apps.end(),
   1898                 std::string(extension_misc::kChromeAppId));
   1899   DCHECK(it != pinned_apps.end());
   1900   int index = it - pinned_apps.begin();
   1901 
   1902   // We should do here a comparison between the is state and the "want to be"
   1903   // state since some apps might be able to pin but are not yet. Instead - for
   1904   // the time being we clamp against the amount of known items and wait for the
   1905   // next |UpdateAppLaunchersFromPref()| call to correct it - it will come since
   1906   // the pinning will be done then.
   1907   return std::min(model_->item_count(), index);
   1908 }
   1909 
   1910 std::vector<std::string>
   1911 ChromeLauncherController::GetListOfPinnedAppsAndBrowser() {
   1912   // Adding the app list item to the list of items requires that the ID is not
   1913   // a valid and known ID for the extension system. The ID was constructed that
   1914   // way - but just to make sure...
   1915   DCHECK(!app_tab_helper_->IsValidIDForCurrentUser(kAppShelfIdPlaceholder));
   1916 
   1917   std::vector<std::string> pinned_apps;
   1918 
   1919   // Get the new incarnation of the list.
   1920   const base::ListValue* pinned_apps_pref =
   1921       profile_->GetPrefs()->GetList(prefs::kPinnedLauncherApps);
   1922 
   1923   // Keep track of the addition of the chrome and the app list icon.
   1924   bool chrome_icon_added = false;
   1925   bool app_list_icon_added = false;
   1926   size_t chrome_icon_index = GetChromeIconIndexFromPref();
   1927 
   1928   // See if the chrome string is already in the pinned list and remove it if
   1929   // needed.
   1930   base::Value* chrome_app = ash::CreateAppDict(extension_misc::kChromeAppId);
   1931   if (chrome_app) {
   1932     chrome_icon_added = pinned_apps_pref->Find(*chrome_app) !=
   1933         pinned_apps_pref->end();
   1934     delete chrome_app;
   1935   }
   1936 
   1937   for (size_t index = 0; index < pinned_apps_pref->GetSize(); ++index) {
   1938     // We need to position the chrome icon relative to it's place in the pinned
   1939     // preference list - even if an item of that list isn't shown yet.
   1940     if (index == chrome_icon_index && !chrome_icon_added) {
   1941       pinned_apps.push_back(extension_misc::kChromeAppId);
   1942       chrome_icon_added = true;
   1943     }
   1944     const base::DictionaryValue* app = NULL;
   1945     std::string app_id;
   1946     if (pinned_apps_pref->GetDictionary(index, &app) &&
   1947         app->GetString(ash::kPinnedAppsPrefAppIDPath, &app_id) &&
   1948         (std::find(pinned_apps.begin(), pinned_apps.end(), app_id) ==
   1949              pinned_apps.end())) {
   1950       if (app_id == extension_misc::kChromeAppId) {
   1951         chrome_icon_added = true;
   1952         pinned_apps.push_back(extension_misc::kChromeAppId);
   1953       } else if (app_id == kAppShelfIdPlaceholder) {
   1954         app_list_icon_added = true;
   1955         pinned_apps.push_back(kAppShelfIdPlaceholder);
   1956       } else if (app_tab_helper_->IsValidIDForCurrentUser(app_id)) {
   1957         // Note: In multi profile scenarios we only want to show pinnable apps
   1958         // here which is correct. Running applications from the other users will
   1959         // continue to run. So no need for multi profile modifications.
   1960         pinned_apps.push_back(app_id);
   1961       }
   1962     }
   1963   }
   1964 
   1965   // If not added yet, the chrome item will be the last item in the list.
   1966   if (!chrome_icon_added)
   1967     pinned_apps.push_back(extension_misc::kChromeAppId);
   1968 
   1969   // If not added yet, add the app list item either at the end or at the
   1970   // beginning - depending on the shelf layout.
   1971   if (!app_list_icon_added) {
   1972     pinned_apps.insert(pinned_apps.begin(), kAppShelfIdPlaceholder);
   1973   }
   1974   return pinned_apps;
   1975 }
   1976 
   1977 bool ChromeLauncherController::IsIncognito(
   1978     const content::WebContents* web_contents) const {
   1979   const Profile* profile =
   1980       Profile::FromBrowserContext(web_contents->GetBrowserContext());
   1981   return profile->IsOffTheRecord() && !profile->IsGuestSession();
   1982 }
   1983 
   1984 void ChromeLauncherController::CloseWindowedAppsFromRemovedExtension(
   1985     const std::string& app_id) {
   1986   // This function cannot rely on the controller's enumeration functionality
   1987   // since the extension has already be unloaded.
   1988   const BrowserList* ash_browser_list =
   1989       BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
   1990   std::vector<Browser*> browser_to_close;
   1991   for (BrowserList::const_reverse_iterator
   1992            it = ash_browser_list->begin_last_active();
   1993        it != ash_browser_list->end_last_active(); ++it) {
   1994     Browser* browser = *it;
   1995     if (!browser->is_type_tabbed() &&
   1996         browser->is_type_popup() &&
   1997         browser->is_app() &&
   1998         app_id == web_app::GetExtensionIdFromApplicationName(
   1999             browser->app_name())) {
   2000       browser_to_close.push_back(browser);
   2001     }
   2002   }
   2003   while (!browser_to_close.empty()) {
   2004     TabStripModel* tab_strip = browser_to_close.back()->tab_strip_model();
   2005     tab_strip->CloseWebContentsAt(0, TabStripModel::CLOSE_NONE);
   2006     browser_to_close.pop_back();
   2007   }
   2008 }
   2009 
   2010 void ChromeLauncherController::SetShelfItemDelegate(
   2011     ash::ShelfID id,
   2012     ash::ShelfItemDelegate* item_delegate) {
   2013   DCHECK_GT(id, 0);
   2014   DCHECK(item_delegate);
   2015   DCHECK(item_delegate_manager_);
   2016   item_delegate_manager_->SetShelfItemDelegate(
   2017       id, scoped_ptr<ash::ShelfItemDelegate>(item_delegate).Pass());
   2018 }
   2019 
   2020 void ChromeLauncherController::AttachProfile(Profile* profile) {
   2021   profile_ = profile;
   2022   // Either add the profile to the list of known profiles and make it the active
   2023   // one for some functions of AppTabHelper or create a new one.
   2024   if (!app_tab_helper_.get())
   2025     app_tab_helper_.reset(new LauncherAppTabHelper(profile_));
   2026   else
   2027     app_tab_helper_->SetCurrentUser(profile_);
   2028   // TODO(skuhne): The AppIconLoaderImpl has the same problem. Each loaded
   2029   // image is associated with a profile (it's loader requires the profile).
   2030   // Since icon size changes are possible, the icon could be requested to be
   2031   // reloaded. However - having it not multi profile aware would cause problems
   2032   // if the icon cache gets deleted upon user switch.
   2033   app_icon_loader_.reset(new extensions::AppIconLoaderImpl(
   2034       profile_, extension_misc::EXTENSION_ICON_SMALL, this));
   2035 
   2036   pref_change_registrar_.Init(profile_->GetPrefs());
   2037   pref_change_registrar_.Add(
   2038       prefs::kPinnedLauncherApps,
   2039       base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref,
   2040                  base::Unretained(this)));
   2041   pref_change_registrar_.Add(
   2042       prefs::kShelfAlignmentLocal,
   2043       base::Bind(&ChromeLauncherController::SetShelfAlignmentFromPrefs,
   2044                  base::Unretained(this)));
   2045   pref_change_registrar_.Add(
   2046       prefs::kShelfAutoHideBehaviorLocal,
   2047       base::Bind(&ChromeLauncherController::
   2048                      SetShelfAutoHideBehaviorFromPrefs,
   2049                  base::Unretained(this)));
   2050   pref_change_registrar_.Add(
   2051       prefs::kShelfPreferences,
   2052       base::Bind(&ChromeLauncherController::SetShelfBehaviorsFromPrefs,
   2053                  base::Unretained(this)));
   2054 #if defined(OS_CHROMEOS)
   2055   pref_change_registrar_.Add(
   2056       prefs::kTouchVirtualKeyboardEnabled,
   2057       base::Bind(&ChromeLauncherController::SetVirtualKeyboardBehaviorFromPrefs,
   2058                  base::Unretained(this)));
   2059 #endif  // defined(OS_CHROMEOS)
   2060 }
   2061 
   2062 void ChromeLauncherController::ReleaseProfile() {
   2063   if (app_sync_ui_state_)
   2064     app_sync_ui_state_->RemoveObserver(this);
   2065 
   2066   PrefServiceSyncable::FromProfile(profile_)->RemoveObserver(this);
   2067 
   2068   pref_change_registrar_.RemoveAll();
   2069 }
   2070