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