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

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

Powered by Google App Engine
This is Rietveld 408576698