Elim cr-checkbox
[chromium-blink-merge.git] / chrome / browser / ui / ash / launcher / chrome_launcher_controller.cc
blob69567f1a7137e5900ba77694cb712df0165ebab6
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_util.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 "components/syncable_prefs/pref_service_syncable.h"
72 #include "content/public/browser/navigation_entry.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 DCHECK(profile);
209 // Don't show the shelf in app mode.
210 if (chrome::IsRunningInAppMode())
211 return ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN;
213 // See comment in |kShelfAlignment| as to why we consider two prefs.
214 const std::string behavior_value(
215 GetPrefForRootWindow(profile->GetPrefs(),
216 root_window,
217 prefs::kShelfAutoHideBehaviorLocal,
218 prefs::kShelfAutoHideBehavior));
220 // Note: To maintain sync compatibility with old images of chrome/chromeos
221 // the set of values that may be encountered includes the now-extinct
222 // "Default" as well as "Never" and "Always", "Default" should now
223 // be treated as "Never" (http://crbug.com/146773).
224 if (behavior_value == ash::kShelfAutoHideBehaviorAlways)
225 return ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS;
226 return ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER;
229 // Gets the shelf alignment from prefs for a root window.
230 ash::ShelfAlignment GetShelfAlignmentFromPrefs(Profile* profile,
231 aura::Window* root_window) {
232 DCHECK(profile);
234 // See comment in |kShelfAlignment| as to why we consider two prefs.
235 const std::string alignment_value(
236 GetPrefForRootWindow(profile->GetPrefs(),
237 root_window,
238 prefs::kShelfAlignmentLocal,
239 prefs::kShelfAlignment));
240 if (alignment_value == ash::kShelfAlignmentLeft)
241 return ash::SHELF_ALIGNMENT_LEFT;
242 else if (alignment_value == ash::kShelfAlignmentRight)
243 return ash::SHELF_ALIGNMENT_RIGHT;
244 else if (alignment_value == ash::kShelfAlignmentTop)
245 return ash::SHELF_ALIGNMENT_TOP;
246 return ash::SHELF_ALIGNMENT_BOTTOM;
249 // If prefs have synced and no user-set value exists at |local_path|, the value
250 // from |synced_path| is copied to |local_path|.
251 void MaybePropagatePrefToLocal(
252 syncable_prefs::PrefServiceSyncable* pref_service,
253 const char* local_path,
254 const char* synced_path) {
255 if (!pref_service->FindPreference(local_path)->HasUserSetting() &&
256 pref_service->IsSyncing()) {
257 // First time the user is using this machine, propagate from remote to
258 // local.
259 pref_service->SetString(local_path, pref_service->GetString(synced_path));
263 std::string GetSourceFromAppListSource(ash::LaunchSource source) {
264 switch (source) {
265 case ash::LAUNCH_FROM_APP_LIST:
266 return std::string(extension_urls::kLaunchSourceAppList);
267 case ash::LAUNCH_FROM_APP_LIST_SEARCH:
268 return std::string(extension_urls::kLaunchSourceAppListSearch);
269 default: return std::string();
273 } // namespace
275 #if defined(OS_CHROMEOS)
276 // A class to get events from ChromeOS when a user gets changed or added.
277 class ChromeLauncherControllerUserSwitchObserver
278 : public user_manager::UserManager::UserSessionStateObserver {
279 public:
280 ChromeLauncherControllerUserSwitchObserver(
281 ChromeLauncherController* controller)
282 : controller_(controller) {
283 DCHECK(user_manager::UserManager::IsInitialized());
284 user_manager::UserManager::Get()->AddSessionStateObserver(this);
286 ~ChromeLauncherControllerUserSwitchObserver() override {
287 user_manager::UserManager::Get()->RemoveSessionStateObserver(this);
290 // user_manager::UserManager::UserSessionStateObserver overrides:
291 void UserAddedToSession(const user_manager::User* added_user) override;
293 // ChromeLauncherControllerUserSwitchObserver:
294 void OnUserProfileReadyToSwitch(Profile* profile);
296 private:
297 // Add a user to the session.
298 void AddUser(Profile* profile);
300 // The owning ChromeLauncherController.
301 ChromeLauncherController* controller_;
303 // Users which were just added to the system, but which profiles were not yet
304 // (fully) loaded.
305 std::set<std::string> added_user_ids_waiting_for_profiles_;
307 DISALLOW_COPY_AND_ASSIGN(ChromeLauncherControllerUserSwitchObserver);
310 void ChromeLauncherControllerUserSwitchObserver::UserAddedToSession(
311 const user_manager::User* active_user) {
312 Profile* profile = multi_user_util::GetProfileFromUserID(
313 active_user->email());
314 // If we do not have a profile yet, we postpone forwarding the notification
315 // until it is loaded.
316 if (!profile)
317 added_user_ids_waiting_for_profiles_.insert(active_user->email());
318 else
319 AddUser(profile);
322 void ChromeLauncherControllerUserSwitchObserver::OnUserProfileReadyToSwitch(
323 Profile* profile) {
324 if (!added_user_ids_waiting_for_profiles_.empty()) {
325 // Check if the profile is from a user which was on the waiting list.
326 std::string user_id = multi_user_util::GetUserIDFromProfile(profile);
327 std::set<std::string>::iterator it = std::find(
328 added_user_ids_waiting_for_profiles_.begin(),
329 added_user_ids_waiting_for_profiles_.end(),
330 user_id);
331 if (it != added_user_ids_waiting_for_profiles_.end()) {
332 added_user_ids_waiting_for_profiles_.erase(it);
333 AddUser(profile->GetOriginalProfile());
338 void ChromeLauncherControllerUserSwitchObserver::AddUser(Profile* profile) {
339 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
340 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED)
341 chrome::MultiUserWindowManager::GetInstance()->AddUser(profile);
342 controller_->AdditionalUserAddedToSession(profile->GetOriginalProfile());
344 #endif
346 ChromeLauncherController::ChromeLauncherController(Profile* profile,
347 ash::ShelfModel* model)
348 : model_(model),
349 item_delegate_manager_(NULL),
350 profile_(profile),
351 app_sync_ui_state_(NULL),
352 ignore_persist_pinned_state_change_(false) {
353 if (!profile_) {
354 // If no profile was passed, we take the currently active profile and use it
355 // as the owner of the current desktop.
356 // Use the original profile as on chromeos we may get a temporary off the
357 // record profile, unless in guest session (where off the record profile is
358 // the right one).
359 profile_ = ProfileManager::GetActiveUserProfile();
360 if (!profile_->IsGuestSession() && !profile_->IsSystemProfile())
361 profile_ = profile_->GetOriginalProfile();
363 app_sync_ui_state_ = AppSyncUIState::Get(profile_);
364 if (app_sync_ui_state_)
365 app_sync_ui_state_->AddObserver(this);
368 // All profile relevant settings get bound to the current profile.
369 AttachProfile(profile_);
370 model_->AddObserver(this);
372 // In multi profile mode we might have a window manager. We try to create it
373 // here. If the instantiation fails, the manager is not needed.
374 chrome::MultiUserWindowManager::CreateInstance();
376 #if defined(OS_CHROMEOS)
377 // On Chrome OS using multi profile we want to switch the content of the shelf
378 // with a user change. Note that for unit tests the instance can be NULL.
379 if (chrome::MultiUserWindowManager::GetMultiProfileMode() !=
380 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_OFF) {
381 user_switch_observer_.reset(
382 new ChromeLauncherControllerUserSwitchObserver(this));
385 // Create our v1/v2 application / browser monitors which will inform the
386 // launcher of status changes.
387 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
388 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED) {
389 // If running in separated destkop mode, we create the multi profile version
390 // of status monitor.
391 browser_status_monitor_.reset(new MultiProfileBrowserStatusMonitor(this));
392 app_window_controller_.reset(
393 new MultiProfileAppWindowLauncherController(this));
394 } else {
395 // Create our v1/v2 application / browser monitors which will inform the
396 // launcher of status changes.
397 browser_status_monitor_.reset(new BrowserStatusMonitor(this));
398 app_window_controller_.reset(new AppWindowLauncherController(this));
400 #else
401 // Create our v1/v2 application / browser monitors which will inform the
402 // launcher of status changes.
403 browser_status_monitor_.reset(new BrowserStatusMonitor(this));
404 app_window_controller_.reset(new AppWindowLauncherController(this));
405 #endif
407 // Right now ash::Shell isn't created for tests.
408 // TODO(mukai): Allows it to observe display change and write tests.
409 if (ash::Shell::HasInstance()) {
410 ash::Shell::GetInstance()->window_tree_host_manager()->AddObserver(this);
411 // If it got already set, we remove the observer first again and swap the
412 // ItemDelegateManager.
413 if (item_delegate_manager_)
414 item_delegate_manager_->RemoveObserver(this);
415 item_delegate_manager_ =
416 ash::Shell::GetInstance()->shelf_item_delegate_manager();
417 item_delegate_manager_->AddObserver(this);
421 ChromeLauncherController::~ChromeLauncherController() {
422 if (item_delegate_manager_)
423 item_delegate_manager_->RemoveObserver(this);
425 // Reset the BrowserStatusMonitor as it has a weak pointer to this.
426 browser_status_monitor_.reset();
428 // Reset the app window controller here since it has a weak pointer to this.
429 app_window_controller_.reset();
431 for (std::set<ash::Shelf*>::iterator iter = shelves_.begin();
432 iter != shelves_.end();
433 ++iter)
434 (*iter)->shelf_widget()->shelf_layout_manager()->RemoveObserver(this);
436 model_->RemoveObserver(this);
437 if (ash::Shell::HasInstance())
438 ash::Shell::GetInstance()->window_tree_host_manager()->RemoveObserver(this);
439 for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin();
440 i != id_to_item_controller_map_.end(); ++i) {
441 int index = model_->ItemIndexByID(i->first);
442 // A "browser proxy" is not known to the model and this removal does
443 // therefore not need to be propagated to the model.
444 if (index != -1 &&
445 model_->items()[index].type != ash::TYPE_BROWSER_SHORTCUT)
446 model_->RemoveItemAt(index);
449 if (ash::Shell::HasInstance())
450 ash::Shell::GetInstance()->RemoveShellObserver(this);
452 // Release all profile dependent resources.
453 ReleaseProfile();
454 if (instance_ == this)
455 instance_ = NULL;
457 // Get rid of the multi user window manager instance.
458 chrome::MultiUserWindowManager::DeleteInstance();
461 // static
462 ChromeLauncherController* ChromeLauncherController::CreateInstance(
463 Profile* profile,
464 ash::ShelfModel* model) {
465 // We do not check here for re-creation of the ChromeLauncherController since
466 // it appears that it might be intentional that the ChromeLauncherController
467 // can be re-created.
468 instance_ = new ChromeLauncherController(profile, model);
469 return instance_;
472 void ChromeLauncherController::Init() {
473 CreateBrowserShortcutLauncherItem();
474 UpdateAppLaunchersFromPref();
476 // TODO(sky): update unit test so that this test isn't necessary.
477 if (ash::Shell::HasInstance()) {
478 SetShelfAutoHideBehaviorFromPrefs();
479 SetShelfAlignmentFromPrefs();
480 #if defined(OS_CHROMEOS)
481 SetVirtualKeyboardBehaviorFromPrefs();
482 #endif // defined(OS_CHROMEOS)
483 syncable_prefs::PrefServiceSyncable* prefs =
484 PrefServiceSyncableFromProfile(profile_);
485 if (!prefs->FindPreference(prefs::kShelfAlignmentLocal)->HasUserSetting() ||
486 !prefs->FindPreference(prefs::kShelfAutoHideBehaviorLocal)->
487 HasUserSetting()) {
488 // This causes OnIsSyncingChanged to be called when the value of
489 // PrefService::IsSyncing() changes.
490 prefs->AddObserver(this);
492 ash::Shell::GetInstance()->AddShellObserver(this);
496 ash::ShelfID ChromeLauncherController::CreateAppLauncherItem(
497 LauncherItemController* controller,
498 const std::string& app_id,
499 ash::ShelfItemStatus status) {
500 CHECK(controller);
501 int index = 0;
502 // Panels are inserted on the left so as not to push all existing panels over.
503 if (controller->GetShelfItemType() != ash::TYPE_APP_PANEL)
504 index = model_->item_count();
505 return InsertAppLauncherItem(controller,
506 app_id,
507 status,
508 index,
509 controller->GetShelfItemType());
512 void ChromeLauncherController::SetItemStatus(ash::ShelfID id,
513 ash::ShelfItemStatus status) {
514 int index = model_->ItemIndexByID(id);
515 ash::ShelfItemStatus old_status = model_->items()[index].status;
516 // Since ordinary browser windows are not registered, we might get a negative
517 // index here.
518 if (index >= 0 && old_status != status) {
519 ash::ShelfItem item = model_->items()[index];
520 item.status = status;
521 model_->Set(index, item);
525 void ChromeLauncherController::SetItemController(
526 ash::ShelfID id,
527 LauncherItemController* controller) {
528 CHECK(controller);
529 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
530 CHECK(iter != id_to_item_controller_map_.end());
531 controller->set_shelf_id(id);
532 iter->second = controller;
533 // Existing controller is destroyed and replaced by registering again.
534 SetShelfItemDelegate(id, controller);
537 void ChromeLauncherController::CloseLauncherItem(ash::ShelfID id) {
538 CHECK(id);
539 if (IsPinned(id)) {
540 // Create a new shortcut controller.
541 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
542 CHECK(iter != id_to_item_controller_map_.end());
543 SetItemStatus(id, ash::STATUS_CLOSED);
544 std::string app_id = iter->second->app_id();
545 iter->second = new AppShortcutLauncherItemController(app_id, this);
546 iter->second->set_shelf_id(id);
547 // Existing controller is destroyed and replaced by registering again.
548 SetShelfItemDelegate(id, iter->second);
549 } else {
550 LauncherItemClosed(id);
554 void ChromeLauncherController::Pin(ash::ShelfID id) {
555 DCHECK(HasShelfIDToAppIDMapping(id));
557 int index = model_->ItemIndexByID(id);
558 DCHECK_GE(index, 0);
560 ash::ShelfItem item = model_->items()[index];
562 if (item.type == ash::TYPE_PLATFORM_APP ||
563 item.type == ash::TYPE_WINDOWED_APP) {
564 item.type = ash::TYPE_APP_SHORTCUT;
565 model_->Set(index, item);
566 } else if (item.type != ash::TYPE_APP_SHORTCUT) {
567 return;
570 if (CanPin())
571 PersistPinnedState();
574 void ChromeLauncherController::Unpin(ash::ShelfID id) {
575 LauncherItemController* controller = GetLauncherItemController(id);
576 CHECK(controller);
578 if (controller->type() == LauncherItemController::TYPE_APP ||
579 controller->locked()) {
580 UnpinRunningAppInternal(model_->ItemIndexByID(id));
581 } else {
582 LauncherItemClosed(id);
584 if (CanPin())
585 PersistPinnedState();
588 bool ChromeLauncherController::IsPinned(ash::ShelfID id) {
589 int index = model_->ItemIndexByID(id);
590 if (index < 0)
591 return false;
592 ash::ShelfItemType type = model_->items()[index].type;
593 return (type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_BROWSER_SHORTCUT);
596 void ChromeLauncherController::TogglePinned(ash::ShelfID id) {
597 if (!HasShelfIDToAppIDMapping(id))
598 return; // May happen if item closed with menu open.
600 if (IsPinned(id))
601 Unpin(id);
602 else
603 Pin(id);
606 bool ChromeLauncherController::IsPinnable(ash::ShelfID id) const {
607 int index = model_->ItemIndexByID(id);
608 if (index == -1)
609 return false;
611 ash::ShelfItemType type = model_->items()[index].type;
612 return ((type == ash::TYPE_APP_SHORTCUT ||
613 type == ash::TYPE_PLATFORM_APP ||
614 type == ash::TYPE_WINDOWED_APP) &&
615 CanPin());
618 void ChromeLauncherController::LockV1AppWithID(const std::string& app_id) {
619 ash::ShelfID id = GetShelfIDForAppID(app_id);
620 if (!IsPinned(id) && !IsWindowedAppInLauncher(app_id)) {
621 CreateAppShortcutLauncherItemWithType(app_id,
622 model_->item_count(),
623 ash::TYPE_WINDOWED_APP);
624 id = GetShelfIDForAppID(app_id);
626 CHECK(id);
627 id_to_item_controller_map_[id]->lock();
630 void ChromeLauncherController::UnlockV1AppWithID(const std::string& app_id) {
631 ash::ShelfID id = GetShelfIDForAppID(app_id);
632 CHECK(id);
633 CHECK(IsPinned(id) || IsWindowedAppInLauncher(app_id));
634 LauncherItemController* controller = id_to_item_controller_map_[id];
635 controller->unlock();
636 if (!controller->locked() && !IsPinned(id))
637 CloseLauncherItem(id);
640 void ChromeLauncherController::Launch(ash::ShelfID id, int event_flags) {
641 LauncherItemController* controller = GetLauncherItemController(id);
642 if (!controller)
643 return; // In case invoked from menu and item closed while menu up.
644 controller->Launch(ash::LAUNCH_FROM_UNKNOWN, event_flags);
647 void ChromeLauncherController::Close(ash::ShelfID id) {
648 LauncherItemController* controller = GetLauncherItemController(id);
649 if (!controller)
650 return; // May happen if menu closed.
651 controller->Close();
654 bool ChromeLauncherController::IsOpen(ash::ShelfID id) {
655 LauncherItemController* controller = GetLauncherItemController(id);
656 if (!controller)
657 return false;
658 return controller->IsOpen();
661 bool ChromeLauncherController::IsPlatformApp(ash::ShelfID id) {
662 if (!HasShelfIDToAppIDMapping(id))
663 return false;
665 std::string app_id = GetAppIDForShelfID(id);
666 const Extension* extension = GetExtensionForAppID(app_id);
667 // An extension can be synced / updated at any time and therefore not be
668 // available.
669 return extension ? extension->is_platform_app() : false;
672 void ChromeLauncherController::LaunchApp(const std::string& app_id,
673 ash::LaunchSource source,
674 int event_flags) {
675 // |extension| could be NULL when it is being unloaded for updating.
676 const Extension* extension = GetExtensionForAppID(app_id);
677 if (!extension)
678 return;
680 if (!extensions::util::IsAppLaunchableWithoutEnabling(app_id, profile_)) {
681 // Do nothing if there is already a running enable flow.
682 if (extension_enable_flow_)
683 return;
685 extension_enable_flow_.reset(
686 new ExtensionEnableFlow(profile_, app_id, this));
687 extension_enable_flow_->StartForNativeWindow(NULL);
688 return;
691 #if defined(OS_WIN)
692 if (LaunchedInNativeDesktop(app_id))
693 return;
694 #endif
696 // The app will be created for the currently active profile.
697 AppLaunchParams params(
698 profile_, extension, ui::DispositionFromEventFlags(event_flags),
699 chrome::HOST_DESKTOP_TYPE_ASH, extensions::SOURCE_APP_LAUNCHER);
700 if (source != ash::LAUNCH_FROM_UNKNOWN &&
701 app_id == extensions::kWebStoreAppId) {
702 // Get the corresponding source string.
703 std::string source_value = GetSourceFromAppListSource(source);
705 // Set an override URL to include the source.
706 GURL extension_url = extensions::AppLaunchInfo::GetFullLaunchURL(extension);
707 params.override_url = net::AppendQueryParameter(
708 extension_url, extension_urls::kWebstoreSourceField, source_value);
711 OpenApplication(params);
714 void ChromeLauncherController::ActivateApp(const std::string& app_id,
715 ash::LaunchSource source,
716 int event_flags) {
717 // If there is an existing non-shortcut controller for this app, open it.
718 ash::ShelfID id = GetShelfIDForAppID(app_id);
719 if (id) {
720 LauncherItemController* controller = GetLauncherItemController(id);
721 controller->Activate(source);
722 return;
725 // Create a temporary application launcher item and use it to see if there are
726 // running instances.
727 scoped_ptr<AppShortcutLauncherItemController> app_controller(
728 new AppShortcutLauncherItemController(app_id, this));
729 if (!app_controller->GetRunningApplications().empty())
730 app_controller->Activate(source);
731 else
732 LaunchApp(app_id, source, event_flags);
735 extensions::LaunchType ChromeLauncherController::GetLaunchType(
736 ash::ShelfID id) {
737 const Extension* extension = GetExtensionForAppID(GetAppIDForShelfID(id));
739 // An extension can be unloaded/updated/unavailable at any time.
740 if (!extension)
741 return extensions::LAUNCH_TYPE_DEFAULT;
743 return extensions::GetLaunchType(extensions::ExtensionPrefs::Get(profile_),
744 extension);
747 ash::ShelfID ChromeLauncherController::GetShelfIDForAppID(
748 const std::string& app_id) {
749 for (IDToItemControllerMap::const_iterator i =
750 id_to_item_controller_map_.begin();
751 i != id_to_item_controller_map_.end(); ++i) {
752 if (i->second->type() == LauncherItemController::TYPE_APP_PANEL)
753 continue; // Don't include panels
754 if (i->second->app_id() == app_id)
755 return i->first;
757 return 0;
760 bool ChromeLauncherController::HasShelfIDToAppIDMapping(ash::ShelfID id) const {
761 return id_to_item_controller_map_.find(id) !=
762 id_to_item_controller_map_.end();
765 const std::string& ChromeLauncherController::GetAppIDForShelfID(
766 ash::ShelfID id) {
767 LauncherItemController* controller = GetLauncherItemController(id);
768 CHECK(controller);
769 return controller->app_id();
772 void ChromeLauncherController::SetAppImage(const std::string& id,
773 const gfx::ImageSkia& image) {
774 // TODO: need to get this working for shortcuts.
775 for (IDToItemControllerMap::const_iterator i =
776 id_to_item_controller_map_.begin();
777 i != id_to_item_controller_map_.end(); ++i) {
778 LauncherItemController* controller = i->second;
779 if (controller->app_id() != id)
780 continue;
781 if (controller->image_set_by_controller())
782 continue;
783 int index = model_->ItemIndexByID(i->first);
784 if (index == -1)
785 continue;
786 ash::ShelfItem item = model_->items()[index];
787 item.image = image;
788 model_->Set(index, item);
789 // It's possible we're waiting on more than one item, so don't break.
793 void ChromeLauncherController::OnAutoHideBehaviorChanged(
794 aura::Window* root_window,
795 ash::ShelfAutoHideBehavior new_behavior) {
796 SetShelfAutoHideBehaviorPrefs(new_behavior, root_window);
799 void ChromeLauncherController::SetLauncherItemImage(
800 ash::ShelfID shelf_id,
801 const gfx::ImageSkia& image) {
802 int index = model_->ItemIndexByID(shelf_id);
803 if (index == -1)
804 return;
805 ash::ShelfItem item = model_->items()[index];
806 item.image = image;
807 model_->Set(index, item);
810 bool ChromeLauncherController::CanPin() const {
811 const PrefService::Preference* pref =
812 profile_->GetPrefs()->FindPreference(prefs::kPinnedLauncherApps);
813 return pref && pref->IsUserModifiable();
816 bool ChromeLauncherController::IsAppPinned(const std::string& app_id) {
817 for (IDToItemControllerMap::const_iterator i =
818 id_to_item_controller_map_.begin();
819 i != id_to_item_controller_map_.end(); ++i) {
820 if (IsPinned(i->first) && i->second->app_id() == app_id)
821 return true;
823 return false;
826 bool ChromeLauncherController::IsWindowedAppInLauncher(
827 const std::string& app_id) {
828 int index = model_->ItemIndexByID(GetShelfIDForAppID(app_id));
829 if (index < 0)
830 return false;
832 ash::ShelfItemType type = model_->items()[index].type;
833 return type == ash::TYPE_WINDOWED_APP;
836 void ChromeLauncherController::PinAppWithID(const std::string& app_id) {
837 if (CanPin())
838 DoPinAppWithID(app_id);
839 else
840 NOTREACHED();
843 void ChromeLauncherController::SetLaunchType(
844 ash::ShelfID id,
845 extensions::LaunchType launch_type) {
846 LauncherItemController* controller = GetLauncherItemController(id);
847 if (!controller)
848 return;
850 extensions::SetLaunchType(profile_, controller->app_id(), launch_type);
853 void ChromeLauncherController::UnpinAppWithID(const std::string& app_id) {
854 if (CanPin())
855 DoUnpinAppWithID(app_id);
856 else
857 NOTREACHED();
860 void ChromeLauncherController::OnSetShelfItemDelegate(
861 ash::ShelfID id,
862 ash::ShelfItemDelegate* item_delegate) {
863 // TODO(skuhne): This fixes crbug.com/429870, but it does not answer why we
864 // get into this state in the first place.
865 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
866 if (iter == id_to_item_controller_map_.end() || item_delegate == iter->second)
867 return;
868 LOG(ERROR) << "Unexpected change of shelf item id: " << id;
869 id_to_item_controller_map_.erase(iter);
872 bool ChromeLauncherController::IsLoggedInAsGuest() {
873 return profile_->IsGuestSession();
876 void ChromeLauncherController::CreateNewWindow() {
877 // Use the currently active user.
878 chrome::NewEmptyWindow(profile_, chrome::HOST_DESKTOP_TYPE_ASH);
881 void ChromeLauncherController::CreateNewIncognitoWindow() {
882 // Use the currently active user.
883 chrome::NewEmptyWindow(profile_->GetOffTheRecordProfile(),
884 chrome::HOST_DESKTOP_TYPE_ASH);
887 void ChromeLauncherController::PersistPinnedState() {
888 if (ignore_persist_pinned_state_change_)
889 return;
890 // It is a coding error to call PersistPinnedState() if the pinned apps are
891 // not user-editable. The code should check earlier and not perform any
892 // modification actions that trigger persisting the state.
893 if (!CanPin()) {
894 NOTREACHED() << "Can't pin but pinned state being updated";
895 return;
897 // Mutating kPinnedLauncherApps is going to notify us and trigger us to
898 // process the change. We don't want that to happen so remove ourselves as a
899 // listener.
900 pref_change_registrar_.Remove(prefs::kPinnedLauncherApps);
902 ListPrefUpdate updater(profile_->GetPrefs(), prefs::kPinnedLauncherApps);
903 updater->Clear();
904 for (size_t i = 0; i < model_->items().size(); ++i) {
905 if (model_->items()[i].type == ash::TYPE_APP_SHORTCUT) {
906 ash::ShelfID id = model_->items()[i].id;
907 LauncherItemController* controller = GetLauncherItemController(id);
908 if (controller && IsPinned(id)) {
909 base::DictionaryValue* app_value = ash::CreateAppDict(
910 controller->app_id());
911 if (app_value)
912 updater->Append(app_value);
914 } else if (model_->items()[i].type == ash::TYPE_BROWSER_SHORTCUT) {
915 PersistChromeItemIndex(i);
916 } else if (model_->items()[i].type == ash::TYPE_APP_LIST) {
917 base::DictionaryValue* app_value = ash::CreateAppDict(
918 kAppShelfIdPlaceholder);
919 if (app_value)
920 updater->Append(app_value);
924 pref_change_registrar_.Add(
925 prefs::kPinnedLauncherApps,
926 base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref,
927 base::Unretained(this)));
930 ash::ShelfModel* ChromeLauncherController::model() {
931 return model_;
934 Profile* ChromeLauncherController::profile() {
935 return profile_;
938 ash::ShelfAutoHideBehavior ChromeLauncherController::GetShelfAutoHideBehavior(
939 aura::Window* root_window) const {
940 return GetShelfAutoHideBehaviorFromPrefs(profile_, root_window);
943 bool ChromeLauncherController::CanUserModifyShelfAutoHideBehavior(
944 aura::Window* root_window) const {
945 #if defined(OS_WIN)
946 // Disable shelf auto-hide behavior on screen sides in Metro mode.
947 if (ash::Shell::GetInstance()->GetShelfAlignment(root_window) !=
948 ash::SHELF_ALIGNMENT_BOTTOM) {
949 return false;
951 #endif
952 return profile_->GetPrefs()->
953 FindPreference(prefs::kShelfAutoHideBehaviorLocal)->IsUserModifiable();
956 void ChromeLauncherController::ToggleShelfAutoHideBehavior(
957 aura::Window* root_window) {
958 ash::ShelfAutoHideBehavior behavior = GetShelfAutoHideBehavior(root_window) ==
959 ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS ?
960 ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER :
961 ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS;
962 SetShelfAutoHideBehaviorPrefs(behavior, root_window);
963 return;
966 void ChromeLauncherController::UpdateAppState(content::WebContents* contents,
967 AppState app_state) {
968 std::string app_id = app_tab_helper_->GetAppID(contents);
970 // Check if the gMail app is loaded and it matches the given content.
971 // This special treatment is needed to address crbug.com/234268.
972 if (app_id.empty() && ContentCanBeHandledByGmailApp(contents))
973 app_id = kGmailAppId;
975 // Check the old |app_id| for a tab. If the contents has changed we need to
976 // remove it from the previous app.
977 if (web_contents_to_app_id_.find(contents) != web_contents_to_app_id_.end()) {
978 std::string last_app_id = web_contents_to_app_id_[contents];
979 if (last_app_id != app_id) {
980 ash::ShelfID id = GetShelfIDForAppID(last_app_id);
981 if (id) {
982 // Since GetAppState() will use |web_contents_to_app_id_| we remove
983 // the connection before calling it.
984 web_contents_to_app_id_.erase(contents);
985 SetItemStatus(id, GetAppState(last_app_id));
990 if (app_state == APP_STATE_REMOVED)
991 web_contents_to_app_id_.erase(contents);
992 else
993 web_contents_to_app_id_[contents] = app_id;
995 ash::ShelfID id = GetShelfIDForAppID(app_id);
996 if (id) {
997 SetItemStatus(id, (app_state == APP_STATE_WINDOW_ACTIVE ||
998 app_state == APP_STATE_ACTIVE) ? ash::STATUS_ACTIVE :
999 GetAppState(app_id));
1003 ash::ShelfID ChromeLauncherController::GetShelfIDForWebContents(
1004 content::WebContents* contents) {
1005 DCHECK(contents);
1007 std::string app_id = app_tab_helper_->GetAppID(contents);
1009 if (app_id.empty() && ContentCanBeHandledByGmailApp(contents))
1010 app_id = kGmailAppId;
1012 ash::ShelfID id = GetShelfIDForAppID(app_id);
1014 if (app_id.empty() || !id) {
1015 int browser_index = model_->GetItemIndexForType(ash::TYPE_BROWSER_SHORTCUT);
1016 return model_->items()[browser_index].id;
1019 return id;
1022 void ChromeLauncherController::SetRefocusURLPatternForTest(ash::ShelfID id,
1023 const GURL& url) {
1024 LauncherItemController* controller = GetLauncherItemController(id);
1025 DCHECK(controller);
1027 int index = model_->ItemIndexByID(id);
1028 if (index == -1) {
1029 NOTREACHED() << "Invalid launcher id";
1030 return;
1033 ash::ShelfItemType type = model_->items()[index].type;
1034 if (type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_WINDOWED_APP) {
1035 AppShortcutLauncherItemController* app_controller =
1036 static_cast<AppShortcutLauncherItemController*>(controller);
1037 app_controller->set_refocus_url(url);
1038 } else {
1039 NOTREACHED() << "Invalid launcher type";
1043 const Extension* ChromeLauncherController::GetExtensionForAppID(
1044 const std::string& app_id) const {
1045 return extensions::ExtensionRegistry::Get(profile_)->GetExtensionById(
1046 app_id, extensions::ExtensionRegistry::EVERYTHING);
1049 ash::ShelfItemDelegate::PerformedAction
1050 ChromeLauncherController::ActivateWindowOrMinimizeIfActive(
1051 ui::BaseWindow* window,
1052 bool allow_minimize) {
1053 // In separated desktop mode we might have to teleport a window back to the
1054 // current user.
1055 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
1056 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED) {
1057 aura::Window* native_window = window->GetNativeWindow();
1058 const std::string& current_user =
1059 multi_user_util::GetUserIDFromProfile(profile());
1060 chrome::MultiUserWindowManager* manager =
1061 chrome::MultiUserWindowManager::GetInstance();
1062 if (!manager->IsWindowOnDesktopOfUser(native_window, current_user)) {
1063 ash::MultiProfileUMA::RecordTeleportAction(
1064 ash::MultiProfileUMA::TELEPORT_WINDOW_RETURN_BY_LAUNCHER);
1065 manager->ShowWindowForUser(native_window, current_user);
1066 window->Activate();
1067 return ash::ShelfItemDelegate::kExistingWindowActivated;
1071 if (window->IsActive() && allow_minimize) {
1072 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1073 switches::kDisableMinimizeOnSecondLauncherItemClick)) {
1074 AnimateWindow(window->GetNativeWindow(),
1075 wm::WINDOW_ANIMATION_TYPE_BOUNCE);
1076 } else {
1077 window->Minimize();
1078 return ash::ShelfItemDelegate::kExistingWindowMinimized;
1080 } else {
1081 window->Show();
1082 window->Activate();
1083 return ash::ShelfItemDelegate::kExistingWindowActivated;
1085 return ash::ShelfItemDelegate::kNoAction;
1088 void ChromeLauncherController::OnShelfCreated(ash::Shelf* shelf) {
1089 shelves_.insert(shelf);
1090 shelf->shelf_widget()->shelf_layout_manager()->AddObserver(this);
1093 void ChromeLauncherController::OnShelfDestroyed(ash::Shelf* shelf) {
1094 shelves_.erase(shelf);
1095 // RemoveObserver is not called here, since by the time this method is called
1096 // Shelf is already in its destructor.
1099 void ChromeLauncherController::ShelfItemAdded(int index) {
1100 // The app list launcher can get added to the shelf after we applied the
1101 // preferences. In that case the item might be at the wrong spot. As such we
1102 // call the function again.
1103 if (model_->items()[index].type == ash::TYPE_APP_LIST)
1104 UpdateAppLaunchersFromPref();
1107 void ChromeLauncherController::ShelfItemRemoved(int index, ash::ShelfID id) {
1108 // TODO(skuhne): This fixes crbug.com/429870, but it does not answer why we
1109 // get into this state in the first place.
1110 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
1111 if (iter == id_to_item_controller_map_.end())
1112 return;
1114 LOG(ERROR) << "Unexpected change of shelf item id: " << id;
1116 id_to_item_controller_map_.erase(iter);
1119 void ChromeLauncherController::ShelfItemMoved(int start_index,
1120 int target_index) {
1121 const ash::ShelfItem& item = model_->items()[target_index];
1122 // We remember the moved item position if it is either pinnable or
1123 // it is the app list with the alternate shelf layout.
1124 if ((HasShelfIDToAppIDMapping(item.id) && IsPinned(item.id)) ||
1125 item.type == ash::TYPE_APP_LIST)
1126 PersistPinnedState();
1129 void ChromeLauncherController::ShelfItemChanged(
1130 int index,
1131 const ash::ShelfItem& old_item) {
1134 void ChromeLauncherController::ShelfStatusChanged() {
1137 void ChromeLauncherController::ActiveUserChanged(
1138 const std::string& user_email) {
1139 // Store the order of running applications for the user which gets inactive.
1140 RememberUnpinnedRunningApplicationOrder();
1141 // Coming here the default profile is already switched. All profile specific
1142 // resources get released and the new profile gets attached instead.
1143 ReleaseProfile();
1144 // When coming here, the active user has already be changed so that we can
1145 // set it as active.
1146 AttachProfile(ProfileManager::GetActiveUserProfile());
1147 // Update the V1 applications.
1148 browser_status_monitor_->ActiveUserChanged(user_email);
1149 // Switch the running applications to the new user.
1150 app_window_controller_->ActiveUserChanged(user_email);
1151 // Update the user specific shell properties from the new user profile.
1152 UpdateAppLaunchersFromPref();
1153 SetShelfAlignmentFromPrefs();
1154 SetShelfAutoHideBehaviorFromPrefs();
1155 SetShelfBehaviorsFromPrefs();
1156 #if defined(OS_CHROMEOS)
1157 SetVirtualKeyboardBehaviorFromPrefs();
1158 #endif // defined(OS_CHROMEOS)
1159 // Restore the order of running, but unpinned applications for the activated
1160 // user.
1161 RestoreUnpinnedRunningApplicationOrder(user_email);
1162 // Inform the system tray of the change.
1163 ash::Shell::GetInstance()->system_tray_delegate()->ActiveUserWasChanged();
1164 // Force on-screen keyboard to reset.
1165 if (keyboard::IsKeyboardEnabled())
1166 ash::Shell::GetInstance()->CreateKeyboard();
1169 void ChromeLauncherController::AdditionalUserAddedToSession(Profile* profile) {
1170 // Switch the running applications to the new user.
1171 app_window_controller_->AdditionalUserAddedToSession(profile);
1174 void ChromeLauncherController::OnExtensionLoaded(
1175 content::BrowserContext* browser_context,
1176 const Extension* extension) {
1177 if (IsAppPinned(extension->id())) {
1178 // Clear and re-fetch to ensure icon is up-to-date.
1179 app_icon_loader_->ClearImage(extension->id());
1180 app_icon_loader_->FetchImage(extension->id());
1183 UpdateAppLaunchersFromPref();
1186 void ChromeLauncherController::OnExtensionUnloaded(
1187 content::BrowserContext* browser_context,
1188 const Extension* extension,
1189 UnloadedExtensionInfo::Reason reason) {
1190 const std::string& id = extension->id();
1191 const Profile* profile = Profile::FromBrowserContext(browser_context);
1193 // Since we might have windowed apps of this type which might have
1194 // outstanding locks which needs to be removed.
1195 if (GetShelfIDForAppID(id) &&
1196 reason == UnloadedExtensionInfo::REASON_UNINSTALL) {
1197 CloseWindowedAppsFromRemovedExtension(id, profile);
1200 if (IsAppPinned(id)) {
1201 if (reason == UnloadedExtensionInfo::REASON_UNINSTALL) {
1202 if (profile == profile_) {
1203 DoUnpinAppWithID(id);
1205 app_icon_loader_->ClearImage(id);
1206 } else {
1207 app_icon_loader_->UpdateImage(id);
1212 void ChromeLauncherController::OnShelfAlignmentChanged(
1213 aura::Window* root_window) {
1214 const char* pref_value = NULL;
1215 switch (ash::Shell::GetInstance()->GetShelfAlignment(root_window)) {
1216 case ash::SHELF_ALIGNMENT_BOTTOM:
1217 pref_value = ash::kShelfAlignmentBottom;
1218 break;
1219 case ash::SHELF_ALIGNMENT_LEFT:
1220 pref_value = ash::kShelfAlignmentLeft;
1221 break;
1222 case ash::SHELF_ALIGNMENT_RIGHT:
1223 pref_value = ash::kShelfAlignmentRight;
1224 break;
1225 case ash::SHELF_ALIGNMENT_TOP:
1226 pref_value = ash::kShelfAlignmentTop;
1229 UpdatePerDisplayPref(
1230 profile_->GetPrefs(), root_window, prefs::kShelfAlignment, pref_value);
1232 if (root_window == ash::Shell::GetPrimaryRootWindow()) {
1233 // See comment in |kShelfAlignment| about why we have two prefs here.
1234 profile_->GetPrefs()->SetString(prefs::kShelfAlignmentLocal, pref_value);
1235 profile_->GetPrefs()->SetString(prefs::kShelfAlignment, pref_value);
1239 void ChromeLauncherController::OnDisplayConfigurationChanged() {
1240 SetShelfBehaviorsFromPrefs();
1243 void ChromeLauncherController::OnIsSyncingChanged() {
1244 syncable_prefs::PrefServiceSyncable* prefs =
1245 PrefServiceSyncableFromProfile(profile_);
1246 MaybePropagatePrefToLocal(prefs,
1247 prefs::kShelfAlignmentLocal,
1248 prefs::kShelfAlignment);
1249 MaybePropagatePrefToLocal(prefs,
1250 prefs::kShelfAutoHideBehaviorLocal,
1251 prefs::kShelfAutoHideBehavior);
1254 void ChromeLauncherController::OnAppSyncUIStatusChanged() {
1255 if (app_sync_ui_state_->status() == AppSyncUIState::STATUS_SYNCING)
1256 model_->SetStatus(ash::ShelfModel::STATUS_LOADING);
1257 else
1258 model_->SetStatus(ash::ShelfModel::STATUS_NORMAL);
1261 void ChromeLauncherController::ExtensionEnableFlowFinished() {
1262 LaunchApp(extension_enable_flow_->extension_id(),
1263 ash::LAUNCH_FROM_UNKNOWN,
1264 ui::EF_NONE);
1265 extension_enable_flow_.reset();
1268 void ChromeLauncherController::ExtensionEnableFlowAborted(bool user_initiated) {
1269 extension_enable_flow_.reset();
1272 ChromeLauncherAppMenuItems ChromeLauncherController::GetApplicationList(
1273 const ash::ShelfItem& item,
1274 int event_flags) {
1275 // Make sure that there is a controller associated with the id and that the
1276 // extension itself is a valid application and not a panel.
1277 LauncherItemController* controller = GetLauncherItemController(item.id);
1278 if (!controller || !GetShelfIDForAppID(controller->app_id()))
1279 return ChromeLauncherAppMenuItems().Pass();
1281 return controller->GetApplicationList(event_flags);
1284 std::vector<content::WebContents*>
1285 ChromeLauncherController::GetV1ApplicationsFromAppId(std::string app_id) {
1286 ash::ShelfID id = GetShelfIDForAppID(app_id);
1288 // If there is no such an item pinned to the launcher, no menu gets created.
1289 if (id) {
1290 LauncherItemController* controller = GetLauncherItemController(id);
1291 DCHECK(controller);
1292 if (controller->type() == LauncherItemController::TYPE_SHORTCUT)
1293 return GetV1ApplicationsFromController(controller);
1295 return std::vector<content::WebContents*>();
1298 void ChromeLauncherController::ActivateShellApp(const std::string& app_id,
1299 int index) {
1300 ash::ShelfID id = GetShelfIDForAppID(app_id);
1301 if (id) {
1302 LauncherItemController* controller = GetLauncherItemController(id);
1303 if (controller && controller->type() == LauncherItemController::TYPE_APP) {
1304 AppWindowLauncherItemController* app_window_controller =
1305 static_cast<AppWindowLauncherItemController*>(controller);
1306 app_window_controller->ActivateIndexedApp(index);
1311 bool ChromeLauncherController::IsWebContentHandledByApplication(
1312 content::WebContents* web_contents,
1313 const std::string& app_id) {
1314 if ((web_contents_to_app_id_.find(web_contents) !=
1315 web_contents_to_app_id_.end()) &&
1316 (web_contents_to_app_id_[web_contents] == app_id))
1317 return true;
1318 return (app_id == kGmailAppId && ContentCanBeHandledByGmailApp(web_contents));
1321 bool ChromeLauncherController::ContentCanBeHandledByGmailApp(
1322 content::WebContents* web_contents) {
1323 ash::ShelfID id = GetShelfIDForAppID(kGmailAppId);
1324 if (id) {
1325 const GURL url = web_contents->GetURL();
1326 // We need to extend the application matching for the gMail app beyond the
1327 // manifest file's specification. This is required because of the namespace
1328 // overlap with the offline app ("/mail/mu/").
1329 if (!base::MatchPattern(url.path(), "/mail/mu/*") &&
1330 base::MatchPattern(url.path(), "/mail/*") &&
1331 GetExtensionForAppID(kGmailAppId) &&
1332 GetExtensionForAppID(kGmailAppId)->OverlapsWithOrigin(url))
1333 return true;
1335 return false;
1338 gfx::Image ChromeLauncherController::GetAppListIcon(
1339 content::WebContents* web_contents) const {
1340 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
1341 if (IsIncognito(web_contents))
1342 return rb.GetImageNamed(IDR_ASH_SHELF_LIST_INCOGNITO_BROWSER);
1343 favicon::FaviconDriver* favicon_driver =
1344 favicon::ContentFaviconDriver::FromWebContents(web_contents);
1345 gfx::Image result = favicon_driver->GetFavicon();
1346 if (result.IsEmpty())
1347 return rb.GetImageNamed(IDR_DEFAULT_FAVICON);
1348 return result;
1351 base::string16 ChromeLauncherController::GetAppListTitle(
1352 content::WebContents* web_contents) const {
1353 base::string16 title = web_contents->GetTitle();
1354 if (!title.empty())
1355 return title;
1356 WebContentsToAppIDMap::const_iterator iter =
1357 web_contents_to_app_id_.find(web_contents);
1358 if (iter != web_contents_to_app_id_.end()) {
1359 std::string app_id = iter->second;
1360 const extensions::Extension* extension = GetExtensionForAppID(app_id);
1361 if (extension)
1362 return base::UTF8ToUTF16(extension->name());
1364 return l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE);
1367 ash::ShelfID ChromeLauncherController::CreateAppShortcutLauncherItem(
1368 const std::string& app_id,
1369 int index) {
1370 return CreateAppShortcutLauncherItemWithType(app_id,
1371 index,
1372 ash::TYPE_APP_SHORTCUT);
1375 void ChromeLauncherController::SetAppTabHelperForTest(AppTabHelper* helper) {
1376 app_tab_helper_.reset(helper);
1379 void ChromeLauncherController::SetAppIconLoaderForTest(
1380 extensions::AppIconLoader* loader) {
1381 app_icon_loader_.reset(loader);
1384 const std::string& ChromeLauncherController::GetAppIdFromShelfIdForTest(
1385 ash::ShelfID id) {
1386 return id_to_item_controller_map_[id]->app_id();
1389 void ChromeLauncherController::SetShelfItemDelegateManagerForTest(
1390 ash::ShelfItemDelegateManager* manager) {
1391 if (item_delegate_manager_)
1392 item_delegate_manager_->RemoveObserver(this);
1394 item_delegate_manager_ = manager;
1396 if (item_delegate_manager_)
1397 item_delegate_manager_->AddObserver(this);
1400 void ChromeLauncherController::RememberUnpinnedRunningApplicationOrder() {
1401 RunningAppListIds list;
1402 for (int i = 0; i < model_->item_count(); i++) {
1403 ash::ShelfItemType type = model_->items()[i].type;
1404 if (type == ash::TYPE_WINDOWED_APP || type == ash::TYPE_PLATFORM_APP)
1405 list.push_back(GetAppIDForShelfID(model_->items()[i].id));
1407 last_used_running_application_order_[
1408 multi_user_util::GetUserIDFromProfile(profile_)] = list;
1411 void ChromeLauncherController::RestoreUnpinnedRunningApplicationOrder(
1412 const std::string& user_id) {
1413 const RunningAppListIdMap::iterator app_id_list =
1414 last_used_running_application_order_.find(user_id);
1415 if (app_id_list == last_used_running_application_order_.end())
1416 return;
1418 // Find the first insertion point for running applications.
1419 int running_index = model_->FirstRunningAppIndex();
1420 for (RunningAppListIds::iterator app_id = app_id_list->second.begin();
1421 app_id != app_id_list->second.end(); ++app_id) {
1422 ash::ShelfID shelf_id = GetShelfIDForAppID(*app_id);
1423 if (shelf_id) {
1424 int app_index = model_->ItemIndexByID(shelf_id);
1425 DCHECK_GE(app_index, 0);
1426 ash::ShelfItemType type = model_->items()[app_index].type;
1427 if (type == ash::TYPE_WINDOWED_APP || type == ash::TYPE_PLATFORM_APP) {
1428 if (running_index != app_index)
1429 model_->Move(running_index, app_index);
1430 running_index++;
1436 ash::ShelfID ChromeLauncherController::CreateAppShortcutLauncherItemWithType(
1437 const std::string& app_id,
1438 int index,
1439 ash::ShelfItemType shelf_item_type) {
1440 AppShortcutLauncherItemController* controller =
1441 new AppShortcutLauncherItemController(app_id, this);
1442 ash::ShelfID shelf_id = InsertAppLauncherItem(
1443 controller, app_id, ash::STATUS_CLOSED, index, shelf_item_type);
1444 return shelf_id;
1447 LauncherItemController* ChromeLauncherController::GetLauncherItemController(
1448 const ash::ShelfID id) {
1449 if (!HasShelfIDToAppIDMapping(id))
1450 return NULL;
1451 return id_to_item_controller_map_[id];
1454 bool ChromeLauncherController::IsBrowserFromActiveUser(Browser* browser) {
1455 // If running multi user mode with separate desktops, we have to check if the
1456 // browser is from the active user.
1457 if (chrome::MultiUserWindowManager::GetMultiProfileMode() !=
1458 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED)
1459 return true;
1460 return multi_user_util::IsProfileFromActiveUser(browser->profile());
1463 bool ChromeLauncherController::ShelfBoundsChangesProbablyWithUser(
1464 aura::Window* root_window,
1465 const std::string& user_id) const {
1466 Profile* other_profile = multi_user_util::GetProfileFromUserID(user_id);
1467 DCHECK_NE(other_profile, profile_);
1469 // Note: The Auto hide state from preferences is not the same as the actual
1470 // visibility of the shelf. Depending on all the various states (full screen,
1471 // no window on desktop, multi user, ..) the shelf could be shown - or not.
1472 bool currently_shown = ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER ==
1473 GetShelfAutoHideBehaviorFromPrefs(profile_, root_window);
1474 bool other_shown = ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER ==
1475 GetShelfAutoHideBehaviorFromPrefs(other_profile, root_window);
1477 return currently_shown != other_shown ||
1478 GetShelfAlignmentFromPrefs(profile_, root_window) !=
1479 GetShelfAlignmentFromPrefs(other_profile, root_window);
1482 void ChromeLauncherController::OnUserProfileReadyToSwitch(Profile* profile) {
1483 #if defined(OS_CHROMEOS)
1484 if (user_switch_observer_.get())
1485 user_switch_observer_->OnUserProfileReadyToSwitch(profile);
1486 #endif
1489 void ChromeLauncherController::LauncherItemClosed(ash::ShelfID id) {
1490 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
1491 CHECK(iter != id_to_item_controller_map_.end());
1492 CHECK(iter->second);
1493 app_icon_loader_->ClearImage(iter->second->app_id());
1494 id_to_item_controller_map_.erase(iter);
1495 int index = model_->ItemIndexByID(id);
1496 // A "browser proxy" is not known to the model and this removal does
1497 // therefore not need to be propagated to the model.
1498 if (index != -1)
1499 model_->RemoveItemAt(index);
1502 void ChromeLauncherController::DoPinAppWithID(const std::string& app_id) {
1503 // If there is an item, do nothing and return.
1504 if (IsAppPinned(app_id))
1505 return;
1507 ash::ShelfID shelf_id = GetShelfIDForAppID(app_id);
1508 if (shelf_id) {
1509 // App item exists, pin it
1510 Pin(shelf_id);
1511 } else {
1512 // Otherwise, create a shortcut item for it.
1513 CreateAppShortcutLauncherItem(app_id, model_->item_count());
1514 if (CanPin())
1515 PersistPinnedState();
1519 void ChromeLauncherController::DoUnpinAppWithID(const std::string& app_id) {
1520 ash::ShelfID shelf_id = GetShelfIDForAppID(app_id);
1521 if (shelf_id && IsPinned(shelf_id))
1522 Unpin(shelf_id);
1525 int ChromeLauncherController::PinRunningAppInternal(int index,
1526 ash::ShelfID shelf_id) {
1527 int running_index = model_->ItemIndexByID(shelf_id);
1528 ash::ShelfItem item = model_->items()[running_index];
1529 DCHECK(item.type == ash::TYPE_WINDOWED_APP ||
1530 item.type == ash::TYPE_PLATFORM_APP);
1531 item.type = ash::TYPE_APP_SHORTCUT;
1532 model_->Set(running_index, item);
1533 // The |ShelfModel|'s weight system might reposition the item to a
1534 // new index, so we get the index again.
1535 running_index = model_->ItemIndexByID(shelf_id);
1536 if (running_index < index)
1537 --index;
1538 if (running_index != index)
1539 model_->Move(running_index, index);
1540 return index;
1543 void ChromeLauncherController::UnpinRunningAppInternal(int index) {
1544 DCHECK_GE(index, 0);
1545 ash::ShelfItem item = model_->items()[index];
1546 DCHECK_EQ(item.type, ash::TYPE_APP_SHORTCUT);
1547 item.type = ash::TYPE_WINDOWED_APP;
1548 // A platform app and a windowed app are sharing TYPE_APP_SHORTCUT. As such
1549 // we have to check here what this was before it got a shortcut.
1550 LauncherItemController* controller = GetLauncherItemController(item.id);
1551 if (controller && controller->type() == LauncherItemController::TYPE_APP)
1552 item.type = ash::TYPE_PLATFORM_APP;
1553 model_->Set(index, item);
1556 void ChromeLauncherController::UpdateAppLaunchersFromPref() {
1557 // There are various functions which will trigger a |PersistPinnedState| call
1558 // like a direct call to |DoPinAppWithID|, or an indirect call to the menu
1559 // model which will use weights to re-arrange the icons to new positions.
1560 // Since this function is meant to synchronize the "is state" with the
1561 // "sync state", it makes no sense to store any changes by this function back
1562 // into the pref state. Therefore we tell |persistPinnedState| to ignore any
1563 // invocations while we are running.
1564 base::AutoReset<bool> auto_reset(&ignore_persist_pinned_state_change_, true);
1565 std::vector<std::string> pinned_apps = GetListOfPinnedAppsAndBrowser();
1567 int index = 0;
1568 int max_index = model_->item_count();
1570 // When one of the two special items cannot be moved (and we do not know where
1571 // yet), we remember the current location in one of these variables.
1572 int chrome_index = -1;
1573 int app_list_index = -1;
1575 // Walk the model and |pinned_apps| from the pref lockstep, adding and
1576 // removing items as necessary. NB: This code uses plain old indexing instead
1577 // of iterators because of model mutations as part of the loop.
1578 std::vector<std::string>::const_iterator pref_app_id(pinned_apps.begin());
1579 for (; index < max_index && pref_app_id != pinned_apps.end(); ++index) {
1580 // Check if we have an item which we need to handle.
1581 if (*pref_app_id == extension_misc::kChromeAppId ||
1582 *pref_app_id == kAppShelfIdPlaceholder ||
1583 IsAppPinned(*pref_app_id)) {
1584 for (; index < max_index; ++index) {
1585 const ash::ShelfItem& item(model_->items()[index]);
1586 bool is_app_list = item.type == ash::TYPE_APP_LIST;
1587 bool is_chrome = item.type == ash::TYPE_BROWSER_SHORTCUT;
1588 if (item.type != ash::TYPE_APP_SHORTCUT && !is_app_list && !is_chrome)
1589 continue;
1590 LauncherItemController* controller = GetLauncherItemController(item.id);
1591 if ((kAppShelfIdPlaceholder == *pref_app_id && is_app_list) ||
1592 (extension_misc::kChromeAppId == *pref_app_id && is_chrome) ||
1593 (controller && controller->app_id() == *pref_app_id)) {
1594 // Check if an item needs to be moved here.
1595 MoveChromeOrApplistToFinalPosition(
1596 is_chrome, is_app_list, index, &chrome_index, &app_list_index);
1597 ++pref_app_id;
1598 break;
1599 } else {
1600 if (is_chrome || is_app_list) {
1601 // We cannot delete any of these shortcuts. As such we remember
1602 // their positions and move them later where they belong.
1603 if (is_chrome)
1604 chrome_index = index;
1605 else
1606 app_list_index = index;
1607 // And skip the item - or exit the loop if end is reached (note that
1608 // in that case we will reduce the index again by one and this only
1609 // compensates for it).
1610 if (index >= max_index - 1)
1611 break;
1612 ++index;
1613 } else {
1614 // Check if this is a platform or a windowed app.
1615 if (item.type == ash::TYPE_APP_SHORTCUT &&
1616 controller &&
1617 (controller->locked() ||
1618 controller->type() == LauncherItemController::TYPE_APP)) {
1619 // Note: This will not change the amount of items (|max_index|).
1620 // Even changes to the actual |index| due to item weighting
1621 // changes should be fine.
1622 UnpinRunningAppInternal(index);
1623 } else {
1624 if (controller)
1625 LauncherItemClosed(item.id);
1626 --max_index;
1629 --index;
1632 // If the item wasn't found, that means id_to_item_controller_map_
1633 // is out of sync.
1634 DCHECK(index <= max_index);
1635 } else {
1636 // Check if the item was already running but not yet pinned.
1637 ash::ShelfID shelf_id = GetShelfIDForAppID(*pref_app_id);
1638 if (shelf_id) {
1639 // This app is running but not yet pinned. So pin and move it.
1640 index = PinRunningAppInternal(index, shelf_id);
1641 } else {
1642 // This app wasn't pinned before, insert a new entry.
1643 shelf_id = CreateAppShortcutLauncherItem(*pref_app_id, index);
1644 ++max_index;
1645 index = model_->ItemIndexByID(shelf_id);
1647 ++pref_app_id;
1651 // Remove any trailing existing items.
1652 while (index < model_->item_count()) {
1653 const ash::ShelfItem& item(model_->items()[index]);
1654 if (item.type == ash::TYPE_APP_SHORTCUT) {
1655 LauncherItemController* controller = GetLauncherItemController(item.id);
1656 if (controller) {
1657 if (controller->locked() ||
1658 controller->type() == LauncherItemController::TYPE_APP) {
1659 UnpinRunningAppInternal(index);
1660 } else {
1661 LauncherItemClosed(item.id);
1664 } else {
1665 if (item.type == ash::TYPE_BROWSER_SHORTCUT)
1666 chrome_index = index;
1667 else if (item.type == ash::TYPE_APP_LIST)
1668 app_list_index = index;
1669 ++index;
1673 // Append unprocessed items from the pref to the end of the model.
1674 for (; pref_app_id != pinned_apps.end(); ++pref_app_id) {
1675 // All items but the chrome and / or app list shortcut needs to be added.
1676 bool is_chrome = *pref_app_id == extension_misc::kChromeAppId;
1677 bool is_app_list = *pref_app_id == kAppShelfIdPlaceholder;
1678 // Coming here we know the next item which can be finalized, either the
1679 // chrome item or the app launcher. The final position is the end of the
1680 // list. The menu model will make sure that the item is grouped according
1681 // to its weight (which we do not know here).
1682 if (!is_chrome && !is_app_list) {
1683 DoPinAppWithID(*pref_app_id);
1684 int target_index = FindInsertionPoint(false);
1685 ash::ShelfID id = GetShelfIDForAppID(*pref_app_id);
1686 int source_index = model_->ItemIndexByID(id);
1687 if (source_index != target_index)
1688 model_->Move(source_index, target_index);
1690 // Needed for the old layout - the weight might force it to be lower in
1691 // rank.
1692 if (app_list_index != -1 && target_index <= app_list_index)
1693 ++app_list_index;
1694 } else {
1695 int target_index = FindInsertionPoint(is_app_list);
1696 MoveChromeOrApplistToFinalPosition(
1697 is_chrome, is_app_list, target_index, &chrome_index, &app_list_index);
1702 void ChromeLauncherController::SetShelfAutoHideBehaviorPrefs(
1703 ash::ShelfAutoHideBehavior behavior,
1704 aura::Window* root_window) {
1705 const char* value = NULL;
1706 switch (behavior) {
1707 case ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS:
1708 value = ash::kShelfAutoHideBehaviorAlways;
1709 break;
1710 case ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER:
1711 value = ash::kShelfAutoHideBehaviorNever;
1712 break;
1713 case ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN:
1714 // This one should not be a valid preference option for now. We only want
1715 // to completely hide it when we run in app mode - or while we temporarily
1716 // hide the shelf as part of an animation (e.g. the multi user change).
1717 return;
1720 UpdatePerDisplayPref(
1721 profile_->GetPrefs(), root_window, prefs::kShelfAutoHideBehavior, value);
1723 if (root_window == ash::Shell::GetPrimaryRootWindow()) {
1724 // See comment in |kShelfAlignment| about why we have two prefs here.
1725 profile_->GetPrefs()->SetString(prefs::kShelfAutoHideBehaviorLocal, value);
1726 profile_->GetPrefs()->SetString(prefs::kShelfAutoHideBehavior, value);
1730 void ChromeLauncherController::SetShelfAutoHideBehaviorFromPrefs() {
1731 aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
1733 for (aura::Window::Windows::const_iterator iter = root_windows.begin();
1734 iter != root_windows.end(); ++iter) {
1735 ash::Shell::GetInstance()->SetShelfAutoHideBehavior(
1736 GetShelfAutoHideBehavior(*iter), *iter);
1740 void ChromeLauncherController::SetShelfAlignmentFromPrefs() {
1741 if (!ash::ShelfWidget::ShelfAlignmentAllowed())
1742 return;
1744 aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
1746 for (aura::Window::Windows::const_iterator iter = root_windows.begin();
1747 iter != root_windows.end(); ++iter) {
1748 ash::Shell::GetInstance()->SetShelfAlignment(
1749 GetShelfAlignmentFromPrefs(profile_, *iter), *iter);
1753 void ChromeLauncherController::SetShelfBehaviorsFromPrefs() {
1754 SetShelfAutoHideBehaviorFromPrefs();
1755 SetShelfAlignmentFromPrefs();
1758 #if defined(OS_CHROMEOS)
1759 void ChromeLauncherController::SetVirtualKeyboardBehaviorFromPrefs() {
1760 const PrefService* service = profile_->GetPrefs();
1761 const bool was_enabled = keyboard::IsKeyboardEnabled();
1762 if (!service->HasPrefPath(prefs::kTouchVirtualKeyboardEnabled)) {
1763 keyboard::SetKeyboardShowOverride(keyboard::KEYBOARD_SHOW_OVERRIDE_NONE);
1764 } else {
1765 const bool enable = service->GetBoolean(
1766 prefs::kTouchVirtualKeyboardEnabled);
1767 keyboard::SetKeyboardShowOverride(
1768 enable ? keyboard::KEYBOARD_SHOW_OVERRIDE_ENABLED
1769 : keyboard::KEYBOARD_SHOW_OVERRIDE_DISABLED);
1771 const bool is_enabled = keyboard::IsKeyboardEnabled();
1772 if (was_enabled && !is_enabled)
1773 ash::Shell::GetInstance()->DeactivateKeyboard();
1774 else if (is_enabled && !was_enabled)
1775 ash::Shell::GetInstance()->CreateKeyboard();
1777 #endif // defined(OS_CHROMEOS)
1779 ash::ShelfItemStatus ChromeLauncherController::GetAppState(
1780 const std::string& app_id) {
1781 ash::ShelfItemStatus status = ash::STATUS_CLOSED;
1782 for (WebContentsToAppIDMap::iterator it = web_contents_to_app_id_.begin();
1783 it != web_contents_to_app_id_.end();
1784 ++it) {
1785 if (it->second == app_id) {
1786 Browser* browser = chrome::FindBrowserWithWebContents(it->first);
1787 // Usually there should never be an item in our |web_contents_to_app_id_|
1788 // list which got deleted already. However - in some situations e.g.
1789 // Browser::SwapTabContent there is temporarily no associated browser.
1790 if (!browser)
1791 continue;
1792 if (browser->window()->IsActive()) {
1793 return browser->tab_strip_model()->GetActiveWebContents() == it->first ?
1794 ash::STATUS_ACTIVE : ash::STATUS_RUNNING;
1795 } else {
1796 status = ash::STATUS_RUNNING;
1800 return status;
1803 ash::ShelfID ChromeLauncherController::InsertAppLauncherItem(
1804 LauncherItemController* controller,
1805 const std::string& app_id,
1806 ash::ShelfItemStatus status,
1807 int index,
1808 ash::ShelfItemType shelf_item_type) {
1809 ash::ShelfID id = model_->next_id();
1810 CHECK(!HasShelfIDToAppIDMapping(id));
1811 CHECK(controller);
1812 id_to_item_controller_map_[id] = controller;
1813 controller->set_shelf_id(id);
1815 ash::ShelfItem item;
1816 item.type = shelf_item_type;
1817 item.image = extensions::util::GetDefaultAppIcon();
1819 ash::ShelfItemStatus new_state = GetAppState(app_id);
1820 if (new_state != ash::STATUS_CLOSED)
1821 status = new_state;
1823 item.status = status;
1825 model_->AddAt(index, item);
1827 app_icon_loader_->FetchImage(app_id);
1828 app_icon_loader_->UpdateImage(app_id);
1830 SetShelfItemDelegate(id, controller);
1832 return id;
1835 std::vector<content::WebContents*>
1836 ChromeLauncherController::GetV1ApplicationsFromController(
1837 LauncherItemController* controller) {
1838 DCHECK(controller->type() == LauncherItemController::TYPE_SHORTCUT);
1839 AppShortcutLauncherItemController* app_controller =
1840 static_cast<AppShortcutLauncherItemController*>(controller);
1841 return app_controller->GetRunningApplications();
1844 BrowserShortcutLauncherItemController*
1845 ChromeLauncherController::GetBrowserShortcutLauncherItemController() {
1846 for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin();
1847 i != id_to_item_controller_map_.end(); ++i) {
1848 int index = model_->ItemIndexByID(i->first);
1849 const ash::ShelfItem& item = model_->items()[index];
1850 if (item.type == ash::TYPE_BROWSER_SHORTCUT)
1851 return static_cast<BrowserShortcutLauncherItemController*>(i->second);
1853 NOTREACHED() << "There should be always a BrowserLauncherItemController.";
1854 return nullptr;
1857 ash::ShelfID ChromeLauncherController::CreateBrowserShortcutLauncherItem() {
1858 ash::ShelfItem browser_shortcut;
1859 browser_shortcut.type = ash::TYPE_BROWSER_SHORTCUT;
1860 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
1861 browser_shortcut.image = *rb.GetImageSkiaNamed(IDR_PRODUCT_LOGO_32);
1862 ash::ShelfID id = model_->next_id();
1863 size_t index = GetChromeIconIndexForCreation();
1864 model_->AddAt(index, browser_shortcut);
1865 id_to_item_controller_map_[id] =
1866 new BrowserShortcutLauncherItemController(this);
1867 id_to_item_controller_map_[id]->set_shelf_id(id);
1868 // ShelfItemDelegateManager owns BrowserShortcutLauncherItemController.
1869 SetShelfItemDelegate(id, id_to_item_controller_map_[id]);
1870 return id;
1873 void ChromeLauncherController::PersistChromeItemIndex(int index) {
1874 profile_->GetPrefs()->SetInteger(prefs::kShelfChromeIconIndex, index);
1877 int ChromeLauncherController::GetChromeIconIndexFromPref() const {
1878 size_t index = profile_->GetPrefs()->GetInteger(prefs::kShelfChromeIconIndex);
1879 const base::ListValue* pinned_apps_pref =
1880 profile_->GetPrefs()->GetList(prefs::kPinnedLauncherApps);
1881 return std::max(static_cast<size_t>(0),
1882 std::min(pinned_apps_pref->GetSize(), index));
1885 void ChromeLauncherController::MoveChromeOrApplistToFinalPosition(
1886 bool is_chrome,
1887 bool is_app_list,
1888 int target_index,
1889 int* chrome_index,
1890 int* app_list_index) {
1891 if (is_chrome && *chrome_index != -1) {
1892 model_->Move(*chrome_index, target_index);
1893 if (*app_list_index != -1 &&
1894 *chrome_index < *app_list_index &&
1895 target_index > *app_list_index)
1896 --(*app_list_index);
1897 *chrome_index = -1;
1898 } else if (is_app_list && *app_list_index != -1) {
1899 model_->Move(*app_list_index, target_index);
1900 if (*chrome_index != -1 &&
1901 *app_list_index < *chrome_index &&
1902 target_index > *chrome_index)
1903 --(*chrome_index);
1904 *app_list_index = -1;
1908 int ChromeLauncherController::FindInsertionPoint(bool is_app_list) {
1909 // Keeping this change small to backport to M33&32 (see crbug.com/329597).
1910 // TODO(skuhne): With the removal of the legacy shelf layout we should remove
1911 // the ability to move the app list item since this was never used. We should
1912 // instead ask the ShelfModel::ValidateInsertionIndex or similir for an index.
1913 if (is_app_list)
1914 return 0;
1916 for (int i = model_->item_count() - 1; i > 0; --i) {
1917 ash::ShelfItemType type = model_->items()[i].type;
1918 if (type == ash::TYPE_APP_SHORTCUT ||
1919 (is_app_list && type == ash::TYPE_APP_LIST) ||
1920 type == ash::TYPE_BROWSER_SHORTCUT) {
1921 return i;
1924 return 0;
1927 int ChromeLauncherController::GetChromeIconIndexForCreation() {
1928 // We get the list of pinned apps as they currently would get pinned.
1929 // Within this list the chrome icon will be the correct location.
1930 std::vector<std::string> pinned_apps = GetListOfPinnedAppsAndBrowser();
1932 std::vector<std::string>::iterator it =
1933 std::find(pinned_apps.begin(),
1934 pinned_apps.end(),
1935 std::string(extension_misc::kChromeAppId));
1936 DCHECK(it != pinned_apps.end());
1937 int index = it - pinned_apps.begin();
1939 // We should do here a comparison between the is state and the "want to be"
1940 // state since some apps might be able to pin but are not yet. Instead - for
1941 // the time being we clamp against the amount of known items and wait for the
1942 // next |UpdateAppLaunchersFromPref()| call to correct it - it will come since
1943 // the pinning will be done then.
1944 return std::min(model_->item_count(), index);
1947 std::vector<std::string>
1948 ChromeLauncherController::GetListOfPinnedAppsAndBrowser() {
1949 // Adding the app list item to the list of items requires that the ID is not
1950 // a valid and known ID for the extension system. The ID was constructed that
1951 // way - but just to make sure...
1952 DCHECK(!app_tab_helper_->IsValidIDForCurrentUser(kAppShelfIdPlaceholder));
1954 std::vector<std::string> pinned_apps;
1956 // Get the new incarnation of the list.
1957 const base::ListValue* pinned_apps_pref =
1958 profile_->GetPrefs()->GetList(prefs::kPinnedLauncherApps);
1960 // Keep track of the addition of the chrome and the app list icon.
1961 bool chrome_icon_added = false;
1962 bool app_list_icon_added = false;
1963 size_t chrome_icon_index = GetChromeIconIndexFromPref();
1965 // See if the chrome string is already in the pinned list and remove it if
1966 // needed.
1967 base::Value* chrome_app = ash::CreateAppDict(extension_misc::kChromeAppId);
1968 if (chrome_app) {
1969 chrome_icon_added = pinned_apps_pref->Find(*chrome_app) !=
1970 pinned_apps_pref->end();
1971 delete chrome_app;
1974 for (size_t index = 0; index < pinned_apps_pref->GetSize(); ++index) {
1975 // We need to position the chrome icon relative to it's place in the pinned
1976 // preference list - even if an item of that list isn't shown yet.
1977 if (index == chrome_icon_index && !chrome_icon_added) {
1978 pinned_apps.push_back(extension_misc::kChromeAppId);
1979 chrome_icon_added = true;
1981 const base::DictionaryValue* app = NULL;
1982 std::string app_id;
1983 if (pinned_apps_pref->GetDictionary(index, &app) &&
1984 app->GetString(ash::kPinnedAppsPrefAppIDPath, &app_id) &&
1985 (std::find(pinned_apps.begin(), pinned_apps.end(), app_id) ==
1986 pinned_apps.end())) {
1987 if (app_id == extension_misc::kChromeAppId) {
1988 chrome_icon_added = true;
1989 pinned_apps.push_back(extension_misc::kChromeAppId);
1990 } else if (app_id == kAppShelfIdPlaceholder) {
1991 app_list_icon_added = true;
1992 pinned_apps.push_back(kAppShelfIdPlaceholder);
1993 } else if (app_tab_helper_->IsValidIDForCurrentUser(app_id)) {
1994 // Note: In multi profile scenarios we only want to show pinnable apps
1995 // here which is correct. Running applications from the other users will
1996 // continue to run. So no need for multi profile modifications.
1997 pinned_apps.push_back(app_id);
2002 // If not added yet, the chrome item will be the last item in the list.
2003 if (!chrome_icon_added)
2004 pinned_apps.push_back(extension_misc::kChromeAppId);
2006 // If not added yet, add the app list item either at the end or at the
2007 // beginning - depending on the shelf layout.
2008 if (!app_list_icon_added) {
2009 pinned_apps.insert(pinned_apps.begin(), kAppShelfIdPlaceholder);
2011 return pinned_apps;
2014 bool ChromeLauncherController::IsIncognito(
2015 const content::WebContents* web_contents) const {
2016 const Profile* profile =
2017 Profile::FromBrowserContext(web_contents->GetBrowserContext());
2018 return profile->IsOffTheRecord() && !profile->IsGuestSession() &&
2019 !profile->IsSystemProfile();
2022 void ChromeLauncherController::CloseWindowedAppsFromRemovedExtension(
2023 const std::string& app_id,
2024 const Profile* profile) {
2025 // This function cannot rely on the controller's enumeration functionality
2026 // since the extension has already be unloaded.
2027 const BrowserList* ash_browser_list =
2028 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
2029 std::vector<Browser*> browser_to_close;
2030 for (BrowserList::const_reverse_iterator
2031 it = ash_browser_list->begin_last_active();
2032 it != ash_browser_list->end_last_active(); ++it) {
2033 Browser* browser = *it;
2034 if (!browser->is_type_tabbed() && browser->is_type_popup() &&
2035 browser->is_app() &&
2036 app_id ==
2037 web_app::GetExtensionIdFromApplicationName(browser->app_name()) &&
2038 profile == browser->profile()) {
2039 browser_to_close.push_back(browser);
2042 while (!browser_to_close.empty()) {
2043 TabStripModel* tab_strip = browser_to_close.back()->tab_strip_model();
2044 tab_strip->CloseWebContentsAt(0, TabStripModel::CLOSE_NONE);
2045 browser_to_close.pop_back();
2049 void ChromeLauncherController::SetShelfItemDelegate(
2050 ash::ShelfID id,
2051 ash::ShelfItemDelegate* item_delegate) {
2052 DCHECK_GT(id, 0);
2053 DCHECK(item_delegate);
2054 DCHECK(item_delegate_manager_);
2055 item_delegate_manager_->SetShelfItemDelegate(
2056 id, scoped_ptr<ash::ShelfItemDelegate>(item_delegate).Pass());
2059 void ChromeLauncherController::AttachProfile(Profile* profile) {
2060 profile_ = profile;
2061 // Either add the profile to the list of known profiles and make it the active
2062 // one for some functions of AppTabHelper or create a new one.
2063 if (!app_tab_helper_.get())
2064 app_tab_helper_.reset(new LauncherAppTabHelper(profile_));
2065 else
2066 app_tab_helper_->SetCurrentUser(profile_);
2067 // TODO(skuhne): The AppIconLoaderImpl has the same problem. Each loaded
2068 // image is associated with a profile (it's loader requires the profile).
2069 // Since icon size changes are possible, the icon could be requested to be
2070 // reloaded. However - having it not multi profile aware would cause problems
2071 // if the icon cache gets deleted upon user switch.
2072 app_icon_loader_.reset(new extensions::AppIconLoaderImpl(
2073 profile_, extension_misc::EXTENSION_ICON_SMALL, this));
2075 pref_change_registrar_.Init(profile_->GetPrefs());
2076 pref_change_registrar_.Add(
2077 prefs::kPinnedLauncherApps,
2078 base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref,
2079 base::Unretained(this)));
2080 pref_change_registrar_.Add(
2081 prefs::kShelfAlignmentLocal,
2082 base::Bind(&ChromeLauncherController::SetShelfAlignmentFromPrefs,
2083 base::Unretained(this)));
2084 pref_change_registrar_.Add(
2085 prefs::kShelfAutoHideBehaviorLocal,
2086 base::Bind(&ChromeLauncherController::
2087 SetShelfAutoHideBehaviorFromPrefs,
2088 base::Unretained(this)));
2089 pref_change_registrar_.Add(
2090 prefs::kShelfPreferences,
2091 base::Bind(&ChromeLauncherController::SetShelfBehaviorsFromPrefs,
2092 base::Unretained(this)));
2093 #if defined(OS_CHROMEOS)
2094 pref_change_registrar_.Add(
2095 prefs::kTouchVirtualKeyboardEnabled,
2096 base::Bind(&ChromeLauncherController::SetVirtualKeyboardBehaviorFromPrefs,
2097 base::Unretained(this)));
2098 #endif // defined(OS_CHROMEOS)
2100 extensions::ExtensionRegistry::Get(profile_)->AddObserver(this);
2103 void ChromeLauncherController::ReleaseProfile() {
2104 if (app_sync_ui_state_)
2105 app_sync_ui_state_->RemoveObserver(this);
2107 extensions::ExtensionRegistry::Get(profile_)->RemoveObserver(this);
2109 PrefServiceSyncableFromProfile(profile_)->RemoveObserver(this);
2111 pref_change_registrar_.RemoveAll();