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

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

Issue 2839933005: mash: Merge ChromeLauncherController and *Impl subclass. (Closed)
Patch Set: Address comments. Created 3 years, 8 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 "ash/multi_profile_uma.h"
8 #include "ash/public/cpp/shelf_item.h"
7 #include "ash/public/interfaces/constants.mojom.h" 9 #include "ash/public/interfaces/constants.mojom.h"
10 #include "ash/resources/grit/ash_resources.h"
11 #include "ash/shelf/shelf_model.h"
12 #include "ash/shelf/wm_shelf.h"
13 #include "ash/shell.h"
14 #include "ash/shell_port.h"
15 #include "ash/strings/grit/ash_strings.h"
16 #include "ash/system/tray/system_tray_delegate.h"
17 #include "ash/wm_window.h"
8 #include "base/memory/ptr_util.h" 18 #include "base/memory/ptr_util.h"
19 #include "base/strings/pattern.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/threading/thread_task_runner_handle.h"
9 #include "chrome/browser/chromeos/arc/arc_util.h" 23 #include "chrome/browser/chromeos/arc/arc_util.h"
10 #include "chrome/browser/extensions/extension_app_icon_loader.h" 24 #include "chrome/browser/extensions/extension_app_icon_loader.h"
25 #include "chrome/browser/extensions/extension_util.h"
26 #include "chrome/browser/prefs/pref_service_syncable_util.h"
11 #include "chrome/browser/profiles/profile.h" 27 #include "chrome/browser/profiles/profile.h"
28 #include "chrome/browser/profiles/profile_manager.h"
29 #include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
12 #include "chrome/browser/ui/app_list/arc/arc_app_icon_loader.h" 30 #include "chrome/browser/ui/app_list/arc/arc_app_icon_loader.h"
31 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
32 #include "chrome/browser/ui/ash/app_sync_ui_state.h"
33 #include "chrome/browser/ui/ash/ash_util.h"
13 #include "chrome/browser/ui/ash/chrome_launcher_prefs.h" 34 #include "chrome/browser/ui/ash/chrome_launcher_prefs.h"
35 #include "chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.h "
36 #include "chrome/browser/ui/ash/launcher/app_window_launcher_controller.h"
37 #include "chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h"
38 #include "chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_controller.h"
39 #include "chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.h"
40 #include "chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controll er.h"
41 #include "chrome/browser/ui/ash/launcher/browser_status_monitor.h"
42 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_util.h"
43 #include "chrome/browser/ui/ash/launcher/launcher_arc_app_updater.h"
14 #include "chrome/browser/ui/ash/launcher/launcher_controller_helper.h" 44 #include "chrome/browser/ui/ash/launcher/launcher_controller_helper.h"
45 #include "chrome/browser/ui/ash/launcher/launcher_extension_app_updater.h"
46 #include "chrome/browser/ui/ash/launcher/multi_profile_app_window_launcher_contr oller.h"
47 #include "chrome/browser/ui/ash/launcher/multi_profile_browser_status_monitor.h"
48 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
49 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager.h"
50 #include "chrome/browser/ui/browser.h"
51 #include "chrome/browser/ui/browser_finder.h"
52 #include "chrome/browser/ui/browser_list.h"
53 #include "chrome/browser/ui/browser_window.h"
54 #include "chrome/browser/ui/tabs/tab_strip_model.h"
55 #include "chrome/browser/web_applications/web_app.h"
56 #include "chrome/common/pref_names.h"
57 #include "chrome/grit/chromium_strings.h"
58 #include "chrome/grit/generated_resources.h"
59 #include "chrome/grit/theme_resources.h"
60 #include "components/favicon/content/content_favicon_driver.h"
61 #include "components/signin/core/account_id/account_id.h"
62 #include "components/strings/grit/components_strings.h"
63 #include "components/sync_preferences/pref_service_syncable.h"
64 #include "components/user_manager/user_manager.h"
65 #include "content/public/browser/web_contents.h"
15 #include "content/public/common/service_manager_connection.h" 66 #include "content/public/common/service_manager_connection.h"
67 #include "extensions/common/extension.h"
16 #include "services/service_manager/public/cpp/connector.h" 68 #include "services/service_manager/public/cpp/connector.h"
69 #include "ui/aura/window.h"
70 #include "ui/base/l10n/l10n_util.h"
71 #include "ui/base/resource/resource_bundle.h"
17 #include "ui/display/display.h" 72 #include "ui/display/display.h"
18 #include "ui/display/screen.h" 73 #include "ui/display/screen.h"
74 #include "ui/display/types/display_constants.h"
75 #include "ui/keyboard/keyboard_util.h"
76 #include "ui/resources/grit/ui_resources.h"
77
78 using extension_misc::kChromeAppId;
79 using extension_misc::kGmailAppId;
80
81 namespace {
82
83 int64_t GetDisplayIDForShelf(ash::WmShelf* shelf) {
84 display::Display display =
85 shelf->GetWindow()->GetRootWindow()->GetDisplayNearestWindow();
86 DCHECK(display.is_valid());
87 return display.id();
88 }
89
90 // A callback that does nothing after shelf item selection handling.
91 void NoopCallback(ash::ShelfAction, base::Optional<ash::MenuItemList>) {}
92
93 // Calls ItemSelected with |source|, default arguments, and no callback.
94 void SelectItemWithSource(ash::ShelfItemDelegate* delegate,
95 ash::ShelfLaunchSource source) {
96 delegate->ItemSelected(nullptr, display::kInvalidDisplayId, source,
97 base::Bind(&NoopCallback));
98 }
99
100 // Returns true if the given |item| has a pinned shelf item type.
101 bool ItemTypeIsPinned(const ash::ShelfItem& item) {
102 return item.type == ash::TYPE_PINNED_APP ||
103 item.type == ash::TYPE_BROWSER_SHORTCUT;
104 }
105
106 } // namespace
107
108 // A class to get events from ChromeOS when a user gets changed or added.
109 class ChromeLauncherControllerUserSwitchObserver
110 : public user_manager::UserManager::UserSessionStateObserver {
111 public:
112 ChromeLauncherControllerUserSwitchObserver(
113 ChromeLauncherController* controller)
114 : controller_(controller) {
115 DCHECK(user_manager::UserManager::IsInitialized());
116 user_manager::UserManager::Get()->AddSessionStateObserver(this);
117 }
118 ~ChromeLauncherControllerUserSwitchObserver() override {
119 user_manager::UserManager::Get()->RemoveSessionStateObserver(this);
120 }
121
122 // user_manager::UserManager::UserSessionStateObserver overrides:
123 void UserAddedToSession(const user_manager::User* added_user) override;
124
125 // ChromeLauncherControllerUserSwitchObserver:
126 void OnUserProfileReadyToSwitch(Profile* profile);
127
128 private:
129 // Add a user to the session.
130 void AddUser(Profile* profile);
131
132 // The owning ChromeLauncherController.
133 ChromeLauncherController* controller_;
134
135 // Users which were just added to the system, but which profiles were not yet
136 // (fully) loaded.
137 std::set<std::string> added_user_ids_waiting_for_profiles_;
138
139 DISALLOW_COPY_AND_ASSIGN(ChromeLauncherControllerUserSwitchObserver);
140 };
141
142 void ChromeLauncherControllerUserSwitchObserver::UserAddedToSession(
143 const user_manager::User* active_user) {
144 Profile* profile =
145 multi_user_util::GetProfileFromAccountId(active_user->GetAccountId());
146 // If we do not have a profile yet, we postpone forwarding the notification
147 // until it is loaded.
148 if (!profile) {
149 added_user_ids_waiting_for_profiles_.insert(
150 active_user->GetAccountId().GetUserEmail());
151 } else {
152 AddUser(profile);
153 }
154 }
155
156 void ChromeLauncherControllerUserSwitchObserver::OnUserProfileReadyToSwitch(
157 Profile* profile) {
158 if (!added_user_ids_waiting_for_profiles_.empty()) {
159 // Check if the profile is from a user which was on the waiting list.
160 // TODO(alemate): added_user_ids_waiting_for_profiles_ should be
161 // a set<AccountId>
162 std::string user_id =
163 multi_user_util::GetAccountIdFromProfile(profile).GetUserEmail();
164 std::set<std::string>::iterator it =
165 std::find(added_user_ids_waiting_for_profiles_.begin(),
166 added_user_ids_waiting_for_profiles_.end(), user_id);
167 if (it != added_user_ids_waiting_for_profiles_.end()) {
168 added_user_ids_waiting_for_profiles_.erase(it);
169 AddUser(profile->GetOriginalProfile());
170 }
171 }
172 }
173
174 void ChromeLauncherControllerUserSwitchObserver::AddUser(Profile* profile) {
175 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
176 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED)
177 chrome::MultiUserWindowManager::GetInstance()->AddUser(profile);
178 controller_->AdditionalUserAddedToSession(profile->GetOriginalProfile());
179 }
19 180
20 // static 181 // static
21 ChromeLauncherController* ChromeLauncherController::instance_ = nullptr; 182 ChromeLauncherController* ChromeLauncherController::instance_ = nullptr;
22 183
184 ChromeLauncherController::ChromeLauncherController(Profile* profile,
185 ash::ShelfModel* model)
186 : model_(model), observer_binding_(this), weak_ptr_factory_(this) {
187 DCHECK(!instance_);
188 instance_ = this;
189
190 DCHECK(model_);
191
192 if (!profile) {
193 // If no profile was passed, we take the currently active profile and use it
194 // as the owner of the current desktop.
195 // Use the original profile as on chromeos we may get a temporary off the
196 // record profile, unless in guest session (where off the record profile is
197 // the right one).
198 profile = ProfileManager::GetActiveUserProfile();
199 if (!profile->IsGuestSession() && !profile->IsSystemProfile())
200 profile = profile->GetOriginalProfile();
201
202 app_sync_ui_state_ = AppSyncUIState::Get(profile);
203 if (app_sync_ui_state_)
204 app_sync_ui_state_->AddObserver(this);
205 }
206
207 // All profile relevant settings get bound to the current profile.
208 AttachProfile(profile);
209 DCHECK_EQ(profile, profile_);
210 model_->AddObserver(this);
211
212 if (arc::IsArcAllowedForProfile(profile))
213 arc_deferred_launcher_.reset(new ArcAppDeferredLauncherController(this));
214
215 // In multi profile mode we might have a window manager. We try to create it
216 // here. If the instantiation fails, the manager is not needed.
217 chrome::MultiUserWindowManager::CreateInstance();
218
219 // On Chrome OS using multi profile we want to switch the content of the shelf
220 // with a user change. Note that for unit tests the instance can be NULL.
221 if (chrome::MultiUserWindowManager::GetMultiProfileMode() !=
222 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_OFF) {
223 user_switch_observer_.reset(
224 new ChromeLauncherControllerUserSwitchObserver(this));
225 }
226
227 std::unique_ptr<AppWindowLauncherController> extension_app_window_controller;
228 // Create our v1/v2 application / browser monitors which will inform the
229 // launcher of status changes.
230 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
231 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED) {
232 // If running in separated destkop mode, we create the multi profile version
233 // of status monitor.
234 browser_status_monitor_.reset(new MultiProfileBrowserStatusMonitor(this));
235 browser_status_monitor_->Initialize();
236 extension_app_window_controller.reset(
237 new MultiProfileAppWindowLauncherController(this));
238 } else {
239 // Create our v1/v2 application / browser monitors which will inform the
240 // launcher of status changes.
241 browser_status_monitor_.reset(new BrowserStatusMonitor(this));
242 browser_status_monitor_->Initialize();
243 extension_app_window_controller.reset(
244 new ExtensionAppWindowLauncherController(this));
245 }
246 app_window_controllers_.push_back(std::move(extension_app_window_controller));
247 app_window_controllers_.push_back(
248 base::MakeUnique<ArcAppWindowLauncherController>(this));
249
250 // Right now ash::Shell isn't created for tests.
251 // TODO(mukai): Allows it to observe display change and write tests.
252 if (ash::Shell::HasInstance())
253 ash::Shell::Get()->window_tree_host_manager()->AddObserver(this);
254 }
255
23 ChromeLauncherController::~ChromeLauncherController() { 256 ChromeLauncherController::~ChromeLauncherController() {
257 // Reset the BrowserStatusMonitor as it has a weak pointer to this.
258 browser_status_monitor_.reset();
259
260 // Reset the app window controllers here since it has a weak pointer to this.
261 app_window_controllers_.clear();
262
263 model_->RemoveObserver(this);
264 if (ash::Shell::HasInstance())
265 ash::Shell::Get()->window_tree_host_manager()->RemoveObserver(this);
266
267 // Release all profile dependent resources.
268 ReleaseProfile();
269
270 // Get rid of the multi user window manager instance.
271 chrome::MultiUserWindowManager::DeleteInstance();
272
24 if (instance_ == this) 273 if (instance_ == this)
25 instance_ = nullptr; 274 instance_ = nullptr;
26 } 275 }
27 276
28 void ChromeLauncherController::Init() { 277 void ChromeLauncherController::Init() {
29 // Start observing the shelf controller. 278 // Start observing the shelf controller.
30 if (ConnectToShelfController()) { 279 if (ConnectToShelfController()) {
31 ash::mojom::ShelfObserverAssociatedPtrInfo ptr_info; 280 ash::mojom::ShelfObserverAssociatedPtrInfo ptr_info;
32 observer_binding_.Bind(&ptr_info); 281 observer_binding_.Bind(&ptr_info);
33 shelf_controller_->AddObserver(std::move(ptr_info)); 282 shelf_controller_->AddObserver(std::move(ptr_info));
34 } 283 }
35 OnInit(); 284
285 CreateBrowserShortcutLauncherItem();
286 UpdateAppLaunchersFromPref();
287
288 // TODO(sky): update unit test so that this test isn't necessary.
289 if (ash::Shell::HasInstance())
290 SetVirtualKeyboardBehaviorFromPrefs();
291
292 prefs_observer_ =
293 ash::launcher::ChromeLauncherPrefsObserver::CreateIfNecessary(profile());
294 }
295
296 ash::ShelfID ChromeLauncherController::CreateAppLauncherItem(
297 std::unique_ptr<ash::ShelfItemDelegate> item_delegate,
298 ash::ShelfItemStatus status) {
299 return InsertAppLauncherItem(std::move(item_delegate), status,
300 model_->item_count(), ash::TYPE_APP);
301 }
302
303 const ash::ShelfItem* ChromeLauncherController::GetItem(ash::ShelfID id) const {
304 const int index = model_->ItemIndexByID(id);
305 if (index >= 0 && index < model_->item_count())
306 return &model_->items()[index];
307 return nullptr;
308 }
309
310 void ChromeLauncherController::SetItemType(ash::ShelfID id,
311 ash::ShelfItemType type) {
312 const ash::ShelfItem* item = GetItem(id);
313 if (item && item->type != type) {
314 ash::ShelfItem new_item = *item;
315 new_item.type = type;
316 model_->Set(model_->ItemIndexByID(id), new_item);
317 }
318 }
319
320 void ChromeLauncherController::SetItemStatus(ash::ShelfID id,
321 ash::ShelfItemStatus status) {
322 const ash::ShelfItem* item = GetItem(id);
323 if (item && item->status != status) {
324 ash::ShelfItem new_item = *item;
325 new_item.status = status;
326 model_->Set(model_->ItemIndexByID(id), new_item);
327 }
328 }
329
330 void ChromeLauncherController::CloseLauncherItem(ash::ShelfID id) {
331 CHECK(id);
332 if (IsPinned(id)) {
333 // Create a new shortcut delegate.
334 SetItemStatus(id, ash::STATUS_CLOSED);
335 model_->SetShelfItemDelegate(id, AppShortcutLauncherItemController::Create(
336 GetItem(id)->app_launch_id));
337 } else {
338 RemoveShelfItem(id);
339 }
340 }
341
342 void ChromeLauncherController::UnpinShelfItemInternal(ash::ShelfID id) {
343 const ash::ShelfItem* item = GetItem(id);
344 if (item && item->status != ash::STATUS_CLOSED)
345 UnpinRunningAppInternal(model_->ItemIndexByID(id));
346 else
347 RemoveShelfItem(id);
348 }
349
350 bool ChromeLauncherController::IsPinned(ash::ShelfID id) {
351 const ash::ShelfItem* item = GetItem(id);
352 return item && ItemTypeIsPinned(*item);
353 }
354
355 void ChromeLauncherController::SetV1AppStatus(const std::string& app_id,
356 ash::ShelfItemStatus status) {
357 ash::ShelfID id = GetShelfIDForAppID(app_id);
358 const ash::ShelfItem* item = GetItem(id);
359 if (item) {
360 if (!IsPinned(id) && status == ash::STATUS_CLOSED)
361 RemoveShelfItem(id);
362 else
363 SetItemStatus(id, status);
364 } else if (status != ash::STATUS_CLOSED && !app_id.empty()) {
365 InsertAppLauncherItem(
366 AppShortcutLauncherItemController::Create(ash::AppLaunchId(app_id)),
367 status, model_->item_count(), ash::TYPE_APP);
368 }
369 }
370
371 void ChromeLauncherController::Launch(ash::ShelfID id, int event_flags) {
372 ash::ShelfItemDelegate* delegate = model_->GetShelfItemDelegate(id);
373 if (!delegate)
374 return; // In case invoked from menu and item closed while menu up.
375
376 // Launching some items replaces the associated item delegate instance,
377 // which destroys the app and launch id strings; making copies avoid crashes.
378 LaunchApp(ash::AppLaunchId(delegate->app_id(), delegate->launch_id()),
379 ash::LAUNCH_FROM_UNKNOWN, event_flags);
380 }
381
382 void ChromeLauncherController::Close(ash::ShelfID id) {
383 ash::ShelfItemDelegate* delegate = model_->GetShelfItemDelegate(id);
384 if (!delegate)
385 return; // May happen if menu closed.
386 delegate->Close();
387 }
388
389 bool ChromeLauncherController::IsOpen(ash::ShelfID id) {
390 const ash::ShelfItem* item = GetItem(id);
391 return item && item->status != ash::STATUS_CLOSED;
392 }
393
394 bool ChromeLauncherController::IsPlatformApp(ash::ShelfID id) {
395 std::string app_id = GetAppIDForShelfID(id);
396 const extensions::Extension* extension =
397 GetExtensionForAppID(app_id, profile());
398 // An extension can be synced / updated at any time and therefore not be
399 // available.
400 return extension ? extension->is_platform_app() : false;
36 } 401 }
37 402
38 void ChromeLauncherController::LaunchApp(ash::AppLaunchId id, 403 void ChromeLauncherController::LaunchApp(ash::AppLaunchId id,
39 ash::ShelfLaunchSource source, 404 ash::ShelfLaunchSource source,
40 int event_flags) { 405 int event_flags) {
41 launcher_controller_helper_->LaunchApp(id, source, event_flags); 406 launcher_controller_helper_->LaunchApp(id, source, event_flags);
42 } 407 }
43 408
44 ChromeLauncherController::ChromeLauncherController() : observer_binding_(this) { 409 void ChromeLauncherController::ActivateApp(const std::string& app_id,
45 DCHECK(!instance_); 410 ash::ShelfLaunchSource source,
46 instance_ = this; 411 int event_flags) {
47 } 412 // If there is an existing non-shortcut delegate for this app, open it.
48 413 ash::ShelfID id = GetShelfIDForAppID(app_id);
49 bool ChromeLauncherController::ConnectToShelfController() { 414 if (id) {
50 if (shelf_controller_.is_bound()) 415 SelectItemWithSource(model_->GetShelfItemDelegate(id), source);
416 return;
417 }
418
419 // Create a temporary application launcher item and use it to see if there are
420 // running instances.
421 ash::AppLaunchId app_launch_id(app_id);
422 std::unique_ptr<AppShortcutLauncherItemController> item_delegate =
423 AppShortcutLauncherItemController::Create(app_launch_id);
424 if (!item_delegate->GetRunningApplications().empty())
425 SelectItemWithSource(item_delegate.get(), source);
426 else
427 LaunchApp(app_launch_id, source, event_flags);
428 }
429
430 void ChromeLauncherController::SetLauncherItemImage(
431 ash::ShelfID shelf_id,
432 const gfx::ImageSkia& image) {
433 const ash::ShelfItem* item = GetItem(shelf_id);
434 if (item) {
435 ash::ShelfItem new_item = *item;
436 new_item.image = image;
437 model_->Set(model_->ItemIndexByID(shelf_id), new_item);
438 }
439 }
440
441 void ChromeLauncherController::UpdateAppState(content::WebContents* contents,
442 AppState app_state) {
443 std::string app_id = launcher_controller_helper_->GetAppID(contents);
444
445 // Check if the gMail app is loaded and it matches the given content.
446 // This special treatment is needed to address crbug.com/234268.
447 if (app_id.empty() && ContentCanBeHandledByGmailApp(contents))
448 app_id = kGmailAppId;
449
450 // Check the old |app_id| for a tab. If the contents has changed we need to
451 // remove it from the previous app.
452 if (web_contents_to_app_id_.find(contents) != web_contents_to_app_id_.end()) {
453 std::string last_app_id = web_contents_to_app_id_[contents];
454 if (last_app_id != app_id) {
455 ash::ShelfID id = GetShelfIDForAppID(last_app_id);
456 if (id) {
457 // Since GetAppState() will use |web_contents_to_app_id_| we remove
458 // the connection before calling it.
459 web_contents_to_app_id_.erase(contents);
460 SetItemStatus(id, GetAppState(last_app_id));
461 }
462 }
463 }
464
465 if (app_state == APP_STATE_REMOVED)
466 web_contents_to_app_id_.erase(contents);
467 else
468 web_contents_to_app_id_[contents] = app_id;
469
470 ash::ShelfID id = GetShelfIDForAppID(app_id);
471 if (id) {
472 SetItemStatus(id, (app_state == APP_STATE_WINDOW_ACTIVE ||
473 app_state == APP_STATE_ACTIVE)
474 ? ash::STATUS_ACTIVE
475 : GetAppState(app_id));
476 }
477 }
478
479 ash::ShelfID ChromeLauncherController::GetShelfIDForWebContents(
480 content::WebContents* contents) {
481 std::string app_id = launcher_controller_helper_->GetAppID(contents);
482 if (app_id.empty() && ContentCanBeHandledByGmailApp(contents))
483 app_id = kGmailAppId;
484
485 ash::ShelfID id = GetShelfIDForAppID(app_id);
486 // If there is no dedicated app item, use the browser shortcut item.
487 return id == ash::kInvalidShelfID ? GetShelfIDForAppID(kChromeAppId) : id;
488 }
489
490 void ChromeLauncherController::SetRefocusURLPatternForTest(ash::ShelfID id,
491 const GURL& url) {
492 const ash::ShelfItem* item = GetItem(id);
493 if (item && !IsPlatformApp(id) &&
494 (item->type == ash::TYPE_PINNED_APP || item->type == ash::TYPE_APP)) {
495 ash::ShelfItemDelegate* delegate = model_->GetShelfItemDelegate(id);
496 AppShortcutLauncherItemController* item_controller =
497 static_cast<AppShortcutLauncherItemController*>(delegate);
498 item_controller->set_refocus_url(url);
499 } else {
500 NOTREACHED() << "Invalid launcher item or type";
501 }
502 }
503
504 ash::ShelfAction ChromeLauncherController::ActivateWindowOrMinimizeIfActive(
505 ui::BaseWindow* window,
506 bool allow_minimize) {
507 // In separated desktop mode we might have to teleport a window back to the
508 // current user.
509 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
510 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_SEPARATED) {
511 aura::Window* native_window = window->GetNativeWindow();
512 const AccountId& current_account_id =
513 multi_user_util::GetAccountIdFromProfile(profile());
514 chrome::MultiUserWindowManager* manager =
515 chrome::MultiUserWindowManager::GetInstance();
516 if (!manager->IsWindowOnDesktopOfUser(native_window, current_account_id)) {
517 ash::MultiProfileUMA::RecordTeleportAction(
518 ash::MultiProfileUMA::TELEPORT_WINDOW_RETURN_BY_LAUNCHER);
519 manager->ShowWindowForUser(native_window, current_account_id);
520 window->Activate();
521 return ash::SHELF_ACTION_WINDOW_ACTIVATED;
522 }
523 }
524
525 if (window->IsActive() && allow_minimize) {
526 window->Minimize();
527 return ash::SHELF_ACTION_WINDOW_MINIMIZED;
528 }
529
530 window->Show();
531 window->Activate();
532 return ash::SHELF_ACTION_WINDOW_ACTIVATED;
533 }
534
535 void ChromeLauncherController::ActiveUserChanged(
536 const std::string& user_email) {
537 // Store the order of running applications for the user which gets inactive.
538 RememberUnpinnedRunningApplicationOrder();
539 // Coming here the default profile is already switched. All profile specific
540 // resources get released and the new profile gets attached instead.
541 ReleaseProfile();
542 // When coming here, the active user has already be changed so that we can
543 // set it as active.
544 AttachProfile(ProfileManager::GetActiveUserProfile());
545 // Update the V1 applications.
546 browser_status_monitor_->ActiveUserChanged(user_email);
547 // Switch the running applications to the new user.
548 for (auto& controller : app_window_controllers_)
549 controller->ActiveUserChanged(user_email);
550 // Update the user specific shell properties from the new user profile.
551 // Shelf preferences are loaded in ChromeLauncherController::AttachProfile.
552 UpdateAppLaunchersFromPref();
553 SetVirtualKeyboardBehaviorFromPrefs();
554
555 // Restore the order of running, but unpinned applications for the activated
556 // user.
557 RestoreUnpinnedRunningApplicationOrder(user_email);
558 // TODO(crbug.com/557406): Fix this interaction pattern in Mash.
559 if (!ash_util::IsRunningInMash()) {
560 // Inform the system tray of the change.
561 ash::Shell::Get()->system_tray_delegate()->ActiveUserWasChanged();
562 // Force on-screen keyboard to reset.
563 if (keyboard::IsKeyboardEnabled())
564 ash::Shell::Get()->CreateKeyboard();
565 }
566 }
567
568 void ChromeLauncherController::AdditionalUserAddedToSession(Profile* profile) {
569 // Switch the running applications to the new user.
570 for (auto& controller : app_window_controllers_)
571 controller->AdditionalUserAddedToSession(profile);
572 }
573
574 ash::MenuItemList ChromeLauncherController::GetAppMenuItemsForTesting(
575 const ash::ShelfItem& item) {
576 ash::ShelfItemDelegate* delegate = model_->GetShelfItemDelegate(item.id);
577 return delegate ? delegate->GetAppMenuItems(ui::EF_NONE)
578 : ash::MenuItemList();
579 }
580
581 std::vector<content::WebContents*>
582 ChromeLauncherController::GetV1ApplicationsFromAppId(
583 const std::string& app_id) {
584 const ash::ShelfItem* item = GetItem(GetShelfIDForAppID(app_id));
585 // If there is no such item pinned to the launcher, no menu gets created.
586 if (!item || item->type != ash::TYPE_PINNED_APP)
587 return std::vector<content::WebContents*>();
588 ash::ShelfItemDelegate* delegate = model_->GetShelfItemDelegate(item->id);
589 AppShortcutLauncherItemController* item_controller =
590 static_cast<AppShortcutLauncherItemController*>(delegate);
591 return item_controller->GetRunningApplications();
592 }
593
594 void ChromeLauncherController::ActivateShellApp(const std::string& app_id,
595 int window_index) {
596 const ash::ShelfItem* item = GetItem(GetShelfIDForAppID(app_id));
597 if (item &&
598 (item->type == ash::TYPE_APP || item->type == ash::TYPE_PINNED_APP)) {
599 ash::ShelfItemDelegate* delegate = model_->GetShelfItemDelegate(item->id);
600 AppWindowLauncherItemController* item_controller =
601 delegate->AsAppWindowLauncherItemController();
602 item_controller->ActivateIndexedApp(window_index);
603 }
604 }
605
606 bool ChromeLauncherController::IsWebContentHandledByApplication(
607 content::WebContents* web_contents,
608 const std::string& app_id) {
609 if ((web_contents_to_app_id_.find(web_contents) !=
610 web_contents_to_app_id_.end()) &&
611 (web_contents_to_app_id_[web_contents] == app_id))
51 return true; 612 return true;
52 613 return (app_id == kGmailAppId && ContentCanBeHandledByGmailApp(web_contents));
53 auto* connection = content::ServiceManagerConnection::GetForProcess(); 614 }
54 auto* connector = connection ? connection->GetConnector() : nullptr; 615
55 // Unit tests may not have a connector. 616 bool ChromeLauncherController::ContentCanBeHandledByGmailApp(
56 if (!connector) 617 content::WebContents* web_contents) {
618 ash::ShelfID id = GetShelfIDForAppID(kGmailAppId);
619 if (id) {
620 const GURL url = web_contents->GetURL();
621 // We need to extend the application matching for the gMail app beyond the
622 // manifest file's specification. This is required because of the namespace
623 // overlap with the offline app ("/mail/mu/").
624 if (!base::MatchPattern(url.path(), "/mail/mu/*") &&
625 base::MatchPattern(url.path(), "/mail/*") &&
626 GetExtensionForAppID(kGmailAppId, profile()) &&
627 GetExtensionForAppID(kGmailAppId, profile())->OverlapsWithOrigin(url))
628 return true;
629 }
630 return false;
631 }
632
633 gfx::Image ChromeLauncherController::GetAppListIcon(
634 content::WebContents* web_contents) const {
635 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
636 if (IsIncognito(web_contents))
637 return rb.GetImageNamed(IDR_ASH_SHELF_LIST_INCOGNITO_BROWSER);
638 favicon::FaviconDriver* favicon_driver =
639 favicon::ContentFaviconDriver::FromWebContents(web_contents);
640 gfx::Image result = favicon_driver->GetFavicon();
641 if (result.IsEmpty())
642 return rb.GetImageNamed(IDR_DEFAULT_FAVICON);
643 return result;
644 }
645
646 base::string16 ChromeLauncherController::GetAppListTitle(
647 content::WebContents* web_contents) const {
648 base::string16 title = web_contents->GetTitle();
649 if (!title.empty())
650 return title;
651 WebContentsToAppIDMap::const_iterator iter =
652 web_contents_to_app_id_.find(web_contents);
653 if (iter != web_contents_to_app_id_.end()) {
654 std::string app_id = iter->second;
655 const extensions::Extension* extension =
656 GetExtensionForAppID(app_id, profile());
657 if (extension)
658 return base::UTF8ToUTF16(extension->name());
659 }
660 return l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE);
661 }
662
663 BrowserShortcutLauncherItemController*
664 ChromeLauncherController::GetBrowserShortcutLauncherItemController() {
665 ash::ShelfID id = GetShelfIDForAppID(kChromeAppId);
666 ash::mojom::ShelfItemDelegate* delegate = model_->GetShelfItemDelegate(id);
667 DCHECK(delegate) << "There should be always be a browser shortcut item.";
668 return static_cast<BrowserShortcutLauncherItemController*>(delegate);
669 }
670
671 bool ChromeLauncherController::ShelfBoundsChangesProbablyWithUser(
672 ash::WmShelf* shelf,
673 const AccountId& account_id) const {
674 Profile* other_profile = multi_user_util::GetProfileFromAccountId(account_id);
675 if (!other_profile || other_profile == profile())
57 return false; 676 return false;
58 677
59 connector->BindInterface(ash::mojom::kServiceName, &shelf_controller_); 678 // Note: The Auto hide state from preferences is not the same as the actual
60 return true; 679 // visibility of the shelf. Depending on all the various states (full screen,
680 // no window on desktop, multi user, ..) the shelf could be shown - or not.
681 PrefService* prefs = profile()->GetPrefs();
682 PrefService* other_prefs = other_profile->GetPrefs();
683 const int64_t display = GetDisplayIDForShelf(shelf);
684 const bool currently_shown =
685 ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER ==
686 ash::launcher::GetShelfAutoHideBehaviorPref(prefs, display);
687 const bool other_shown =
688 ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER ==
689 ash::launcher::GetShelfAutoHideBehaviorPref(other_prefs, display);
690
691 return currently_shown != other_shown ||
692 ash::launcher::GetShelfAlignmentPref(prefs, display) !=
693 ash::launcher::GetShelfAlignmentPref(other_prefs, display);
694 }
695
696 void ChromeLauncherController::OnUserProfileReadyToSwitch(Profile* profile) {
697 if (user_switch_observer_.get())
698 user_switch_observer_->OnUserProfileReadyToSwitch(profile);
699 }
700
701 ArcAppDeferredLauncherController*
702 ChromeLauncherController::GetArcDeferredLauncher() {
703 return arc_deferred_launcher_.get();
704 }
705
706 const std::string& ChromeLauncherController::GetLaunchIDForShelfID(
707 ash::ShelfID id) {
708 ash::ShelfItemDelegate* delegate = model_->GetShelfItemDelegate(id);
709 return delegate ? delegate->launch_id() : base::EmptyString();
61 } 710 }
62 711
63 AppIconLoader* ChromeLauncherController::GetAppIconLoaderForApp( 712 AppIconLoader* ChromeLauncherController::GetAppIconLoaderForApp(
64 const std::string& app_id) { 713 const std::string& app_id) {
65 for (const auto& app_icon_loader : app_icon_loaders_) { 714 for (const auto& app_icon_loader : app_icon_loaders_) {
66 if (app_icon_loader->CanLoadImageForApp(app_id)) 715 if (app_icon_loader->CanLoadImageForApp(app_id))
67 return app_icon_loader.get(); 716 return app_icon_loader.get();
68 } 717 }
69 718
70 return nullptr; 719 return nullptr;
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
118 std::vector<std::unique_ptr<AppIconLoader>>& loaders) { 767 std::vector<std::unique_ptr<AppIconLoader>>& loaders) {
119 app_icon_loaders_.clear(); 768 app_icon_loaders_.clear();
120 for (auto& loader : loaders) 769 for (auto& loader : loaders)
121 app_icon_loaders_.push_back(std::move(loader)); 770 app_icon_loaders_.push_back(std::move(loader));
122 } 771 }
123 772
124 void ChromeLauncherController::SetProfileForTest(Profile* profile) { 773 void ChromeLauncherController::SetProfileForTest(Profile* profile) {
125 profile_ = profile; 774 profile_ = profile;
126 } 775 }
127 776
777 ash::ShelfID ChromeLauncherController::GetShelfIDForAppID(
778 const std::string& app_id) {
779 return model_->GetShelfIDForAppID(app_id);
780 }
781
782 ash::ShelfID ChromeLauncherController::GetShelfIDForAppIDAndLaunchID(
783 const std::string& app_id,
784 const std::string& launch_id) {
785 return model_->GetShelfIDForAppIDAndLaunchID(app_id, launch_id);
786 }
787
788 const std::string& ChromeLauncherController::GetAppIDForShelfID(
789 ash::ShelfID id) {
790 return model_->GetAppIDForShelfID(id);
791 }
792
793 void ChromeLauncherController::PinAppWithID(const std::string& app_id) {
794 model_->PinAppWithID(app_id);
795 }
796
797 bool ChromeLauncherController::IsAppPinned(const std::string& app_id) {
798 return model_->IsAppPinned(app_id);
799 }
800
801 void ChromeLauncherController::UnpinAppWithID(const std::string& app_id) {
802 model_->UnpinAppWithID(app_id);
803 }
804
805 ///////////////////////////////////////////////////////////////////////////////
806 // LauncherAppUpdater::Delegate:
807
808 void ChromeLauncherController::OnAppInstalled(
809 content::BrowserContext* browser_context,
810 const std::string& app_id) {
811 if (IsAppPinned(app_id)) {
812 // Clear and re-fetch to ensure icon is up-to-date.
813 AppIconLoader* app_icon_loader = GetAppIconLoaderForApp(app_id);
814 if (app_icon_loader) {
815 app_icon_loader->ClearImage(app_id);
816 app_icon_loader->FetchImage(app_id);
817 }
818 }
819
820 UpdateAppLaunchersFromPref();
821 }
822
823 void ChromeLauncherController::OnAppUpdated(
824 content::BrowserContext* browser_context,
825 const std::string& app_id) {
826 AppIconLoader* app_icon_loader = GetAppIconLoaderForApp(app_id);
827 if (app_icon_loader)
828 app_icon_loader->UpdateImage(app_id);
829 }
830
831 void ChromeLauncherController::OnAppUninstalledPrepared(
832 content::BrowserContext* browser_context,
833 const std::string& app_id) {
834 // Since we might have windowed apps of this type which might have
835 // outstanding locks which needs to be removed.
836 const Profile* profile = Profile::FromBrowserContext(browser_context);
837 ash::ShelfID shelf_id = GetShelfIDForAppID(app_id);
838 if (shelf_id != ash::kInvalidShelfID)
839 CloseWindowedAppsFromRemovedExtension(app_id, profile);
840
841 if (IsAppPinned(app_id)) {
842 if (profile == this->profile() && shelf_id != ash::kInvalidShelfID) {
843 // Some apps may be removed locally. Unpin the item without removing the
844 // pin position from profile preferences. When needed, it is automatically
845 // deleted on app list model update.
846 UnpinShelfItemInternal(shelf_id);
847 }
848 AppIconLoader* app_icon_loader = GetAppIconLoaderForApp(app_id);
849 if (app_icon_loader)
850 app_icon_loader->ClearImage(app_id);
851 }
852 }
853
854 ///////////////////////////////////////////////////////////////////////////////
855 // AppIconLoaderDelegate:
856
857 void ChromeLauncherController::OnAppImageUpdated(const std::string& app_id,
858 const gfx::ImageSkia& image) {
859 // TODO: need to get this working for shortcuts.
860 for (int index = 0; index < model_->item_count(); ++index) {
861 ash::ShelfItem item = model_->items()[index];
862 ash::ShelfItemDelegate* delegate = model_->GetShelfItemDelegate(item.id);
863 if (item.type == ash::TYPE_APP_PANEL || !delegate ||
864 delegate->image_set_by_controller() ||
865 item.app_launch_id.app_id() != app_id) {
866 continue;
867 }
868 item.image = image;
869 if (arc_deferred_launcher_)
870 arc_deferred_launcher_->MaybeApplySpinningEffect(app_id, &item.image);
871 model_->Set(index, item);
872 // It's possible we're waiting on more than one item, so don't break.
873 }
874 }
875
876 ///////////////////////////////////////////////////////////////////////////////
877 // ChromeLauncherController protected:
878
879 bool ChromeLauncherController::ConnectToShelfController() {
880 if (shelf_controller_.is_bound())
881 return true;
882
883 auto* connection = content::ServiceManagerConnection::GetForProcess();
884 auto* connector = connection ? connection->GetConnector() : nullptr;
885 // Unit tests may not have a connector.
886 if (!connector)
887 return false;
888
889 connector->BindInterface(ash::mojom::kServiceName, &shelf_controller_);
890 return true;
891 }
892
893 ///////////////////////////////////////////////////////////////////////////////
894 // ChromeLauncherController private:
895
896 ash::ShelfID ChromeLauncherController::CreateAppShortcutLauncherItem(
897 const ash::AppLaunchId& app_launch_id,
898 int index) {
899 return InsertAppLauncherItem(
900 AppShortcutLauncherItemController::Create(app_launch_id),
901 ash::STATUS_CLOSED, index, ash::TYPE_PINNED_APP);
902 }
903
904 void ChromeLauncherController::RememberUnpinnedRunningApplicationOrder() {
905 RunningAppListIds list;
906 for (int i = 0; i < model_->item_count(); i++) {
907 if (model_->items()[i].type == ash::TYPE_APP)
908 list.push_back(GetAppIDForShelfID(model_->items()[i].id));
909 }
910 const std::string user_email =
911 multi_user_util::GetAccountIdFromProfile(profile()).GetUserEmail();
912 last_used_running_application_order_[user_email] = list;
913 }
914
915 void ChromeLauncherController::RestoreUnpinnedRunningApplicationOrder(
916 const std::string& user_id) {
917 const RunningAppListIdMap::iterator app_id_list =
918 last_used_running_application_order_.find(user_id);
919 if (app_id_list == last_used_running_application_order_.end())
920 return;
921
922 // Find the first insertion point for running applications.
923 int running_index = model_->FirstRunningAppIndex();
924 for (const std::string& app_id : app_id_list->second) {
925 const ash::ShelfItem* item = GetItem(GetShelfIDForAppID(app_id));
926 if (item && item->type == ash::TYPE_APP) {
927 int app_index = model_->ItemIndexByID(item->id);
928 DCHECK_GE(app_index, 0);
929 if (running_index != app_index)
930 model_->Move(running_index, app_index);
931 running_index++;
932 }
933 }
934 }
935
936 void ChromeLauncherController::RemoveShelfItem(ash::ShelfID id) {
937 const int index = model_->ItemIndexByID(id);
938 if (index >= 0 && index < model_->item_count())
939 model_->RemoveItemAt(index);
940 }
941
942 void ChromeLauncherController::PinRunningAppInternal(int index,
943 ash::ShelfID shelf_id) {
944 DCHECK_EQ(GetItem(shelf_id)->type, ash::TYPE_APP);
945 SetItemType(shelf_id, ash::TYPE_PINNED_APP);
946 int running_index = model_->ItemIndexByID(shelf_id);
947 if (running_index < index)
948 --index;
949 if (running_index != index)
950 model_->Move(running_index, index);
951 }
952
953 void ChromeLauncherController::UnpinRunningAppInternal(int index) {
954 DCHECK(index >= 0 && index < model_->item_count());
955 ash::ShelfItem item = model_->items()[index];
956 DCHECK_EQ(item.type, ash::TYPE_PINNED_APP);
957 SetItemType(item.id, ash::TYPE_APP);
958 }
959
960 void ChromeLauncherController::SyncPinPosition(ash::ShelfID shelf_id) {
961 DCHECK(should_sync_pin_changes_);
962 DCHECK(shelf_id);
963
964 const int max_index = model_->item_count();
965 const int index = model_->ItemIndexByID(shelf_id);
966 DCHECK_GT(index, 0);
967
968 const std::string& app_id = GetAppIDForShelfID(shelf_id);
969 DCHECK(!app_id.empty());
970 const std::string& launch_id = GetLaunchIDForShelfID(shelf_id);
971
972 std::string app_id_before;
973 std::string launch_id_before;
974 std::vector<ash::AppLaunchId> app_launch_ids_after;
975
976 for (int i = index - 1; i > 0; --i) {
977 const ash::ShelfID shelf_id_before = model_->items()[i].id;
978 if (IsPinned(shelf_id_before)) {
979 app_id_before = GetAppIDForShelfID(shelf_id_before);
980 DCHECK(!app_id_before.empty());
981 launch_id_before = GetLaunchIDForShelfID(shelf_id_before);
982 break;
983 }
984 }
985
986 for (int i = index + 1; i < max_index; ++i) {
987 const ash::ShelfID shelf_id_after = model_->items()[i].id;
988 if (IsPinned(shelf_id_after)) {
989 const std::string app_id_after = GetAppIDForShelfID(shelf_id_after);
990 DCHECK(!app_id_after.empty());
991 const std::string launch_id_after = GetLaunchIDForShelfID(shelf_id_after);
992 app_launch_ids_after.push_back(
993 ash::AppLaunchId(app_id_after, launch_id_after));
994 }
995 }
996
997 ash::AppLaunchId app_launch_id_before =
998 app_id_before.empty() ? ash::AppLaunchId()
999 : ash::AppLaunchId(app_id_before, launch_id_before);
1000
1001 ash::launcher::SetPinPosition(profile(), ash::AppLaunchId(app_id, launch_id),
1002 app_launch_id_before, app_launch_ids_after);
1003 }
1004
1005 void ChromeLauncherController::OnSyncModelUpdated() {
1006 UpdateAppLaunchersFromPref();
1007 }
1008
1009 void ChromeLauncherController::OnIsSyncingChanged() {
1010 UpdateAppLaunchersFromPref();
1011 }
1012
1013 void ChromeLauncherController::ScheduleUpdateAppLaunchersFromPref() {
1014 base::ThreadTaskRunnerHandle::Get()->PostTask(
1015 FROM_HERE,
1016 base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref,
1017 weak_ptr_factory_.GetWeakPtr()));
1018 }
1019
1020 void ChromeLauncherController::UpdateAppLaunchersFromPref() {
1021 // Do not sync pin changes during this function to avoid cyclical updates.
1022 // This function makes the shelf model reflect synced prefs, and should not
1023 // cyclically trigger sync changes (eg. ShelfItemAdded calls SyncPinPosition).
1024 ScopedPinSyncDisabler scoped_pin_sync_disabler = GetScopedPinSyncDisabler();
1025
1026 const std::vector<ash::AppLaunchId> pinned_apps =
1027 ash::launcher::GetPinnedAppsFromPrefs(profile()->GetPrefs(),
1028 launcher_controller_helper_.get());
1029
1030 int index = 0;
1031 // Skip app list items if it exists.
1032 if (model_->items()[0].type == ash::TYPE_APP_LIST)
1033 ++index;
1034
1035 // Apply pins in two steps. At the first step, go through the list of apps to
1036 // pin, move existing pin to current position specified by |index| or create
1037 // the new pin at that position.
1038 for (const auto& pref_app_launch_id : pinned_apps) {
1039 // Filter out apps that may be mapped wrongly.
1040 // TODO(khmel): b/31703859 is to refactore shelf mapping.
1041 const std::string app_id = pref_app_launch_id.app_id();
1042 const std::string shelf_app_id =
1043 ArcAppWindowLauncherController::GetShelfAppIdFromArcAppId(app_id);
1044 if (shelf_app_id != app_id)
1045 continue;
1046
1047 // Update apps icon if applicable.
1048 OnAppUpdated(profile(), app_id);
1049
1050 // Find existing pin or app from the right of current |index|.
1051 int app_index = index;
1052 for (; app_index < model_->item_count(); ++app_index) {
1053 const ash::ShelfItem& item = model_->items()[app_index];
1054 if (item.app_launch_id.app_id() == app_id &&
1055 item.app_launch_id.launch_id() == pref_app_launch_id.launch_id()) {
1056 break;
1057 }
1058 }
1059 if (app_index < model_->item_count()) {
1060 // Found existing pin or running app.
1061 const ash::ShelfItem item = model_->items()[app_index];
1062 if (ItemTypeIsPinned(item)) {
1063 // Just move to required position or keep it inplace.
1064 model_->Move(app_index, index);
1065 } else {
1066 PinRunningAppInternal(index, item.id);
1067 }
1068 DCHECK_EQ(model_->ItemIndexByID(item.id), index);
1069 } else {
1070 // This is fresh pin. Create new one.
1071 DCHECK_NE(app_id, kChromeAppId);
1072 CreateAppShortcutLauncherItem(pref_app_launch_id, index);
1073 }
1074 ++index;
1075 }
1076
1077 // At second step remove any pin to the right from the current index.
1078 while (index < model_->item_count()) {
1079 const ash::ShelfItem item = model_->items()[index];
1080 if (item.type == ash::TYPE_PINNED_APP)
1081 UnpinShelfItemInternal(item.id);
1082 else
1083 ++index;
1084 }
1085
1086 UpdatePolicyPinnedAppsFromPrefs();
1087 }
1088
1089 void ChromeLauncherController::UpdatePolicyPinnedAppsFromPrefs() {
1090 for (int index = 0; index < model_->item_count(); index++) {
1091 ash::ShelfItem item = model_->items()[index];
1092 const bool pinned_by_policy =
1093 GetPinnableForAppID(item.app_launch_id.app_id(), profile()) ==
1094 AppListControllerDelegate::PIN_FIXED;
1095 if (item.pinned_by_policy != pinned_by_policy) {
1096 item.pinned_by_policy = pinned_by_policy;
1097 model_->Set(index, item);
1098 }
1099 }
1100 }
1101
1102 void ChromeLauncherController::SetVirtualKeyboardBehaviorFromPrefs() {
1103 const PrefService* service = profile()->GetPrefs();
1104 const bool was_enabled = keyboard::IsKeyboardEnabled();
1105 if (!service->HasPrefPath(prefs::kTouchVirtualKeyboardEnabled)) {
1106 keyboard::SetKeyboardShowOverride(keyboard::KEYBOARD_SHOW_OVERRIDE_NONE);
1107 } else {
1108 const bool enable =
1109 service->GetBoolean(prefs::kTouchVirtualKeyboardEnabled);
1110 keyboard::SetKeyboardShowOverride(
1111 enable ? keyboard::KEYBOARD_SHOW_OVERRIDE_ENABLED
1112 : keyboard::KEYBOARD_SHOW_OVERRIDE_DISABLED);
1113 }
1114 // TODO(crbug.com/557406): Fix this interaction pattern in Mash.
1115 if (!ash_util::IsRunningInMash()) {
1116 const bool is_enabled = keyboard::IsKeyboardEnabled();
1117 if (was_enabled && !is_enabled)
1118 ash::Shell::Get()->DeactivateKeyboard();
1119 else if (is_enabled && !was_enabled)
1120 ash::Shell::Get()->CreateKeyboard();
1121 }
1122 }
1123
1124 ash::ShelfItemStatus ChromeLauncherController::GetAppState(
1125 const std::string& app_id) {
1126 ash::ShelfItemStatus status = ash::STATUS_CLOSED;
1127 for (WebContentsToAppIDMap::iterator it = web_contents_to_app_id_.begin();
1128 it != web_contents_to_app_id_.end(); ++it) {
1129 if (it->second == app_id) {
1130 Browser* browser = chrome::FindBrowserWithWebContents(it->first);
1131 // Usually there should never be an item in our |web_contents_to_app_id_|
1132 // list which got deleted already. However - in some situations e.g.
1133 // Browser::SwapTabContent there is temporarily no associated browser.
1134 if (!browser)
1135 continue;
1136 if (browser->window()->IsActive()) {
1137 return browser->tab_strip_model()->GetActiveWebContents() == it->first
1138 ? ash::STATUS_ACTIVE
1139 : ash::STATUS_RUNNING;
1140 } else {
1141 status = ash::STATUS_RUNNING;
1142 }
1143 }
1144 }
1145 return status;
1146 }
1147
1148 ash::ShelfID ChromeLauncherController::InsertAppLauncherItem(
1149 std::unique_ptr<ash::ShelfItemDelegate> item_delegate,
1150 ash::ShelfItemStatus status,
1151 int index,
1152 ash::ShelfItemType shelf_item_type) {
1153 ash::ShelfID id = model_->next_id();
1154 CHECK(!GetItem(id));
1155 CHECK(item_delegate);
1156 // Ash's ShelfWindowWatcher handles app panel windows separately.
1157 DCHECK_NE(ash::TYPE_APP_PANEL, shelf_item_type);
1158 ash::ShelfItem item;
1159 item.status = status;
1160 item.type = shelf_item_type;
1161 item.app_launch_id = item_delegate->app_launch_id();
1162 // Set the delegate first to avoid constructing one in ShelfItemAdded.
1163 model_->SetShelfItemDelegate(id, std::move(item_delegate));
1164 model_->AddAt(index, item);
1165 return id;
1166 }
1167
1168 void ChromeLauncherController::CreateBrowserShortcutLauncherItem() {
1169 // Do not sync the pin position of the browser shortcut item when it is added;
1170 // its initial position before prefs have loaded is unimportant and the sync
1171 // service may not yet be initialized.
1172 ScopedPinSyncDisabler scoped_pin_sync_disabler = GetScopedPinSyncDisabler();
1173
1174 ash::ShelfItem browser_shortcut;
1175 browser_shortcut.type = ash::TYPE_BROWSER_SHORTCUT;
1176 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
1177 browser_shortcut.image = *rb.GetImageSkiaNamed(IDR_PRODUCT_LOGO_32);
1178 browser_shortcut.title = l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
1179 browser_shortcut.app_launch_id = ash::AppLaunchId(kChromeAppId);
1180 ash::ShelfID id = model_->next_id();
1181 std::unique_ptr<BrowserShortcutLauncherItemController> item_delegate =
1182 base::MakeUnique<BrowserShortcutLauncherItemController>(model_);
1183 BrowserShortcutLauncherItemController* item_controller = item_delegate.get();
1184 // Set the delegate first to avoid constructing another one in ShelfItemAdded.
1185 model_->SetShelfItemDelegate(id, std::move(item_delegate));
1186 model_->AddAt(0, browser_shortcut);
1187 item_controller->UpdateBrowserItemState();
1188 }
1189
1190 bool ChromeLauncherController::IsIncognito(
1191 const content::WebContents* web_contents) const {
1192 const Profile* profile =
1193 Profile::FromBrowserContext(web_contents->GetBrowserContext());
1194 return profile->IsOffTheRecord() && !profile->IsGuestSession() &&
1195 !profile->IsSystemProfile();
1196 }
1197
1198 int ChromeLauncherController::FindInsertionPoint() {
1199 for (int i = model_->item_count() - 1; i > 0; --i) {
1200 if (ItemTypeIsPinned(model_->items()[i]))
1201 return i;
1202 }
1203 return 0;
1204 }
1205
1206 void ChromeLauncherController::CloseWindowedAppsFromRemovedExtension(
1207 const std::string& app_id,
1208 const Profile* profile) {
1209 // This function cannot rely on the controller's enumeration functionality
1210 // since the extension has already been unloaded.
1211 const BrowserList* browser_list = BrowserList::GetInstance();
1212 std::vector<Browser*> browser_to_close;
1213 for (BrowserList::const_reverse_iterator it =
1214 browser_list->begin_last_active();
1215 it != browser_list->end_last_active(); ++it) {
1216 Browser* browser = *it;
1217 if (!browser->is_type_tabbed() && browser->is_type_popup() &&
1218 browser->is_app() &&
1219 app_id ==
1220 web_app::GetExtensionIdFromApplicationName(browser->app_name()) &&
1221 profile == browser->profile()) {
1222 browser_to_close.push_back(browser);
1223 }
1224 }
1225 while (!browser_to_close.empty()) {
1226 TabStripModel* tab_strip = browser_to_close.back()->tab_strip_model();
1227 if (!tab_strip->empty())
1228 tab_strip->CloseWebContentsAt(0, TabStripModel::CLOSE_NONE);
1229 browser_to_close.pop_back();
1230 }
1231 }
1232
128 void ChromeLauncherController::AttachProfile(Profile* profile_to_attach) { 1233 void ChromeLauncherController::AttachProfile(Profile* profile_to_attach) {
129 profile_ = profile_to_attach; 1234 profile_ = profile_to_attach;
130 // Either add the profile to the list of known profiles and make it the active 1235 // Either add the profile to the list of known profiles and make it the active
131 // one for some functions of LauncherControllerHelper or create a new one. 1236 // one for some functions of LauncherControllerHelper or create a new one.
132 if (!launcher_controller_helper_.get()) { 1237 if (!launcher_controller_helper_.get()) {
133 launcher_controller_helper_ = 1238 launcher_controller_helper_ =
134 base::MakeUnique<LauncherControllerHelper>(profile_); 1239 base::MakeUnique<LauncherControllerHelper>(profile_);
135 } else { 1240 } else {
136 launcher_controller_helper_->set_profile(profile_); 1241 launcher_controller_helper_->set_profile(profile_);
137 } 1242 }
138 1243
139 // TODO(skuhne): The AppIconLoaderImpl has the same problem. Each loaded 1244 // TODO(skuhne): The AppIconLoaderImpl has the same problem. Each loaded
140 // image is associated with a profile (its loader requires the profile). 1245 // image is associated with a profile (its loader requires the profile).
141 // Since icon size changes are possible, the icon could be requested to be 1246 // Since icon size changes are possible, the icon could be requested to be
142 // reloaded. However - having it not multi profile aware would cause problems 1247 // reloaded. However - having it not multi profile aware would cause problems
143 // if the icon cache gets deleted upon user switch. 1248 // if the icon cache gets deleted upon user switch.
144 std::unique_ptr<AppIconLoader> extension_app_icon_loader = 1249 std::unique_ptr<AppIconLoader> extension_app_icon_loader =
145 base::MakeUnique<extensions::ExtensionAppIconLoader>( 1250 base::MakeUnique<extensions::ExtensionAppIconLoader>(
146 profile_, extension_misc::EXTENSION_ICON_SMALL, this); 1251 profile_, extension_misc::EXTENSION_ICON_SMALL, this);
147 app_icon_loaders_.push_back(std::move(extension_app_icon_loader)); 1252 app_icon_loaders_.push_back(std::move(extension_app_icon_loader));
148 1253
149 if (arc::IsArcAllowedForProfile(profile_)) { 1254 if (arc::IsArcAllowedForProfile(profile_)) {
150 std::unique_ptr<AppIconLoader> arc_app_icon_loader = 1255 std::unique_ptr<AppIconLoader> arc_app_icon_loader =
151 base::MakeUnique<ArcAppIconLoader>( 1256 base::MakeUnique<ArcAppIconLoader>(
152 profile_, extension_misc::EXTENSION_ICON_SMALL, this); 1257 profile_, extension_misc::EXTENSION_ICON_SMALL, this);
153 app_icon_loaders_.push_back(std::move(arc_app_icon_loader)); 1258 app_icon_loaders_.push_back(std::move(arc_app_icon_loader));
154 } 1259 }
155 1260
156 SetShelfBehaviorsFromPrefs(); 1261 SetShelfBehaviorsFromPrefs();
1262
1263 pref_change_registrar_.Init(profile()->GetPrefs());
1264 pref_change_registrar_.Add(
1265 prefs::kPolicyPinnedLauncherApps,
1266 base::Bind(&ChromeLauncherController::UpdateAppLaunchersFromPref,
1267 base::Unretained(this)));
1268 // Handling of prefs::kArcEnabled change should be called deferred to avoid
1269 // race condition when OnAppUninstalledPrepared for ARC apps is called after
1270 // UpdateAppLaunchersFromPref.
1271 pref_change_registrar_.Add(
1272 prefs::kArcEnabled,
1273 base::Bind(&ChromeLauncherController::ScheduleUpdateAppLaunchersFromPref,
1274 base::Unretained(this)));
1275 pref_change_registrar_.Add(
1276 prefs::kShelfAlignmentLocal,
1277 base::Bind(&ChromeLauncherController::SetShelfAlignmentFromPrefs,
1278 base::Unretained(this)));
1279 pref_change_registrar_.Add(
1280 prefs::kShelfAutoHideBehaviorLocal,
1281 base::Bind(&ChromeLauncherController::SetShelfAutoHideBehaviorFromPrefs,
1282 base::Unretained(this)));
1283 pref_change_registrar_.Add(
1284 prefs::kShelfPreferences,
1285 base::Bind(&ChromeLauncherController::SetShelfBehaviorsFromPrefs,
1286 base::Unretained(this)));
1287 pref_change_registrar_.Add(
1288 prefs::kTouchVirtualKeyboardEnabled,
1289 base::Bind(&ChromeLauncherController::SetVirtualKeyboardBehaviorFromPrefs,
1290 base::Unretained(this)));
1291
1292 std::unique_ptr<LauncherAppUpdater> extension_app_updater(
1293 new LauncherExtensionAppUpdater(this, profile()));
1294 app_updaters_.push_back(std::move(extension_app_updater));
1295
1296 if (arc::IsArcAllowedForProfile(profile())) {
1297 std::unique_ptr<LauncherAppUpdater> arc_app_updater(
1298 new LauncherArcAppUpdater(this, profile()));
1299 app_updaters_.push_back(std::move(arc_app_updater));
1300 }
1301
1302 app_list::AppListSyncableService* app_service =
1303 app_list::AppListSyncableServiceFactory::GetForProfile(profile());
1304 if (app_service)
1305 app_service->AddObserverAndStart(this);
1306
1307 PrefServiceSyncableFromProfile(profile())->AddObserver(this);
157 } 1308 }
158 1309
1310 void ChromeLauncherController::ReleaseProfile() {
1311 if (app_sync_ui_state_)
1312 app_sync_ui_state_->RemoveObserver(this);
1313
1314 app_updaters_.clear();
1315
1316 prefs_observer_.reset();
1317
1318 pref_change_registrar_.RemoveAll();
1319
1320 app_list::AppListSyncableService* app_service =
1321 app_list::AppListSyncableServiceFactory::GetForProfile(profile());
1322 if (app_service)
1323 app_service->RemoveObserver(this);
1324
1325 PrefServiceSyncableFromProfile(profile())->RemoveObserver(this);
1326 }
1327
1328 ///////////////////////////////////////////////////////////////////////////////
1329 // ash::mojom::ShelfObserver:
1330
159 void ChromeLauncherController::OnShelfCreated(int64_t display_id) { 1331 void ChromeLauncherController::OnShelfCreated(int64_t display_id) {
160 if (!ConnectToShelfController()) 1332 if (!ConnectToShelfController())
161 return; 1333 return;
162 1334
163 // The pref helper functions return default values for invalid display ids. 1335 // The pref helper functions return default values for invalid display ids.
164 PrefService* prefs = profile_->GetPrefs(); 1336 PrefService* prefs = profile_->GetPrefs();
165 shelf_controller_->SetAlignment( 1337 shelf_controller_->SetAlignment(
166 ash::launcher::GetShelfAlignmentPref(prefs, display_id), display_id); 1338 ash::launcher::GetShelfAlignmentPref(prefs, display_id), display_id);
167 shelf_controller_->SetAutoHideBehavior( 1339 shelf_controller_->SetAutoHideBehavior(
168 ash::launcher::GetShelfAutoHideBehaviorPref(prefs, display_id), 1340 ash::launcher::GetShelfAutoHideBehaviorPref(prefs, display_id),
(...skipping 15 matching lines...) Expand all
184 void ChromeLauncherController::OnAutoHideBehaviorChanged( 1356 void ChromeLauncherController::OnAutoHideBehaviorChanged(
185 ash::ShelfAutoHideBehavior auto_hide, 1357 ash::ShelfAutoHideBehavior auto_hide,
186 int64_t display_id) { 1358 int64_t display_id) {
187 DCHECK(!updating_shelf_pref_from_observer_); 1359 DCHECK(!updating_shelf_pref_from_observer_);
188 base::AutoReset<bool> updating(&updating_shelf_pref_from_observer_, true); 1360 base::AutoReset<bool> updating(&updating_shelf_pref_from_observer_, true);
189 // This will uselessly store a preference value for invalid display ids. 1361 // This will uselessly store a preference value for invalid display ids.
190 ash::launcher::SetShelfAutoHideBehaviorPref(profile_->GetPrefs(), display_id, 1362 ash::launcher::SetShelfAutoHideBehaviorPref(profile_->GetPrefs(), display_id,
191 auto_hide); 1363 auto_hide);
192 } 1364 }
193 1365
194 void ChromeLauncherController::OnAppImageUpdated(const std::string& app_id, 1366 ///////////////////////////////////////////////////////////////////////////////
195 const gfx::ImageSkia& image) { 1367 // ash::ShelfModelObserver:
196 // Implemented by subclasses; this should not be called. 1368
197 NOTREACHED(); 1369 void ChromeLauncherController::ShelfItemAdded(int index) {
1370 // Update the pin position preference as needed.
1371 ash::ShelfItem item = model_->items()[index];
1372 if (ItemTypeIsPinned(item) && should_sync_pin_changes_)
1373 SyncPinPosition(item.id);
1374
1375 // TODO(khmel): Fix this Arc application id mapping. See http://b/31703859
1376 const std::string shelf_app_id =
1377 ArcAppWindowLauncherController::GetShelfAppIdFromArcAppId(
1378 item.app_launch_id.app_id());
1379
1380 // Fetch and update the icon for the app's item.
1381 AppIconLoader* app_icon_loader = GetAppIconLoaderForApp(shelf_app_id);
1382 if (app_icon_loader) {
1383 app_icon_loader->FetchImage(shelf_app_id);
1384 app_icon_loader->UpdateImage(shelf_app_id);
1385 }
1386
1387 // Update the item with any missing Chrome-specific info.
1388 if (item.type == ash::TYPE_APP || item.type == ash::TYPE_PINNED_APP) {
1389 bool needs_update = false;
1390 if (item.image.isNull()) {
1391 needs_update = true;
1392 item.image = extensions::util::GetDefaultAppIcon();
1393 }
1394 if (item.title.empty()) {
1395 needs_update = true;
1396 item.title =
1397 LauncherControllerHelper::GetAppTitle(profile(), shelf_app_id);
1398 }
1399 ash::ShelfItemStatus status = GetAppState(shelf_app_id);
1400 if (status != item.status && status != ash::STATUS_CLOSED) {
1401 needs_update = true;
1402 item.status = status;
1403 }
1404 if (needs_update)
1405 model_->Set(index, item);
1406 }
1407
1408 // Construct a ShelfItemDelegate for the item if one does not yet exist.
1409 if (!model_->GetShelfItemDelegate(item.id)) {
1410 model_->SetShelfItemDelegate(
1411 item.id, AppShortcutLauncherItemController::Create(ash::AppLaunchId(
1412 shelf_app_id, item.app_launch_id.launch_id())));
1413 }
198 } 1414 }
1415
1416 void ChromeLauncherController::ShelfItemRemoved(
1417 int index,
1418 const ash::ShelfItem& old_item) {
1419 // TODO(khmel): Fix this Arc application id mapping. See http://b/31703859
1420 const std::string shelf_app_id =
1421 ArcAppWindowLauncherController::GetShelfAppIdFromArcAppId(
1422 old_item.app_launch_id.app_id());
1423
1424 // Remove the pin position from preferences as needed.
1425 if (ItemTypeIsPinned(old_item) && should_sync_pin_changes_) {
1426 ash::AppLaunchId app_launch_id(shelf_app_id,
1427 old_item.app_launch_id.launch_id());
1428 ash::launcher::RemovePinPosition(profile(), app_launch_id);
1429 }
1430
1431 AppIconLoader* app_icon_loader = GetAppIconLoaderForApp(shelf_app_id);
1432 if (app_icon_loader)
1433 app_icon_loader->ClearImage(shelf_app_id);
1434 }
1435
1436 void ChromeLauncherController::ShelfItemMoved(int start_index,
1437 int target_index) {
1438 // Update the pin position preference as needed.
1439 const ash::ShelfItem& item = model_->items()[target_index];
1440 DCHECK_NE(ash::TYPE_APP_LIST, item.type);
1441 if (ItemTypeIsPinned(item) && should_sync_pin_changes_)
1442 SyncPinPosition(item.id);
1443 }
1444
1445 void ChromeLauncherController::ShelfItemChanged(
1446 int index,
1447 const ash::ShelfItem& old_item) {
1448 if (!should_sync_pin_changes_)
1449 return;
1450
1451 const ash::ShelfItem& item = model_->items()[index];
1452 // Add or remove the pin position from preferences as needed.
1453 if (!ItemTypeIsPinned(old_item) && ItemTypeIsPinned(item)) {
1454 SyncPinPosition(item.id);
1455 } else if (ItemTypeIsPinned(old_item) && !ItemTypeIsPinned(item)) {
1456 // TODO(khmel): Fix this Arc application id mapping. See http://b/31703859
1457 const std::string shelf_app_id =
1458 ArcAppWindowLauncherController::GetShelfAppIdFromArcAppId(
1459 old_item.app_launch_id.app_id());
1460
1461 ash::AppLaunchId app_launch_id(shelf_app_id,
1462 old_item.app_launch_id.launch_id());
1463 ash::launcher::RemovePinPosition(profile(), app_launch_id);
1464 }
1465 }
1466
1467 ///////////////////////////////////////////////////////////////////////////////
1468 // ash::WindowTreeHostManager::Observer:
1469
1470 void ChromeLauncherController::OnDisplayConfigurationChanged() {
1471 // In BOTTOM_LOCKED state, ignore the call of SetShelfBehaviorsFromPrefs.
1472 // Because it might be called by some operations, like crbug.com/627040
1473 // rotating screen.
1474 ash::WmShelf* shelf =
1475 ash::WmShelf::ForWindow(ash::ShellPort::Get()->GetPrimaryRootWindow());
1476 if (shelf->alignment() != ash::SHELF_ALIGNMENT_BOTTOM_LOCKED)
1477 SetShelfBehaviorsFromPrefs();
1478 }
1479
1480 ///////////////////////////////////////////////////////////////////////////////
1481 // AppSyncUIStateObserver:
1482
1483 void ChromeLauncherController::OnAppSyncUIStatusChanged() {
1484 // Update the app list button title to reflect the syncing status.
1485 base::string16 title = l10n_util::GetStringUTF16(
1486 app_sync_ui_state_->status() == AppSyncUIState::STATUS_SYNCING
1487 ? IDS_ASH_SHELF_APP_LIST_LAUNCHER_SYNCING_TITLE
1488 : IDS_ASH_SHELF_APP_LIST_LAUNCHER_TITLE);
1489
1490 const int app_list_index = model_->GetItemIndexForType(ash::TYPE_APP_LIST);
1491 DCHECK_GE(app_list_index, 0);
1492 ash::ShelfItem item = model_->items()[app_list_index];
1493 if (item.title != title) {
1494 item.title = title;
1495 model_->Set(app_list_index, item);
1496 }
1497 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698