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