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

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

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

Powered by Google App Engine
This is Rietveld 408576698