Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(238)

Side by Side Diff: chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc

Issue 2052013002: Adding ChromeLauncherController interface. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@chrome_launcher_smaller_api
Patch Set: Rebase Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h" 5 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
6 6
7 #include <stddef.h> 7 // static
8 ChromeLauncherController* ChromeLauncherController::instance_ = nullptr;
8 9
9 #include <vector> 10 ChromeLauncherController::~ChromeLauncherController() {
10 11 if (instance_ == this)
11 #include "ash/ash_switches.h" 12 instance_ = nullptr;
12 #include "ash/common/shelf/shelf_item_delegate_manager.h"
13 #include "ash/common/shelf/shelf_model.h"
14 #include "ash/common/system/tray/system_tray_delegate.h"
15 #include "ash/common/wm_shell.h"
16 #include "ash/desktop_background/desktop_background_controller.h"
17 #include "ash/multi_profile_uma.h"
18 #include "ash/root_window_controller.h"
19 #include "ash/shelf/shelf.h"
20 #include "ash/shell.h"
21 #include "ash/wm/window_util.h"
22 #include "base/command_line.h"
23 #include "base/macros.h"
24 #include "base/strings/pattern.h"
25 #include "base/strings/string_util.h"
26 #include "base/strings/utf_string_conversions.h"
27 #include "base/values.h"
28 #include "build/build_config.h"
29 #include "chrome/browser/browser_process.h"
30 #include "chrome/browser/chrome_notification_types.h"
31 #include "chrome/browser/chromeos/arc/arc_support_host.h"
32 #include "chrome/browser/defaults.h"
33 #include "chrome/browser/extensions/extension_app_icon_loader.h"
34 #include "chrome/browser/extensions/extension_util.h"
35 #include "chrome/browser/extensions/launch_util.h"
36 #include "chrome/browser/prefs/incognito_mode_prefs.h"
37 #include "chrome/browser/profiles/profile.h"
38 #include "chrome/browser/profiles/profile_manager.h"
39 #include "chrome/browser/ui/app_list/arc/arc_app_icon_loader.h"
40 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
41 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
42 #include "chrome/browser/ui/ash/app_sync_ui_state.h"
43 #include "chrome/browser/ui/ash/chrome_launcher_prefs.h"
44 #include "chrome/browser/ui/ash/chrome_shell_delegate.h"
45 #include "chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.h "
46 #include "chrome/browser/ui/ash/launcher/app_window_launcher_controller.h"
47 #include "chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h"
48 #include "chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_controller.h"
49 #include "chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.h"
50 #include "chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controll er.h"
51 #include "chrome/browser/ui/ash/launcher/browser_status_monitor.h"
52 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h"
53 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_browser.h "
54 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_tab.h"
55 #include "chrome/browser/ui/ash/launcher/chrome_launcher_types.h"
56 #include "chrome/browser/ui/ash/launcher/launcher_arc_app_updater.h"
57 #include "chrome/browser/ui/ash/launcher/launcher_controller_helper.h"
58 #include "chrome/browser/ui/ash/launcher/launcher_extension_app_updater.h"
59 #include "chrome/browser/ui/ash/launcher/launcher_item_controller.h"
60 #include "chrome/browser/ui/ash/launcher/multi_profile_app_window_launcher_contr oller.h"
61 #include "chrome/browser/ui/ash/launcher/multi_profile_browser_status_monitor.h"
62 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
63 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager.h"
64 #include "chrome/browser/ui/browser.h"
65 #include "chrome/browser/ui/browser_finder.h"
66 #include "chrome/browser/ui/browser_list.h"
67 #include "chrome/browser/ui/browser_tabstrip.h"
68 #include "chrome/browser/ui/browser_window.h"
69 #include "chrome/browser/ui/tabs/tab_strip_model.h"
70 #include "chrome/browser/web_applications/web_app.h"
71 #include "chrome/common/chrome_switches.h"
72 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
73 #include "chrome/common/pref_names.h"
74 #include "chrome/common/url_constants.h"
75 #include "chrome/grit/generated_resources.h"
76 #include "components/favicon/content/content_favicon_driver.h"
77 #include "components/prefs/scoped_user_pref_update.h"
78 #include "components/signin/core/account_id/account_id.h"
79 #include "components/strings/grit/components_strings.h"
80 #include "components/user_manager/user_manager.h"
81 #include "content/public/browser/navigation_entry.h"
82 #include "content/public/browser/web_contents.h"
83 #include "extensions/browser/extension_prefs.h"
84 #include "extensions/browser/extension_registry.h"
85 #include "extensions/browser/extension_system.h"
86 #include "extensions/browser/extension_util.h"
87 #include "extensions/common/constants.h"
88 #include "extensions/common/extension.h"
89 #include "extensions/common/extension_resource.h"
90 #include "extensions/common/manifest_handlers/icons_handler.h"
91 #include "extensions/common/url_pattern.h"
92 #include "grit/ash_resources.h"
93 #include "grit/theme_resources.h"
94 #include "ui/aura/window.h"
95 #include "ui/aura/window_event_dispatcher.h"
96 #include "ui/base/l10n/l10n_util.h"
97 #include "ui/base/resource/resource_bundle.h"
98 #include "ui/base/window_open_disposition.h"
99 #include "ui/keyboard/keyboard_util.h"
100 #include "ui/resources/grit/ui_resources.h"
101 #include "ui/wm/core/window_animations.h"
102
103 using extensions::Extension;
104 using extensions::UnloadedExtensionInfo;
105 using extension_misc::kGmailAppId;
106 using content::WebContents;
107
108 // static
109 ChromeLauncherController* ChromeLauncherController::instance_ = NULL;
110
111 namespace {
112
113 int64_t GetDisplayIDForShelf(ash::Shelf* shelf) {
114 aura::Window* root_window =
115 shelf->shelf_widget()->GetNativeWindow()->GetRootWindow();
116 display::Display display =
117 display::Screen::GetScreen()->GetDisplayNearestWindow(root_window);
118 DCHECK(display.is_valid());
119 return display.id();
120 } 13 }
121 14
122 /* 15 ChromeLauncherController::ChromeLauncherController() {}
123 * Return whether an app is pinned only by user.
124 * This function doesn't expect an app_id neither pinned by user nor by
125 * policy, the app_id in the arguments list MUST be pinned by either of
126 * those. Invalid input may lead to unexpected result.
127 * If this app is pinned by policy, but not by user, false is returned.
128 * If this app is pinned by both policy and user, false is returned.
129 * If this app is pinned not by policy, but by user, true is returned.
130 */
131 bool IsAppForUserPinned(const std::string& app_id,
132 const base::ListValue* pinned_apps_pref,
133 const base::ListValue* policy_pinned_apps_pref) {
134 for (size_t index = 0; index < pinned_apps_pref->GetSize(); ++index) {
135 const base::DictionaryValue* app;
136 if (pinned_apps_pref->GetDictionary(index, &app)) {
137 std::string current_app_id;
138 bool pinned_by_policy = false;
139 if (app->GetString(ash::kPinnedAppsPrefAppIDPath, &current_app_id)) {
140 if (app_id == current_app_id) {
141 if (app->GetBoolean(ash::kPinnedAppsPrefPinnedByPolicy,
142 &pinned_by_policy) &&
143 pinned_by_policy) {
144 // Pinned by policy in the past or present.
145 // Need to check policy_pinned_apps to determine
146 break;
147 } else {
148 // User Preference Already Pinned
149 return true;
150 }
151 }
152 }
153 }
154 }
155 for (size_t index = 0; index < policy_pinned_apps_pref->GetSize(); ++index) {
156 const base::DictionaryValue* app;
157 if (policy_pinned_apps_pref->GetDictionary(index, &app)) {
158 std::string app_id_;
159 if (app->GetString(ash::kPinnedAppsPrefAppIDPath, &app_id_)) {
160 // Only pinned by policy, which is not part of user-pinned
161 if (app_id == app_id_)
162 return false;
163 }
164 }
165 }
166 // Default, user added new pins
167 return true;
168 }
169
170 const char* const kPinProhibitedExtensionIds[] = {
171 ArcSupportHost::kHostAppId, arc::kPlayStoreAppId,
172 };
173
174 const size_t kPinProhibitedExtensionIdsLength =
175 arraysize(kPinProhibitedExtensionIds);
176
177 } // namespace
178
179 // A class to get events from ChromeOS when a user gets changed or added.
180 class ChromeLauncherControllerUserSwitchObserver
181 : public user_manager::UserManager::UserSessionStateObserver {
182 public:
183 ChromeLauncherControllerUserSwitchObserver(
184 ChromeLauncherController* controller)
185 : controller_(controller) {
186 DCHECK(user_manager::UserManager::IsInitialized());
187 user_manager::UserManager::Get()->AddSessionStateObserver(this);
188 }
189 ~ChromeLauncherControllerUserSwitchObserver() override {
190 user_manager::UserManager::Get()->RemoveSessionStateObserver(this);
191 }
192
193 // user_manager::UserManager::UserSessionStateObserver overrides:
194 void UserAddedToSession(const user_manager::User* added_user) override;
195
196 // ChromeLauncherControllerUserSwitchObserver:
197 void OnUserProfileReadyToSwitch(Profile* profile);
198
199 private:
200 // Add a user to the session.
201 void AddUser(Profile* profile);
202
203 // The owning ChromeLauncherController.
204 ChromeLauncherController* controller_;
205
206 // Users which were just added to the system, but which profiles were not yet
207 // (fully) loaded.
208 std::set<std::string> added_user_ids_waiting_for_profiles_;
209
210 DISALLOW_COPY_AND_ASSIGN(ChromeLauncherControllerUserSwitchObserver);
211 };
212
213 void ChromeLauncherControllerUserSwitchObserver::UserAddedToSession(
214 const user_manager::User* active_user) {
215 Profile* profile =
216 multi_user_util::GetProfileFromAccountId(active_user->GetAccountId());
217 // If we do not have a profile yet, we postpone forwarding the notification
218 // until it is loaded.
219 if (!profile)
220 added_user_ids_waiting_for_profiles_.insert(active_user->email());
221 else
222 AddUser(profile);
223 }
224
225 void ChromeLauncherControllerUserSwitchObserver::OnUserProfileReadyToSwitch(
226 Profile* profile) {
227 if (!added_user_ids_waiting_for_profiles_.empty()) {
228 // Check if the profile is from a user which was on the waiting list.
229 std::string user_id =
230 multi_user_util::GetAccountIdFromProfile(profile).GetUserEmail();
231 std::set<std::string>::iterator it = std::find(
232 added_user_ids_waiting_for_profiles_.begin(),
233 added_user_ids_waiting_for_profiles_.end(),
234 user_id);
235 if (it != added_user_ids_waiting_for_profiles_.end()) {
236 added_user_ids_waiting_for_profiles_.erase(it);
237 AddUser(profile->GetOriginalProfile());
238 }
239 }
240 }
241
242 void ChromeLauncherControllerUserSwitchObserver::AddUser(Profile* profile) {
243 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
244 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED)
245 chrome::MultiUserWindowManager::GetInstance()->AddUser(profile);
246 controller_->AdditionalUserAddedToSession(profile->GetOriginalProfile());
247 }
248
249 ChromeLauncherController::ChromeLauncherController(Profile* profile,
250 ash::ShelfModel* model)
251 : model_(model),
252 item_delegate_manager_(NULL),
253 profile_(profile),
254 app_sync_ui_state_(NULL),
255 ignore_persist_pinned_state_change_(false) {
256 if (!profile_) {
257 // If no profile was passed, we take the currently active profile and use it
258 // as the owner of the current desktop.
259 // Use the original profile as on chromeos we may get a temporary off the
260 // record profile, unless in guest session (where off the record profile is
261 // the right one).
262 profile_ = ProfileManager::GetActiveUserProfile();
263 if (!profile_->IsGuestSession() && !profile_->IsSystemProfile())
264 profile_ = profile_->GetOriginalProfile();
265
266 app_sync_ui_state_ = AppSyncUIState::Get(profile_);
267 if (app_sync_ui_state_)
268 app_sync_ui_state_->AddObserver(this);
269 }
270
271 if (arc::ArcAuthService::IsAllowedForProfile(profile_)) {
272 arc_deferred_launcher_.reset(new ArcAppDeferredLauncherController(this));
273 }
274
275 // All profile relevant settings get bound to the current profile.
276 AttachProfile(profile_);
277 model_->AddObserver(this);
278
279 // In multi profile mode we might have a window manager. We try to create it
280 // here. If the instantiation fails, the manager is not needed.
281 chrome::MultiUserWindowManager::CreateInstance();
282
283 // On Chrome OS using multi profile we want to switch the content of the shelf
284 // with a user change. Note that for unit tests the instance can be NULL.
285 if (chrome::MultiUserWindowManager::GetMultiProfileMode() !=
286 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_OFF) {
287 user_switch_observer_.reset(
288 new ChromeLauncherControllerUserSwitchObserver(this));
289 }
290
291 std::unique_ptr<AppWindowLauncherController> extension_app_window_controller;
292 // Create our v1/v2 application / browser monitors which will inform the
293 // launcher of status changes.
294 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
295 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED) {
296 // If running in separated destkop mode, we create the multi profile version
297 // of status monitor.
298 browser_status_monitor_.reset(new MultiProfileBrowserStatusMonitor(this));
299 extension_app_window_controller.reset(
300 new MultiProfileAppWindowLauncherController(this));
301 } else {
302 // Create our v1/v2 application / browser monitors which will inform the
303 // launcher of status changes.
304 browser_status_monitor_.reset(new BrowserStatusMonitor(this));
305 extension_app_window_controller.reset(
306 new ExtensionAppWindowLauncherController(this));
307 }
308 app_window_controllers_.push_back(std::move(extension_app_window_controller));
309
310 std::unique_ptr<AppWindowLauncherController> arc_app_window_controller;
311 arc_app_window_controller.reset(new ArcAppWindowLauncherController(this));
312 app_window_controllers_.push_back(std::move(arc_app_window_controller));
313
314 // Right now ash::Shell isn't created for tests.
315 // TODO(mukai): Allows it to observe display change and write tests.
316 if (ash::Shell::HasInstance()) {
317 ash::Shell::GetInstance()->window_tree_host_manager()->AddObserver(this);
318 // If it got already set, we remove the observer first again and swap the
319 // ItemDelegateManager.
320 if (item_delegate_manager_)
321 item_delegate_manager_->RemoveObserver(this);
322 item_delegate_manager_ =
323 ash::Shell::GetInstance()->shelf_item_delegate_manager();
324 item_delegate_manager_->AddObserver(this);
325 }
326 }
327
328 ChromeLauncherController::~ChromeLauncherController() {
329 if (item_delegate_manager_)
330 item_delegate_manager_->RemoveObserver(this);
331
332 // Reset the BrowserStatusMonitor as it has a weak pointer to this.
333 browser_status_monitor_.reset();
334
335 // Reset the app window controllers here since it has a weak pointer to this.
336 app_window_controllers_.clear();
337
338 model_->RemoveObserver(this);
339 if (ash::Shell::HasInstance())
340 ash::Shell::GetInstance()->window_tree_host_manager()->RemoveObserver(this);
341 for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin();
342 i != id_to_item_controller_map_.end(); ++i) {
343 int index = model_->ItemIndexByID(i->first);
344 // A "browser proxy" is not known to the model and this removal does
345 // therefore not need to be propagated to the model.
346 if (index != -1 &&
347 model_->items()[index].type != ash::TYPE_BROWSER_SHORTCUT)
348 model_->RemoveItemAt(index);
349 }
350
351 // Release all profile dependent resources.
352 ReleaseProfile();
353 if (instance_ == this)
354 instance_ = NULL;
355
356 // Get rid of the multi user window manager instance.
357 chrome::MultiUserWindowManager::DeleteInstance();
358 }
359
360 // static
361 ChromeLauncherController* ChromeLauncherController::CreateInstance(
362 Profile* profile,
363 ash::ShelfModel* model) {
364 // We do not check here for re-creation of the ChromeLauncherController since
365 // it appears that it might be intentional that the ChromeLauncherController
366 // can be re-created.
367 instance_ = new ChromeLauncherController(profile, model);
368 return instance_;
369 }
370
371 void ChromeLauncherController::Init() {
372 CreateBrowserShortcutLauncherItem();
373 UpdateAppLaunchersFromPref();
374
375 // TODO(sky): update unit test so that this test isn't necessary.
376 if (ash::Shell::HasInstance())
377 SetVirtualKeyboardBehaviorFromPrefs();
378
379 prefs_observer_ =
380 ash::ChromeLauncherPrefsObserver::CreateIfNecessary(profile_);
381 }
382
383 ash::ShelfID ChromeLauncherController::CreateAppLauncherItem(
384 LauncherItemController* controller,
385 const std::string& app_id,
386 ash::ShelfItemStatus status) {
387 CHECK(controller);
388 int index = 0;
389 // Panels are inserted on the left so as not to push all existing panels over.
390 if (controller->GetShelfItemType() != ash::TYPE_APP_PANEL)
391 index = model_->item_count();
392 return InsertAppLauncherItem(controller,
393 app_id,
394 status,
395 index,
396 controller->GetShelfItemType());
397 }
398
399 void ChromeLauncherController::SetItemStatus(ash::ShelfID id,
400 ash::ShelfItemStatus status) {
401 int index = model_->ItemIndexByID(id);
402 ash::ShelfItemStatus old_status = model_->items()[index].status;
403 // Since ordinary browser windows are not registered, we might get a negative
404 // index here.
405 if (index >= 0 && old_status != status) {
406 ash::ShelfItem item = model_->items()[index];
407 item.status = status;
408 model_->Set(index, item);
409 }
410 }
411
412 void ChromeLauncherController::SetItemController(
413 ash::ShelfID id,
414 LauncherItemController* controller) {
415 CHECK(controller);
416 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
417 CHECK(iter != id_to_item_controller_map_.end());
418 controller->set_shelf_id(id);
419 iter->second = controller;
420 // Existing controller is destroyed and replaced by registering again.
421 SetShelfItemDelegate(id, controller);
422 }
423
424 void ChromeLauncherController::CloseLauncherItem(ash::ShelfID id) {
425 CHECK(id);
426 if (IsPinned(id)) {
427 // Create a new shortcut controller.
428 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
429 CHECK(iter != id_to_item_controller_map_.end());
430 SetItemStatus(id, ash::STATUS_CLOSED);
431 std::string app_id = iter->second->app_id();
432 iter->second = AppShortcutLauncherItemController::Create(app_id, this);
433 iter->second->set_shelf_id(id);
434 // Existing controller is destroyed and replaced by registering again.
435 SetShelfItemDelegate(id, iter->second);
436 } else {
437 LauncherItemClosed(id);
438 }
439 }
440
441 AppListControllerDelegate::Pinnable ChromeLauncherController::GetPinnable(
442 const std::string& app_id) {
443 for (size_t i = 0; i < kPinProhibitedExtensionIdsLength; ++i) {
444 if (kPinProhibitedExtensionIds[i] == app_id)
445 return AppListControllerDelegate::NO_PIN;
446 }
447
448 const base::ListValue* pref =
449 profile_->GetPrefs()->GetList(prefs::kPolicyPinnedLauncherApps);
450 if (!pref)
451 return AppListControllerDelegate::PIN_EDITABLE;
452
453 // Pinned ARC apps policy defines the package name of the apps, that must
454 // be pinned. All the launch activities of any package in policy are pinned.
455 // In turn the input parameter to this function is app_id, which
456 // is 32 chars hash. In case of ARC app this is a hash of
457 // (package name + activity). This means that we must identify the package
458 // from the hash, and check if this package is pinned by policy.
459 const ArcAppListPrefs* const arc_prefs = ArcAppListPrefs::Get(profile());
460 std::string arc_app_packege_name;
461 if (arc_prefs) {
462 std::unique_ptr<ArcAppListPrefs::AppInfo> app_info =
463 arc_prefs->GetApp(app_id);
464 if (app_info)
465 arc_app_packege_name = app_info->package_name;
466 }
467
468 for (size_t index = 0; index < pref->GetSize(); ++index) {
469 const base::DictionaryValue* app = nullptr;
470 std::string app_id_or_package;
471 if (pref->GetDictionary(index, &app) &&
472 app->GetString(ash::kPinnedAppsPrefAppIDPath, &app_id_or_package) &&
473 (app_id == app_id_or_package ||
474 arc_app_packege_name == app_id_or_package)) {
475 return AppListControllerDelegate::PIN_FIXED;
476 }
477 }
478 return AppListControllerDelegate::PIN_EDITABLE;
479 }
480
481 void ChromeLauncherController::Pin(ash::ShelfID id) {
482 DCHECK(HasShelfIDToAppIDMapping(id));
483
484 int index = model_->ItemIndexByID(id);
485 DCHECK_GE(index, 0);
486
487 ash::ShelfItem item = model_->items()[index];
488
489 if (item.type == ash::TYPE_PLATFORM_APP ||
490 item.type == ash::TYPE_WINDOWED_APP) {
491 item.type = ash::TYPE_APP_SHORTCUT;
492 model_->Set(index, item);
493 } else if (item.type != ash::TYPE_APP_SHORTCUT) {
494 return;
495 }
496
497 if (GetLauncherItemController(id)->CanPin())
498 PersistPinnedState();
499 }
500
501 void ChromeLauncherController::Unpin(ash::ShelfID id) {
502 LauncherItemController* controller = GetLauncherItemController(id);
503 CHECK(controller);
504 const bool can_pin = controller->CanPin();
505
506 if (controller->type() == LauncherItemController::TYPE_APP ||
507 controller->locked()) {
508 UnpinRunningAppInternal(model_->ItemIndexByID(id));
509 } else {
510 LauncherItemClosed(id);
511 }
512 if (can_pin)
513 PersistPinnedState();
514 }
515
516 bool ChromeLauncherController::IsPinned(ash::ShelfID id) {
517 int index = model_->ItemIndexByID(id);
518 if (index < 0)
519 return false;
520 ash::ShelfItemType type = model_->items()[index].type;
521 return (type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_BROWSER_SHORTCUT);
522 }
523
524 void ChromeLauncherController::TogglePinned(ash::ShelfID id) {
525 if (!HasShelfIDToAppIDMapping(id))
526 return; // May happen if item closed with menu open.
527
528 if (IsPinned(id))
529 Unpin(id);
530 else
531 Pin(id);
532 }
533
534 bool ChromeLauncherController::IsPinnable(ash::ShelfID id) const {
535 int index = model_->ItemIndexByID(id);
536 if (index == -1)
537 return false;
538
539 ash::ShelfItemType type = model_->items()[index].type;
540 std::string app_id;
541 return ((type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_PLATFORM_APP ||
542 type == ash::TYPE_WINDOWED_APP) &&
543 item_delegate_manager_->GetShelfItemDelegate(id)->CanPin());
544 }
545
546 void ChromeLauncherController::LockV1AppWithID(const std::string& app_id) {
547 ash::ShelfID id = GetShelfIDForAppID(app_id);
548 if (!IsPinned(id) && !IsWindowedAppInLauncher(app_id)) {
549 CreateAppShortcutLauncherItemWithType(app_id,
550 model_->item_count(),
551 ash::TYPE_WINDOWED_APP);
552 id = GetShelfIDForAppID(app_id);
553 }
554 CHECK(id);
555 id_to_item_controller_map_[id]->lock();
556 }
557
558 void ChromeLauncherController::UnlockV1AppWithID(const std::string& app_id) {
559 ash::ShelfID id = GetShelfIDForAppID(app_id);
560 CHECK(id);
561 CHECK(IsPinned(id) || IsWindowedAppInLauncher(app_id));
562 LauncherItemController* controller = id_to_item_controller_map_[id];
563 controller->unlock();
564 if (!controller->locked() && !IsPinned(id))
565 CloseLauncherItem(id);
566 }
567
568 void ChromeLauncherController::Launch(ash::ShelfID id, int event_flags) {
569 LauncherItemController* controller = GetLauncherItemController(id);
570 if (!controller)
571 return; // In case invoked from menu and item closed while menu up.
572 controller->Launch(ash::LAUNCH_FROM_UNKNOWN, event_flags);
573 }
574
575 void ChromeLauncherController::Close(ash::ShelfID id) {
576 LauncherItemController* controller = GetLauncherItemController(id);
577 if (!controller)
578 return; // May happen if menu closed.
579 controller->Close();
580 }
581
582 bool ChromeLauncherController::IsOpen(ash::ShelfID id) {
583 LauncherItemController* controller = GetLauncherItemController(id);
584 if (!controller)
585 return false;
586 return controller->IsOpen();
587 }
588
589 bool ChromeLauncherController::IsPlatformApp(ash::ShelfID id) {
590 if (!HasShelfIDToAppIDMapping(id))
591 return false;
592
593 std::string app_id = GetAppIDForShelfID(id);
594 const Extension* extension = GetExtensionForAppID(app_id);
595 // An extension can be synced / updated at any time and therefore not be
596 // available.
597 return extension ? extension->is_platform_app() : false;
598 }
599
600 void ChromeLauncherController::LaunchApp(const std::string& app_id,
601 ash::LaunchSource source,
602 int event_flags) {
603 launcher_controller_helper_->LaunchApp(app_id, source, event_flags);
604 }
605
606 void ChromeLauncherController::ActivateApp(const std::string& app_id,
607 ash::LaunchSource source,
608 int event_flags) {
609 // If there is an existing non-shortcut controller for this app, open it.
610 ash::ShelfID id = GetShelfIDForAppID(app_id);
611 if (id) {
612 LauncherItemController* controller = GetLauncherItemController(id);
613 controller->Activate(source);
614 return;
615 }
616
617 // Create a temporary application launcher item and use it to see if there are
618 // running instances.
619 std::unique_ptr<AppShortcutLauncherItemController> app_controller(
620 AppShortcutLauncherItemController::Create(app_id, this));
621 if (!app_controller->GetRunningApplications().empty())
622 app_controller->Activate(source);
623 else
624 LaunchApp(app_id, source, event_flags);
625 }
626
627 extensions::LaunchType ChromeLauncherController::GetLaunchType(
628 ash::ShelfID id) {
629 const Extension* extension = GetExtensionForAppID(GetAppIDForShelfID(id));
630
631 // An extension can be unloaded/updated/unavailable at any time.
632 if (!extension)
633 return extensions::LAUNCH_TYPE_DEFAULT;
634
635 return extensions::GetLaunchType(extensions::ExtensionPrefs::Get(profile_),
636 extension);
637 }
638
639 ash::ShelfID ChromeLauncherController::GetShelfIDForAppID(
640 const std::string& app_id) {
641 for (IDToItemControllerMap::const_iterator i =
642 id_to_item_controller_map_.begin();
643 i != id_to_item_controller_map_.end(); ++i) {
644 if (i->second->type() == LauncherItemController::TYPE_APP_PANEL)
645 continue; // Don't include panels
646 if (i->second->app_id() == app_id)
647 return i->first;
648 }
649 return 0;
650 }
651
652 bool ChromeLauncherController::HasShelfIDToAppIDMapping(ash::ShelfID id) const {
653 return id_to_item_controller_map_.find(id) !=
654 id_to_item_controller_map_.end();
655 }
656
657 const std::string& ChromeLauncherController::GetAppIDForShelfID(
658 ash::ShelfID id) {
659 LauncherItemController* controller = GetLauncherItemController(id);
660 return controller ? controller->app_id() : base::EmptyString();
661 }
662
663 void ChromeLauncherController::OnAppImageUpdated(const std::string& id,
664 const gfx::ImageSkia& image) {
665 // TODO: need to get this working for shortcuts.
666 for (IDToItemControllerMap::const_iterator i =
667 id_to_item_controller_map_.begin();
668 i != id_to_item_controller_map_.end(); ++i) {
669 LauncherItemController* controller = i->second;
670 if (controller->app_id() != id)
671 continue;
672 if (controller->image_set_by_controller())
673 continue;
674 int index = model_->ItemIndexByID(i->first);
675 if (index == -1)
676 continue;
677 ash::ShelfItem item = model_->items()[index];
678 item.image = image;
679 model_->Set(index, item);
680 // It's possible we're waiting on more than one item, so don't break.
681 }
682 }
683
684 void ChromeLauncherController::SetLauncherItemImage(
685 ash::ShelfID shelf_id,
686 const gfx::ImageSkia& image) {
687 int index = model_->ItemIndexByID(shelf_id);
688 if (index == -1)
689 return;
690 ash::ShelfItem item = model_->items()[index];
691 item.image = image;
692 model_->Set(index, item);
693 }
694
695 bool ChromeLauncherController::IsAppPinned(const std::string& app_id) {
696 for (IDToItemControllerMap::const_iterator i =
697 id_to_item_controller_map_.begin();
698 i != id_to_item_controller_map_.end(); ++i) {
699 if (IsPinned(i->first) && i->second->app_id() == app_id)
700 return true;
701 }
702 return false;
703 }
704
705 bool ChromeLauncherController::IsWindowedAppInLauncher(
706 const std::string& app_id) {
707 int index = model_->ItemIndexByID(GetShelfIDForAppID(app_id));
708 if (index < 0)
709 return false;
710
711 ash::ShelfItemType type = model_->items()[index].type;
712 return type == ash::TYPE_WINDOWED_APP;
713 }
714
715 void ChromeLauncherController::PinAppWithID(const std::string& app_id) {
716 if (GetPinnable(app_id) == AppListControllerDelegate::PIN_EDITABLE)
717 DoPinAppWithID(app_id);
718 else
719 NOTREACHED();
720 }
721
722 void ChromeLauncherController::SetLaunchType(
723 ash::ShelfID id,
724 extensions::LaunchType launch_type) {
725 LauncherItemController* controller = GetLauncherItemController(id);
726 if (!controller)
727 return;
728
729 extensions::SetLaunchType(profile_, controller->app_id(), launch_type);
730 }
731
732 void ChromeLauncherController::UnpinAppWithID(const std::string& app_id) {
733 if (GetPinnable(app_id) == AppListControllerDelegate::PIN_EDITABLE)
734 DoUnpinAppWithID(app_id);
735 else
736 NOTREACHED();
737 }
738
739 void ChromeLauncherController::OnSetShelfItemDelegate(
740 ash::ShelfID id,
741 ash::ShelfItemDelegate* item_delegate) {
742 // TODO(skuhne): This fixes crbug.com/429870, but it does not answer why we
743 // get into this state in the first place.
744 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
745 if (iter == id_to_item_controller_map_.end() || item_delegate == iter->second)
746 return;
747 LOG(ERROR) << "Unexpected change of shelf item id: " << id;
748 id_to_item_controller_map_.erase(iter);
749 }
750
751 void ChromeLauncherController::PersistPinnedState() {
752 if (ignore_persist_pinned_state_change_)
753 return;
754 // It is a coding error to call PersistPinnedState() if the pinned apps are
755 // not user-editable. The code should check earlier and not perform any
756 // modification actions that trigger persisting the state.
757 // Mutating kPinnedLauncherApps is going to notify us and trigger us to
758 // process the change. We don't want that to happen so remove ourselves as a
759 // listener.
760 pref_change_registrar_.Remove(prefs::kPinnedLauncherApps);
761 {
762 std::unique_ptr<const base::ListValue> pinned_apps_pref =
763 profile_->GetPrefs()
764 ->GetList(prefs::kPinnedLauncherApps)
765 ->CreateDeepCopy();
766
767 const base::ListValue* policy_pinned_apps_pref =
768 profile_->GetPrefs()->GetList(prefs::kPolicyPinnedLauncherApps);
769
770 ListPrefUpdate updater(profile_->GetPrefs(), prefs::kPinnedLauncherApps);
771 updater->Clear();
772 for (size_t i = 0; i < model_->items().size(); ++i) {
773 if (model_->items()[i].type == ash::TYPE_APP_SHORTCUT) {
774 ash::ShelfID id = model_->items()[i].id;
775 LauncherItemController* controller = GetLauncherItemController(id);
776 // Don't persist pinning state for apps that are handled internally and
777 // have pinnable state AppListControllerDelegate::NO_PIN.
778 if (controller && IsPinned(id) &&
779 GetPinnable(controller->app_id()) !=
780 AppListControllerDelegate::NO_PIN) {
781 base::DictionaryValue* app_value = ash::CreateAppDict(
782 controller->app_id());
783 if (app_value) {
784 if (!IsAppForUserPinned(controller->app_id(),
785 pinned_apps_pref.get(),
786 policy_pinned_apps_pref))
787 app_value->SetBoolean(ash::kPinnedAppsPrefPinnedByPolicy, true);
788 updater->Append(app_value);
789 }
790 }
791 } else if (model_->items()[i].type == ash::TYPE_BROWSER_SHORTCUT) {
792 PersistChromeItemIndex(i);
793 } else if (model_->items()[i].type == ash::TYPE_APP_LIST) {
794 base::DictionaryValue* app_value =
795 ash::CreateAppDict(ash::kPinnedAppsPlaceholder);
796 if (app_value)
797 updater->Append(app_value);
798 }
799 }
800 }
801 pref_change_registrar_.Add(
802 prefs::kPinnedLauncherApps,
803 base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref,
804 base::Unretained(this)));
805 }
806
807 Profile* ChromeLauncherController::profile() {
808 return profile_;
809 }
810
811 void ChromeLauncherController::UpdateAppState(content::WebContents* contents,
812 AppState app_state) {
813 std::string app_id = launcher_controller_helper_->GetAppID(contents);
814
815 // Check if the gMail app is loaded and it matches the given content.
816 // This special treatment is needed to address crbug.com/234268.
817 if (app_id.empty() && ContentCanBeHandledByGmailApp(contents))
818 app_id = kGmailAppId;
819
820 // Check the old |app_id| for a tab. If the contents has changed we need to
821 // remove it from the previous app.
822 if (web_contents_to_app_id_.find(contents) != web_contents_to_app_id_.end()) {
823 std::string last_app_id = web_contents_to_app_id_[contents];
824 if (last_app_id != app_id) {
825 ash::ShelfID id = GetShelfIDForAppID(last_app_id);
826 if (id) {
827 // Since GetAppState() will use |web_contents_to_app_id_| we remove
828 // the connection before calling it.
829 web_contents_to_app_id_.erase(contents);
830 SetItemStatus(id, GetAppState(last_app_id));
831 }
832 }
833 }
834
835 if (app_state == APP_STATE_REMOVED)
836 web_contents_to_app_id_.erase(contents);
837 else
838 web_contents_to_app_id_[contents] = app_id;
839
840 ash::ShelfID id = GetShelfIDForAppID(app_id);
841 if (id) {
842 SetItemStatus(id, (app_state == APP_STATE_WINDOW_ACTIVE ||
843 app_state == APP_STATE_ACTIVE) ? ash::STATUS_ACTIVE :
844 GetAppState(app_id));
845 }
846 }
847
848 ash::ShelfID ChromeLauncherController::GetShelfIDForWebContents(
849 content::WebContents* contents) {
850 DCHECK(contents);
851
852 std::string app_id = launcher_controller_helper_->GetAppID(contents);
853
854 if (app_id.empty() && ContentCanBeHandledByGmailApp(contents))
855 app_id = kGmailAppId;
856
857 ash::ShelfID id = GetShelfIDForAppID(app_id);
858
859 if (app_id.empty() || !id) {
860 int browser_index = model_->GetItemIndexForType(ash::TYPE_BROWSER_SHORTCUT);
861 return model_->items()[browser_index].id;
862 }
863
864 return id;
865 }
866
867 void ChromeLauncherController::SetRefocusURLPatternForTest(ash::ShelfID id,
868 const GURL& url) {
869 LauncherItemController* controller = GetLauncherItemController(id);
870 DCHECK(controller);
871
872 int index = model_->ItemIndexByID(id);
873 if (index == -1) {
874 NOTREACHED() << "Invalid launcher id";
875 return;
876 }
877
878 ash::ShelfItemType type = model_->items()[index].type;
879 if (type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_WINDOWED_APP) {
880 AppShortcutLauncherItemController* app_controller =
881 static_cast<AppShortcutLauncherItemController*>(controller);
882 app_controller->set_refocus_url(url);
883 } else {
884 NOTREACHED() << "Invalid launcher type";
885 }
886 }
887
888 const Extension* ChromeLauncherController::GetExtensionForAppID(
889 const std::string& app_id) const {
890 return extensions::ExtensionRegistry::Get(profile_)->GetExtensionById(
891 app_id, extensions::ExtensionRegistry::EVERYTHING);
892 }
893
894 ash::ShelfItemDelegate::PerformedAction
895 ChromeLauncherController::ActivateWindowOrMinimizeIfActive(
896 ui::BaseWindow* window,
897 bool allow_minimize) {
898 // In separated desktop mode we might have to teleport a window back to the
899 // current user.
900 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
901 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED) {
902 aura::Window* native_window = window->GetNativeWindow();
903 const AccountId& current_account_id =
904 multi_user_util::GetAccountIdFromProfile(profile());
905 chrome::MultiUserWindowManager* manager =
906 chrome::MultiUserWindowManager::GetInstance();
907 if (!manager->IsWindowOnDesktopOfUser(native_window, current_account_id)) {
908 ash::MultiProfileUMA::RecordTeleportAction(
909 ash::MultiProfileUMA::TELEPORT_WINDOW_RETURN_BY_LAUNCHER);
910 manager->ShowWindowForUser(native_window, current_account_id);
911 window->Activate();
912 return ash::ShelfItemDelegate::kExistingWindowActivated;
913 }
914 }
915
916 if (window->IsActive() && allow_minimize) {
917 window->Minimize();
918 return ash::ShelfItemDelegate::kNoAction;
919 }
920
921 window->Show();
922 window->Activate();
923 return ash::ShelfItemDelegate::kExistingWindowActivated;
924 }
925
926 void ChromeLauncherController::OnShelfCreated(ash::Shelf* shelf) {
927 PrefService* prefs = profile_->GetPrefs();
928 const int64_t display = GetDisplayIDForShelf(shelf);
929
930 shelf->SetAutoHideBehavior(ash::GetShelfAutoHideBehaviorPref(prefs, display));
931
932 if (ash::ShelfWidget::ShelfAlignmentAllowed())
933 shelf->SetAlignment(ash::GetShelfAlignmentPref(prefs, display));
934 }
935
936 void ChromeLauncherController::OnShelfDestroyed(ash::Shelf* shelf) {}
937
938 void ChromeLauncherController::OnShelfAlignmentChanged(ash::Shelf* shelf) {
939 ash::SetShelfAlignmentPref(profile_->GetPrefs(), GetDisplayIDForShelf(shelf),
940 shelf->alignment());
941 }
942
943 void ChromeLauncherController::OnShelfAutoHideBehaviorChanged(
944 ash::Shelf* shelf) {
945 ash::SetShelfAutoHideBehaviorPref(profile_->GetPrefs(),
946 GetDisplayIDForShelf(shelf),
947 shelf->auto_hide_behavior());
948 }
949
950 void ChromeLauncherController::OnShelfAutoHideStateChanged(ash::Shelf* shelf) {}
951
952 void ChromeLauncherController::OnShelfVisibilityStateChanged(
953 ash::Shelf* shelf) {}
954
955 void ChromeLauncherController::ShelfItemAdded(int index) {
956 // The app list launcher can get added to the shelf after we applied the
957 // preferences. In that case the item might be at the wrong spot. As such we
958 // call the function again.
959 if (model_->items()[index].type == ash::TYPE_APP_LIST)
960 UpdateAppLaunchersFromPref();
961 }
962
963 void ChromeLauncherController::ShelfItemRemoved(int index, ash::ShelfID id) {
964 // TODO(skuhne): This fixes crbug.com/429870, but it does not answer why we
965 // get into this state in the first place.
966 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
967 if (iter == id_to_item_controller_map_.end())
968 return;
969
970 LOG(ERROR) << "Unexpected change of shelf item id: " << id;
971
972 id_to_item_controller_map_.erase(iter);
973 }
974
975 void ChromeLauncherController::ShelfItemMoved(int start_index,
976 int target_index) {
977 const ash::ShelfItem& item = model_->items()[target_index];
978 // We remember the moved item position if it is either pinnable or
979 // it is the app list with the alternate shelf layout.
980 if ((HasShelfIDToAppIDMapping(item.id) && IsPinned(item.id)) ||
981 item.type == ash::TYPE_APP_LIST)
982 PersistPinnedState();
983 }
984
985 void ChromeLauncherController::ShelfItemChanged(
986 int index,
987 const ash::ShelfItem& old_item) {}
988
989 void ChromeLauncherController::ActiveUserChanged(
990 const std::string& user_email) {
991 // Store the order of running applications for the user which gets inactive.
992 RememberUnpinnedRunningApplicationOrder();
993 // Coming here the default profile is already switched. All profile specific
994 // resources get released and the new profile gets attached instead.
995 ReleaseProfile();
996 // When coming here, the active user has already be changed so that we can
997 // set it as active.
998 AttachProfile(ProfileManager::GetActiveUserProfile());
999 // Update the V1 applications.
1000 browser_status_monitor_->ActiveUserChanged(user_email);
1001 // Switch the running applications to the new user.
1002 for (auto& controller : app_window_controllers_)
1003 controller->ActiveUserChanged(user_email);
1004 // Update the user specific shell properties from the new user profile.
1005 UpdateAppLaunchersFromPref();
1006 SetShelfBehaviorsFromPrefs();
1007 SetVirtualKeyboardBehaviorFromPrefs();
1008
1009 // Restore the order of running, but unpinned applications for the activated
1010 // user.
1011 RestoreUnpinnedRunningApplicationOrder(user_email);
1012 // Inform the system tray of the change.
1013 ash::WmShell::Get()->system_tray_delegate()->ActiveUserWasChanged();
1014 // Force on-screen keyboard to reset.
1015 if (keyboard::IsKeyboardEnabled())
1016 ash::Shell::GetInstance()->CreateKeyboard();
1017 }
1018
1019 void ChromeLauncherController::AdditionalUserAddedToSession(Profile* profile) {
1020 // Switch the running applications to the new user.
1021 for (auto& controller : app_window_controllers_)
1022 controller->AdditionalUserAddedToSession(profile);
1023 }
1024
1025 void ChromeLauncherController::OnAppInstalled(
1026 content::BrowserContext* browser_context,
1027 const std::string& app_id) {
1028 if (IsAppPinned(app_id)) {
1029 // Clear and re-fetch to ensure icon is up-to-date.
1030 AppIconLoader* app_icon_loader = GetAppIconLoaderForApp(app_id);
1031 if (app_icon_loader) {
1032 app_icon_loader->ClearImage(app_id);
1033 app_icon_loader->FetchImage(app_id);
1034 }
1035 }
1036
1037 UpdateAppLaunchersFromPref();
1038 }
1039
1040 void ChromeLauncherController::OnAppUpdated(
1041 content::BrowserContext* browser_context,
1042 const std::string& app_id) {
1043 AppIconLoader* app_icon_loader = GetAppIconLoaderForApp(app_id);
1044 if (app_icon_loader)
1045 app_icon_loader->UpdateImage(app_id);
1046 }
1047
1048 void ChromeLauncherController::OnAppUninstalled(
1049 content::BrowserContext* browser_context,
1050 const std::string& app_id) {
1051 // Since we might have windowed apps of this type which might have
1052 // outstanding locks which needs to be removed.
1053 const Profile* profile = Profile::FromBrowserContext(browser_context);
1054 if (GetShelfIDForAppID(app_id))
1055 CloseWindowedAppsFromRemovedExtension(app_id, profile);
1056
1057 if (IsAppPinned(app_id)) {
1058 if (profile == profile_)
1059 DoUnpinAppWithID(app_id);
1060 AppIconLoader* app_icon_loader = GetAppIconLoaderForApp(app_id);
1061 if (app_icon_loader)
1062 app_icon_loader->ClearImage(app_id);
1063 }
1064 }
1065
1066 void ChromeLauncherController::OnDisplayConfigurationChanged() {
1067 SetShelfBehaviorsFromPrefs();
1068 }
1069
1070 void ChromeLauncherController::OnAppSyncUIStatusChanged() {
1071 if (app_sync_ui_state_->status() == AppSyncUIState::STATUS_SYNCING)
1072 model_->set_status(ash::ShelfModel::STATUS_LOADING);
1073 else
1074 model_->set_status(ash::ShelfModel::STATUS_NORMAL);
1075 }
1076
1077 ChromeLauncherAppMenuItems ChromeLauncherController::GetApplicationList(
1078 const ash::ShelfItem& item,
1079 int event_flags) {
1080 // Make sure that there is a controller associated with the id and that the
1081 // extension itself is a valid application and not a panel.
1082 LauncherItemController* controller = GetLauncherItemController(item.id);
1083 if (!controller || !GetShelfIDForAppID(controller->app_id()))
1084 return ChromeLauncherAppMenuItems();
1085
1086 return controller->GetApplicationList(event_flags);
1087 }
1088
1089 std::vector<content::WebContents*>
1090 ChromeLauncherController::GetV1ApplicationsFromAppId(
1091 const std::string& app_id) {
1092 ash::ShelfID id = GetShelfIDForAppID(app_id);
1093
1094 // If there is no such an item pinned to the launcher, no menu gets created.
1095 if (id) {
1096 LauncherItemController* controller = GetLauncherItemController(id);
1097 DCHECK(controller);
1098 if (controller->type() == LauncherItemController::TYPE_SHORTCUT)
1099 return GetV1ApplicationsFromController(controller);
1100 }
1101 return std::vector<content::WebContents*>();
1102 }
1103
1104 void ChromeLauncherController::ActivateShellApp(const std::string& app_id,
1105 int index) {
1106 ash::ShelfID id = GetShelfIDForAppID(app_id);
1107 if (id) {
1108 LauncherItemController* controller = GetLauncherItemController(id);
1109 if (controller && controller->type() == LauncherItemController::TYPE_APP) {
1110 AppWindowLauncherItemController* app_window_controller =
1111 static_cast<AppWindowLauncherItemController*>(controller);
1112 app_window_controller->ActivateIndexedApp(index);
1113 }
1114 }
1115 }
1116
1117 bool ChromeLauncherController::IsWebContentHandledByApplication(
1118 content::WebContents* web_contents,
1119 const std::string& app_id) {
1120 if ((web_contents_to_app_id_.find(web_contents) !=
1121 web_contents_to_app_id_.end()) &&
1122 (web_contents_to_app_id_[web_contents] == app_id))
1123 return true;
1124 return (app_id == kGmailAppId && ContentCanBeHandledByGmailApp(web_contents));
1125 }
1126
1127 bool ChromeLauncherController::ContentCanBeHandledByGmailApp(
1128 content::WebContents* web_contents) {
1129 ash::ShelfID id = GetShelfIDForAppID(kGmailAppId);
1130 if (id) {
1131 const GURL url = web_contents->GetURL();
1132 // We need to extend the application matching for the gMail app beyond the
1133 // manifest file's specification. This is required because of the namespace
1134 // overlap with the offline app ("/mail/mu/").
1135 if (!base::MatchPattern(url.path(), "/mail/mu/*") &&
1136 base::MatchPattern(url.path(), "/mail/*") &&
1137 GetExtensionForAppID(kGmailAppId) &&
1138 GetExtensionForAppID(kGmailAppId)->OverlapsWithOrigin(url))
1139 return true;
1140 }
1141 return false;
1142 }
1143
1144 gfx::Image ChromeLauncherController::GetAppListIcon(
1145 content::WebContents* web_contents) const {
1146 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
1147 if (IsIncognito(web_contents))
1148 return rb.GetImageNamed(IDR_ASH_SHELF_LIST_INCOGNITO_BROWSER);
1149 favicon::FaviconDriver* favicon_driver =
1150 favicon::ContentFaviconDriver::FromWebContents(web_contents);
1151 gfx::Image result = favicon_driver->GetFavicon();
1152 if (result.IsEmpty())
1153 return rb.GetImageNamed(IDR_DEFAULT_FAVICON);
1154 return result;
1155 }
1156
1157 base::string16 ChromeLauncherController::GetAppListTitle(
1158 content::WebContents* web_contents) const {
1159 base::string16 title = web_contents->GetTitle();
1160 if (!title.empty())
1161 return title;
1162 WebContentsToAppIDMap::const_iterator iter =
1163 web_contents_to_app_id_.find(web_contents);
1164 if (iter != web_contents_to_app_id_.end()) {
1165 std::string app_id = iter->second;
1166 const extensions::Extension* extension = GetExtensionForAppID(app_id);
1167 if (extension)
1168 return base::UTF8ToUTF16(extension->name());
1169 }
1170 return l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE);
1171 }
1172
1173 ash::ShelfID ChromeLauncherController::CreateAppShortcutLauncherItem(
1174 const std::string& app_id,
1175 int index) {
1176 return CreateAppShortcutLauncherItemWithType(app_id,
1177 index,
1178 ash::TYPE_APP_SHORTCUT);
1179 }
1180
1181 void ChromeLauncherController::SetLauncherControllerHelperForTest(
1182 LauncherControllerHelper* helper) {
1183 launcher_controller_helper_.reset(helper);
1184 }
1185
1186 void ChromeLauncherController::SetAppIconLoadersForTest(
1187 std::vector<std::unique_ptr<AppIconLoader>>& loaders) {
1188 app_icon_loaders_.clear();
1189 for (auto& loader : loaders)
1190 app_icon_loaders_.push_back(std::move(loader));
1191 }
1192
1193 const std::string& ChromeLauncherController::GetAppIdFromShelfIdForTest(
1194 ash::ShelfID id) {
1195 return id_to_item_controller_map_[id]->app_id();
1196 }
1197
1198 bool ChromeLauncherController::GetAppIDForShelfIDConst(
1199 ash::ShelfID id,
1200 std::string* app_id) const {
1201 auto app = id_to_item_controller_map_.find(id);
1202 if (app == id_to_item_controller_map_.end()) {
1203 return false;
1204 } else {
1205 *app_id = app->second->app_id();
1206 return true;
1207 }
1208 }
1209
1210 void ChromeLauncherController::SetShelfItemDelegateManagerForTest(
1211 ash::ShelfItemDelegateManager* manager) {
1212 if (item_delegate_manager_)
1213 item_delegate_manager_->RemoveObserver(this);
1214
1215 item_delegate_manager_ = manager;
1216
1217 if (item_delegate_manager_)
1218 item_delegate_manager_->AddObserver(this);
1219 }
1220
1221 void ChromeLauncherController::RememberUnpinnedRunningApplicationOrder() {
1222 RunningAppListIds list;
1223 for (int i = 0; i < model_->item_count(); i++) {
1224 ash::ShelfItemType type = model_->items()[i].type;
1225 if (type == ash::TYPE_WINDOWED_APP || type == ash::TYPE_PLATFORM_APP)
1226 list.push_back(GetAppIDForShelfID(model_->items()[i].id));
1227 }
1228 const std::string user_email =
1229 multi_user_util::GetAccountIdFromProfile(profile_).GetUserEmail();
1230 last_used_running_application_order_[user_email] = list;
1231 }
1232
1233 void ChromeLauncherController::RestoreUnpinnedRunningApplicationOrder(
1234 const std::string& user_id) {
1235 const RunningAppListIdMap::iterator app_id_list =
1236 last_used_running_application_order_.find(user_id);
1237 if (app_id_list == last_used_running_application_order_.end())
1238 return;
1239
1240 // Find the first insertion point for running applications.
1241 int running_index = model_->FirstRunningAppIndex();
1242 for (RunningAppListIds::iterator app_id = app_id_list->second.begin();
1243 app_id != app_id_list->second.end(); ++app_id) {
1244 ash::ShelfID shelf_id = GetShelfIDForAppID(*app_id);
1245 if (shelf_id) {
1246 int app_index = model_->ItemIndexByID(shelf_id);
1247 DCHECK_GE(app_index, 0);
1248 ash::ShelfItemType type = model_->items()[app_index].type;
1249 if (type == ash::TYPE_WINDOWED_APP || type == ash::TYPE_PLATFORM_APP) {
1250 if (running_index != app_index)
1251 model_->Move(running_index, app_index);
1252 running_index++;
1253 }
1254 }
1255 }
1256 }
1257
1258 ash::ShelfID ChromeLauncherController::CreateAppShortcutLauncherItemWithType(
1259 const std::string& app_id,
1260 int index,
1261 ash::ShelfItemType shelf_item_type) {
1262 AppShortcutLauncherItemController* controller =
1263 AppShortcutLauncherItemController::Create(app_id, this);
1264 ash::ShelfID shelf_id = InsertAppLauncherItem(
1265 controller, app_id, ash::STATUS_CLOSED, index, shelf_item_type);
1266 return shelf_id;
1267 }
1268
1269 LauncherItemController* ChromeLauncherController::GetLauncherItemController(
1270 const ash::ShelfID id) {
1271 if (!HasShelfIDToAppIDMapping(id))
1272 return NULL;
1273 return id_to_item_controller_map_[id];
1274 }
1275
1276 bool ChromeLauncherController::IsBrowserFromActiveUser(Browser* browser) {
1277 // If running multi user mode with separate desktops, we have to check if the
1278 // browser is from the active user.
1279 if (chrome::MultiUserWindowManager::GetMultiProfileMode() !=
1280 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED)
1281 return true;
1282 return multi_user_util::IsProfileFromActiveUser(browser->profile());
1283 }
1284
1285 bool ChromeLauncherController::ShelfBoundsChangesProbablyWithUser(
1286 ash::Shelf* shelf,
1287 const std::string& user_id) const {
1288 Profile* other_profile = multi_user_util::GetProfileFromAccountId(
1289 AccountId::FromUserEmail(user_id));
1290 if (other_profile == profile_)
1291 return false;
1292
1293 // Note: The Auto hide state from preferences is not the same as the actual
1294 // visibility of the shelf. Depending on all the various states (full screen,
1295 // no window on desktop, multi user, ..) the shelf could be shown - or not.
1296 PrefService* prefs = profile_->GetPrefs();
1297 PrefService* other_prefs = other_profile->GetPrefs();
1298 const int64_t display = GetDisplayIDForShelf(shelf);
1299 bool currently_shown = ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER ==
1300 ash::GetShelfAutoHideBehaviorPref(prefs, display);
1301 bool other_shown = ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER ==
1302 ash::GetShelfAutoHideBehaviorPref(other_prefs, display);
1303
1304 return currently_shown != other_shown ||
1305 ash::GetShelfAlignmentPref(prefs, display) !=
1306 ash::GetShelfAlignmentPref(other_prefs, display);
1307 }
1308
1309 void ChromeLauncherController::OnUserProfileReadyToSwitch(Profile* profile) {
1310 if (user_switch_observer_.get())
1311 user_switch_observer_->OnUserProfileReadyToSwitch(profile);
1312 }
1313
1314 void ChromeLauncherController::LauncherItemClosed(ash::ShelfID id) {
1315 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
1316 CHECK(iter != id_to_item_controller_map_.end());
1317 CHECK(iter->second);
1318 const std::string& app_id = iter->second->app_id();
1319 AppIconLoader* app_icon_loader = GetAppIconLoaderForApp(app_id);
1320 if (app_icon_loader)
1321 app_icon_loader->ClearImage(app_id);
1322 id_to_item_controller_map_.erase(iter);
1323 int index = model_->ItemIndexByID(id);
1324 // A "browser proxy" is not known to the model and this removal does
1325 // therefore not need to be propagated to the model.
1326 if (index != -1)
1327 model_->RemoveItemAt(index);
1328 }
1329
1330 void ChromeLauncherController::DoPinAppWithID(const std::string& app_id) {
1331 // If there is an item, do nothing and return.
1332 if (IsAppPinned(app_id))
1333 return;
1334
1335 ash::ShelfID shelf_id = GetShelfIDForAppID(app_id);
1336 if (shelf_id) {
1337 // App item exists, pin it
1338 Pin(shelf_id);
1339 } else {
1340 // Otherwise, create a shortcut item for it.
1341 shelf_id = CreateAppShortcutLauncherItem(app_id, model_->item_count());
1342 if (GetPinnable(app_id) == AppListControllerDelegate::PIN_EDITABLE)
1343 PersistPinnedState();
1344 }
1345 }
1346
1347 void ChromeLauncherController::DoUnpinAppWithID(const std::string& app_id) {
1348 ash::ShelfID shelf_id = GetShelfIDForAppID(app_id);
1349 if (shelf_id && IsPinned(shelf_id))
1350 Unpin(shelf_id);
1351 }
1352
1353 int ChromeLauncherController::PinRunningAppInternal(int index,
1354 ash::ShelfID shelf_id) {
1355 int running_index = model_->ItemIndexByID(shelf_id);
1356 ash::ShelfItem item = model_->items()[running_index];
1357 DCHECK(item.type == ash::TYPE_WINDOWED_APP ||
1358 item.type == ash::TYPE_PLATFORM_APP);
1359 item.type = ash::TYPE_APP_SHORTCUT;
1360 model_->Set(running_index, item);
1361 // The |ShelfModel|'s weight system might reposition the item to a
1362 // new index, so we get the index again.
1363 running_index = model_->ItemIndexByID(shelf_id);
1364 if (running_index < index)
1365 --index;
1366 if (running_index != index)
1367 model_->Move(running_index, index);
1368 return index;
1369 }
1370
1371 void ChromeLauncherController::UnpinRunningAppInternal(int index) {
1372 DCHECK_GE(index, 0);
1373 ash::ShelfItem item = model_->items()[index];
1374 DCHECK_EQ(item.type, ash::TYPE_APP_SHORTCUT);
1375 item.type = ash::TYPE_WINDOWED_APP;
1376 // A platform app and a windowed app are sharing TYPE_APP_SHORTCUT. As such
1377 // we have to check here what this was before it got a shortcut.
1378 LauncherItemController* controller = GetLauncherItemController(item.id);
1379 if (controller && controller->type() == LauncherItemController::TYPE_APP)
1380 item.type = ash::TYPE_PLATFORM_APP;
1381 model_->Set(index, item);
1382 }
1383
1384 void ChromeLauncherController::UpdateAppLaunchersFromPref() {
1385 // There are various functions which will trigger a |PersistPinnedState| call
1386 // like a direct call to |DoPinAppWithID|, or an indirect call to the menu
1387 // model which will use weights to re-arrange the icons to new positions.
1388 // Since this function is meant to synchronize the "is state" with the
1389 // "sync state", it makes no sense to store any changes by this function back
1390 // into the pref state. Therefore we tell |persistPinnedState| to ignore any
1391 // invocations while we are running.
1392 base::AutoReset<bool> auto_reset(&ignore_persist_pinned_state_change_, true);
1393 std::vector<std::string> pinned_apps = ash::GetPinnedAppsFromPrefs(
1394 profile_->GetPrefs(), launcher_controller_helper_.get());
1395
1396 int index = 0;
1397 int max_index = model_->item_count();
1398
1399 // When one of the two special items cannot be moved (and we do not know where
1400 // yet), we remember the current location in one of these variables.
1401 int chrome_index = -1;
1402 int app_list_index = -1;
1403
1404 // Walk the model and |pinned_apps| from the pref lockstep, adding and
1405 // removing items as necessary. NB: This code uses plain old indexing instead
1406 // of iterators because of model mutations as part of the loop.
1407 std::vector<std::string>::const_iterator pref_app_id(pinned_apps.begin());
1408 for (; index < max_index && pref_app_id != pinned_apps.end(); ++index) {
1409 // Check if we have an item which we need to handle.
1410 if (*pref_app_id == extension_misc::kChromeAppId ||
1411 *pref_app_id == ash::kPinnedAppsPlaceholder ||
1412 IsAppPinned(*pref_app_id)) {
1413 for (; index < max_index; ++index) {
1414 const ash::ShelfItem& item(model_->items()[index]);
1415 bool is_app_list = item.type == ash::TYPE_APP_LIST;
1416 bool is_chrome = item.type == ash::TYPE_BROWSER_SHORTCUT;
1417 if (item.type != ash::TYPE_APP_SHORTCUT && !is_app_list && !is_chrome)
1418 continue;
1419 LauncherItemController* controller = GetLauncherItemController(item.id);
1420 if ((ash::kPinnedAppsPlaceholder == *pref_app_id && is_app_list) ||
1421 (extension_misc::kChromeAppId == *pref_app_id && is_chrome) ||
1422 (controller && controller->app_id() == *pref_app_id)) {
1423 // Check if an item needs to be moved here.
1424 MoveChromeOrApplistToFinalPosition(
1425 is_chrome, is_app_list, index, &chrome_index, &app_list_index);
1426 ++pref_app_id;
1427 break;
1428 } else {
1429 if (is_chrome || is_app_list) {
1430 // We cannot delete any of these shortcuts. As such we remember
1431 // their positions and move them later where they belong.
1432 if (is_chrome)
1433 chrome_index = index;
1434 else
1435 app_list_index = index;
1436 // And skip the item - or exit the loop if end is reached (note that
1437 // in that case we will reduce the index again by one and this only
1438 // compensates for it).
1439 if (index >= max_index - 1)
1440 break;
1441 ++index;
1442 } else {
1443 // Check if this is a platform or a windowed app.
1444 if (item.type == ash::TYPE_APP_SHORTCUT &&
1445 controller &&
1446 (controller->locked() ||
1447 controller->type() == LauncherItemController::TYPE_APP)) {
1448 // Note: This will not change the amount of items (|max_index|).
1449 // Even changes to the actual |index| due to item weighting
1450 // changes should be fine.
1451 UnpinRunningAppInternal(index);
1452 } else {
1453 if (controller)
1454 LauncherItemClosed(item.id);
1455 --max_index;
1456 }
1457 }
1458 --index;
1459 }
1460 }
1461 // If the item wasn't found, that means id_to_item_controller_map_
1462 // is out of sync.
1463 DCHECK(index <= max_index);
1464 } else {
1465 // Check if the item was already running but not yet pinned.
1466 ash::ShelfID shelf_id = GetShelfIDForAppID(*pref_app_id);
1467 if (shelf_id) {
1468 // This app is running but not yet pinned. So pin and move it.
1469 index = PinRunningAppInternal(index, shelf_id);
1470 } else {
1471 // This app wasn't pinned before, insert a new entry.
1472 shelf_id = CreateAppShortcutLauncherItem(*pref_app_id, index);
1473 ++max_index;
1474 index = model_->ItemIndexByID(shelf_id);
1475 }
1476 ++pref_app_id;
1477 }
1478 }
1479
1480 // Remove any trailing existing items.
1481 while (index < model_->item_count()) {
1482 const ash::ShelfItem& item(model_->items()[index]);
1483 if (item.type == ash::TYPE_APP_SHORTCUT) {
1484 LauncherItemController* controller = GetLauncherItemController(item.id);
1485 if (controller) {
1486 if (controller->locked() ||
1487 controller->type() == LauncherItemController::TYPE_APP) {
1488 UnpinRunningAppInternal(index);
1489 } else {
1490 LauncherItemClosed(item.id);
1491 }
1492 }
1493 } else {
1494 if (item.type == ash::TYPE_BROWSER_SHORTCUT)
1495 chrome_index = index;
1496 else if (item.type == ash::TYPE_APP_LIST)
1497 app_list_index = index;
1498 ++index;
1499 }
1500 }
1501
1502 // Append unprocessed items from the pref to the end of the model.
1503 for (; pref_app_id != pinned_apps.end(); ++pref_app_id) {
1504 // All items but the chrome and / or app list shortcut needs to be added.
1505 bool is_chrome = *pref_app_id == extension_misc::kChromeAppId;
1506 bool is_app_list = *pref_app_id == ash::kPinnedAppsPlaceholder;
1507 // Coming here we know the next item which can be finalized, either the
1508 // chrome item or the app launcher. The final position is the end of the
1509 // list. The menu model will make sure that the item is grouped according
1510 // to its weight (which we do not know here).
1511 if (!is_chrome && !is_app_list) {
1512 DoPinAppWithID(*pref_app_id);
1513 int target_index = FindInsertionPoint(false);
1514 ash::ShelfID id = GetShelfIDForAppID(*pref_app_id);
1515 int source_index = model_->ItemIndexByID(id);
1516 if (source_index != target_index)
1517 model_->Move(source_index, target_index);
1518
1519 // Needed for the old layout - the weight might force it to be lower in
1520 // rank.
1521 if (app_list_index != -1 && target_index <= app_list_index)
1522 ++app_list_index;
1523 } else {
1524 int target_index = FindInsertionPoint(is_app_list);
1525 MoveChromeOrApplistToFinalPosition(
1526 is_chrome, is_app_list, target_index, &chrome_index, &app_list_index);
1527 }
1528 }
1529 }
1530
1531 void ChromeLauncherController::SetShelfAutoHideBehaviorFromPrefs() {
1532 for (auto* window : ash::Shell::GetAllRootWindows()) {
1533 ash::Shelf* shelf = ash::Shelf::ForWindow(window);
1534 if (shelf) {
1535 shelf->SetAutoHideBehavior(ash::GetShelfAutoHideBehaviorPref(
1536 profile_->GetPrefs(), GetDisplayIDForShelf(shelf)));
1537 }
1538 }
1539 }
1540
1541 void ChromeLauncherController::SetShelfAlignmentFromPrefs() {
1542 if (!ash::ShelfWidget::ShelfAlignmentAllowed())
1543 return;
1544
1545 for (auto* window : ash::Shell::GetAllRootWindows()) {
1546 ash::Shelf* shelf = ash::Shelf::ForWindow(window);
1547 if (shelf) {
1548 shelf->SetAlignment(ash::GetShelfAlignmentPref(
1549 profile_->GetPrefs(), GetDisplayIDForShelf(shelf)));
1550 }
1551 }
1552 }
1553
1554 void ChromeLauncherController::SetShelfBehaviorsFromPrefs() {
1555 SetShelfAutoHideBehaviorFromPrefs();
1556 SetShelfAlignmentFromPrefs();
1557 }
1558
1559 void ChromeLauncherController::SetVirtualKeyboardBehaviorFromPrefs() {
1560 const PrefService* service = profile_->GetPrefs();
1561 const bool was_enabled = keyboard::IsKeyboardEnabled();
1562 if (!service->HasPrefPath(prefs::kTouchVirtualKeyboardEnabled)) {
1563 keyboard::SetKeyboardShowOverride(keyboard::KEYBOARD_SHOW_OVERRIDE_NONE);
1564 } else {
1565 const bool enable = service->GetBoolean(
1566 prefs::kTouchVirtualKeyboardEnabled);
1567 keyboard::SetKeyboardShowOverride(
1568 enable ? keyboard::KEYBOARD_SHOW_OVERRIDE_ENABLED
1569 : keyboard::KEYBOARD_SHOW_OVERRIDE_DISABLED);
1570 }
1571 const bool is_enabled = keyboard::IsKeyboardEnabled();
1572 if (was_enabled && !is_enabled)
1573 ash::Shell::GetInstance()->DeactivateKeyboard();
1574 else if (is_enabled && !was_enabled)
1575 ash::Shell::GetInstance()->CreateKeyboard();
1576 }
1577
1578 ash::ShelfItemStatus ChromeLauncherController::GetAppState(
1579 const std::string& app_id) {
1580 ash::ShelfItemStatus status = ash::STATUS_CLOSED;
1581 for (WebContentsToAppIDMap::iterator it = web_contents_to_app_id_.begin();
1582 it != web_contents_to_app_id_.end();
1583 ++it) {
1584 if (it->second == app_id) {
1585 Browser* browser = chrome::FindBrowserWithWebContents(it->first);
1586 // Usually there should never be an item in our |web_contents_to_app_id_|
1587 // list which got deleted already. However - in some situations e.g.
1588 // Browser::SwapTabContent there is temporarily no associated browser.
1589 if (!browser)
1590 continue;
1591 if (browser->window()->IsActive()) {
1592 return browser->tab_strip_model()->GetActiveWebContents() == it->first ?
1593 ash::STATUS_ACTIVE : ash::STATUS_RUNNING;
1594 } else {
1595 status = ash::STATUS_RUNNING;
1596 }
1597 }
1598 }
1599 return status;
1600 }
1601
1602 ash::ShelfID ChromeLauncherController::InsertAppLauncherItem(
1603 LauncherItemController* controller,
1604 const std::string& app_id,
1605 ash::ShelfItemStatus status,
1606 int index,
1607 ash::ShelfItemType shelf_item_type) {
1608 ash::ShelfID id = model_->next_id();
1609 CHECK(!HasShelfIDToAppIDMapping(id));
1610 CHECK(controller);
1611 id_to_item_controller_map_[id] = controller;
1612 controller->set_shelf_id(id);
1613
1614 ash::ShelfItem item;
1615 item.type = shelf_item_type;
1616 item.image = extensions::util::GetDefaultAppIcon();
1617
1618 ash::ShelfItemStatus new_state = GetAppState(app_id);
1619 if (new_state != ash::STATUS_CLOSED)
1620 status = new_state;
1621
1622 item.status = status;
1623
1624 model_->AddAt(index, item);
1625
1626 AppIconLoader* app_icon_loader = GetAppIconLoaderForApp(app_id);
1627 if (app_icon_loader) {
1628 app_icon_loader->FetchImage(app_id);
1629 app_icon_loader->UpdateImage(app_id);
1630 }
1631
1632 SetShelfItemDelegate(id, controller);
1633
1634 return id;
1635 }
1636
1637 std::vector<content::WebContents*>
1638 ChromeLauncherController::GetV1ApplicationsFromController(
1639 LauncherItemController* controller) {
1640 DCHECK(controller->type() == LauncherItemController::TYPE_SHORTCUT);
1641 AppShortcutLauncherItemController* app_controller =
1642 static_cast<AppShortcutLauncherItemController*>(controller);
1643 return app_controller->GetRunningApplications();
1644 }
1645
1646 BrowserShortcutLauncherItemController*
1647 ChromeLauncherController::GetBrowserShortcutLauncherItemController() {
1648 for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin();
1649 i != id_to_item_controller_map_.end(); ++i) {
1650 int index = model_->ItemIndexByID(i->first);
1651 const ash::ShelfItem& item = model_->items()[index];
1652 if (item.type == ash::TYPE_BROWSER_SHORTCUT)
1653 return static_cast<BrowserShortcutLauncherItemController*>(i->second);
1654 }
1655 NOTREACHED()
1656 << "There should be always be a BrowserShortcutLauncherItemController.";
1657 return nullptr;
1658 }
1659
1660 ash::ShelfID ChromeLauncherController::CreateBrowserShortcutLauncherItem() {
1661 ash::ShelfItem browser_shortcut;
1662 browser_shortcut.type = ash::TYPE_BROWSER_SHORTCUT;
1663 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
1664 browser_shortcut.image = *rb.GetImageSkiaNamed(IDR_PRODUCT_LOGO_32);
1665 ash::ShelfID id = model_->next_id();
1666 size_t index = GetChromeIconIndexForCreation();
1667 model_->AddAt(index, browser_shortcut);
1668 id_to_item_controller_map_[id] =
1669 new BrowserShortcutLauncherItemController(this, model_);
1670 id_to_item_controller_map_[id]->set_shelf_id(id);
1671 // ShelfItemDelegateManager owns BrowserShortcutLauncherItemController.
1672 SetShelfItemDelegate(id, id_to_item_controller_map_[id]);
1673 return id;
1674 }
1675
1676 void ChromeLauncherController::PersistChromeItemIndex(int index) {
1677 profile_->GetPrefs()->SetInteger(prefs::kShelfChromeIconIndex, index);
1678 }
1679
1680 void ChromeLauncherController::MoveChromeOrApplistToFinalPosition(
1681 bool is_chrome,
1682 bool is_app_list,
1683 int target_index,
1684 int* chrome_index,
1685 int* app_list_index) {
1686 if (is_chrome && *chrome_index != -1) {
1687 model_->Move(*chrome_index, target_index);
1688 if (*app_list_index != -1 &&
1689 *chrome_index < *app_list_index &&
1690 target_index > *app_list_index)
1691 --(*app_list_index);
1692 *chrome_index = -1;
1693 } else if (is_app_list && *app_list_index != -1) {
1694 model_->Move(*app_list_index, target_index);
1695 if (*chrome_index != -1 &&
1696 *app_list_index < *chrome_index &&
1697 target_index > *chrome_index)
1698 --(*chrome_index);
1699 *app_list_index = -1;
1700 }
1701 }
1702
1703 int ChromeLauncherController::FindInsertionPoint(bool is_app_list) {
1704 // Keeping this change small to backport to M33&32 (see crbug.com/329597).
1705 // TODO(skuhne): With the removal of the legacy shelf layout we should remove
1706 // the ability to move the app list item since this was never used. We should
1707 // instead ask the ShelfModel::ValidateInsertionIndex or similir for an index.
1708 if (is_app_list)
1709 return 0;
1710
1711 for (int i = model_->item_count() - 1; i > 0; --i) {
1712 ash::ShelfItemType type = model_->items()[i].type;
1713 if (type == ash::TYPE_APP_SHORTCUT ||
1714 (is_app_list && type == ash::TYPE_APP_LIST) ||
1715 type == ash::TYPE_BROWSER_SHORTCUT) {
1716 return i;
1717 }
1718 }
1719 return 0;
1720 }
1721
1722 int ChromeLauncherController::GetChromeIconIndexForCreation() {
1723 // We get the list of pinned apps as they currently would get pinned.
1724 // Within this list the chrome icon will be the correct location.
1725 std::vector<std::string> pinned_apps = ash::GetPinnedAppsFromPrefs(
1726 profile_->GetPrefs(), launcher_controller_helper_.get());
1727
1728 std::vector<std::string>::iterator it =
1729 std::find(pinned_apps.begin(),
1730 pinned_apps.end(),
1731 std::string(extension_misc::kChromeAppId));
1732 DCHECK(it != pinned_apps.end());
1733 int index = it - pinned_apps.begin();
1734
1735 // We should do here a comparison between the is state and the "want to be"
1736 // state since some apps might be able to pin but are not yet. Instead - for
1737 // the time being we clamp against the amount of known items and wait for the
1738 // next |UpdateAppLaunchersFromPref()| call to correct it - it will come since
1739 // the pinning will be done then.
1740 return std::min(model_->item_count(), index);
1741 }
1742
1743 bool ChromeLauncherController::IsIncognito(
1744 const content::WebContents* web_contents) const {
1745 const Profile* profile =
1746 Profile::FromBrowserContext(web_contents->GetBrowserContext());
1747 return profile->IsOffTheRecord() && !profile->IsGuestSession() &&
1748 !profile->IsSystemProfile();
1749 }
1750
1751 void ChromeLauncherController::CloseWindowedAppsFromRemovedExtension(
1752 const std::string& app_id,
1753 const Profile* profile) {
1754 // This function cannot rely on the controller's enumeration functionality
1755 // since the extension has already be unloaded.
1756 const BrowserList* browser_list = BrowserList::GetInstance();
1757 std::vector<Browser*> browser_to_close;
1758 for (BrowserList::const_reverse_iterator it =
1759 browser_list->begin_last_active();
1760 it != browser_list->end_last_active(); ++it) {
1761 Browser* browser = *it;
1762 if (!browser->is_type_tabbed() && browser->is_type_popup() &&
1763 browser->is_app() &&
1764 app_id ==
1765 web_app::GetExtensionIdFromApplicationName(browser->app_name()) &&
1766 profile == browser->profile()) {
1767 browser_to_close.push_back(browser);
1768 }
1769 }
1770 while (!browser_to_close.empty()) {
1771 TabStripModel* tab_strip = browser_to_close.back()->tab_strip_model();
1772 tab_strip->CloseWebContentsAt(0, TabStripModel::CLOSE_NONE);
1773 browser_to_close.pop_back();
1774 }
1775 }
1776
1777 void ChromeLauncherController::SetShelfItemDelegate(
1778 ash::ShelfID id,
1779 ash::ShelfItemDelegate* item_delegate) {
1780 DCHECK_GT(id, 0);
1781 DCHECK(item_delegate);
1782 DCHECK(item_delegate_manager_);
1783 item_delegate_manager_->SetShelfItemDelegate(
1784 id, std::unique_ptr<ash::ShelfItemDelegate>(item_delegate));
1785 }
1786
1787 void ChromeLauncherController::AttachProfile(Profile* profile) {
1788 profile_ = profile;
1789 // Either add the profile to the list of known profiles and make it the active
1790 // one for some functions of LauncherControllerHelper or create a new one.
1791 if (!launcher_controller_helper_.get())
1792 launcher_controller_helper_.reset(new LauncherControllerHelper(profile_));
1793 else
1794 launcher_controller_helper_->SetCurrentUser(profile_);
1795 // TODO(skuhne): The AppIconLoaderImpl has the same problem. Each loaded
1796 // image is associated with a profile (its loader requires the profile).
1797 // Since icon size changes are possible, the icon could be requested to be
1798 // reloaded. However - having it not multi profile aware would cause problems
1799 // if the icon cache gets deleted upon user switch.
1800 std::unique_ptr<AppIconLoader> extension_app_icon_loader(
1801 new extensions::ExtensionAppIconLoader(
1802 profile_, extension_misc::EXTENSION_ICON_SMALL, this));
1803 app_icon_loaders_.push_back(std::move(extension_app_icon_loader));
1804
1805 if (arc::ArcAuthService::IsAllowedForProfile(profile_)) {
1806 DCHECK(arc_deferred_launcher());
1807 std::unique_ptr<AppIconLoader> arc_app_icon_loader(
1808 new ArcAppIconLoader(profile_, extension_misc::EXTENSION_ICON_SMALL,
1809 arc_deferred_launcher(), this));
1810 app_icon_loaders_.push_back(std::move(arc_app_icon_loader));
1811 }
1812
1813 pref_change_registrar_.Init(profile_->GetPrefs());
1814 pref_change_registrar_.Add(
1815 prefs::kPinnedLauncherApps,
1816 base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref,
1817 base::Unretained(this)));
1818 pref_change_registrar_.Add(
1819 prefs::kPolicyPinnedLauncherApps,
1820 base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref,
1821 base::Unretained(this)));
1822 pref_change_registrar_.Add(
1823 prefs::kShelfAlignmentLocal,
1824 base::Bind(&ChromeLauncherController::SetShelfAlignmentFromPrefs,
1825 base::Unretained(this)));
1826 pref_change_registrar_.Add(
1827 prefs::kShelfAutoHideBehaviorLocal,
1828 base::Bind(&ChromeLauncherController::
1829 SetShelfAutoHideBehaviorFromPrefs,
1830 base::Unretained(this)));
1831 pref_change_registrar_.Add(
1832 prefs::kShelfPreferences,
1833 base::Bind(&ChromeLauncherController::SetShelfBehaviorsFromPrefs,
1834 base::Unretained(this)));
1835 pref_change_registrar_.Add(
1836 prefs::kTouchVirtualKeyboardEnabled,
1837 base::Bind(&ChromeLauncherController::SetVirtualKeyboardBehaviorFromPrefs,
1838 base::Unretained(this)));
1839
1840 std::unique_ptr<LauncherAppUpdater> extension_app_updater(
1841 new LauncherExtensionAppUpdater(this, profile_));
1842 app_updaters_.push_back(std::move(extension_app_updater));
1843
1844 if (arc::ArcAuthService::IsAllowedForProfile(profile_)) {
1845 std::unique_ptr<LauncherAppUpdater> arc_app_updater(
1846 new LauncherArcAppUpdater(this, profile_));
1847 app_updaters_.push_back(std::move(arc_app_updater));
1848 }
1849 }
1850
1851 void ChromeLauncherController::ReleaseProfile() {
1852 if (app_sync_ui_state_)
1853 app_sync_ui_state_->RemoveObserver(this);
1854
1855 app_updaters_.clear();
1856
1857 prefs_observer_.reset();
1858
1859 pref_change_registrar_.RemoveAll();
1860 }
1861
1862 AppIconLoader* ChromeLauncherController::GetAppIconLoaderForApp(
1863 const std::string& app_id) {
1864 for (const auto& app_icon_loader : app_icon_loaders_) {
1865 if (app_icon_loader->CanLoadImageForApp(app_id))
1866 return app_icon_loader.get();
1867 }
1868
1869 return nullptr;
1870 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698