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