Adding instrumentation to locate the source of jankiness
[chromium-blink-merge.git] / chrome / browser / ui / app_list / app_list_view_delegate.cc
blobc62666cc116babbc1ed5d7f09be697a07e711aad
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/app_list/app_list_view_delegate.h"
7 #include <vector>
9 #include "apps/custom_launcher_page_contents.h"
10 #include "base/callback.h"
11 #include "base/command_line.h"
12 #include "base/files/file_path.h"
13 #include "base/metrics/user_metrics.h"
14 #include "base/stl_util.h"
15 #include "chrome/browser/apps/scoped_keep_alive.h"
16 #include "chrome/browser/browser_process.h"
17 #include "chrome/browser/chrome_notification_types.h"
18 #include "chrome/browser/profiles/profile_info_cache.h"
19 #include "chrome/browser/profiles/profile_manager.h"
20 #include "chrome/browser/search/hotword_service.h"
21 #include "chrome/browser/search/hotword_service_factory.h"
22 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
23 #include "chrome/browser/ui/app_list/app_list_service.h"
24 #include "chrome/browser/ui/app_list/app_list_syncable_service.h"
25 #include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
26 #include "chrome/browser/ui/app_list/search/search_controller_factory.h"
27 #include "chrome/browser/ui/app_list/search/search_resource_manager.h"
28 #include "chrome/browser/ui/app_list/start_page_service.h"
29 #include "chrome/browser/ui/apps/chrome_app_delegate.h"
30 #include "chrome/browser/ui/browser_finder.h"
31 #include "chrome/browser/ui/chrome_pages.h"
32 #include "chrome/browser/ui/host_desktop.h"
33 #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
34 #include "chrome/browser/web_applications/web_app.h"
35 #include "chrome/common/chrome_switches.h"
36 #include "chrome/common/extensions/extension_constants.h"
37 #include "chrome/common/url_constants.h"
38 #include "components/signin/core/browser/signin_manager.h"
39 #include "content/public/browser/browser_thread.h"
40 #include "content/public/browser/notification_service.h"
41 #include "content/public/browser/page_navigator.h"
42 #include "content/public/browser/user_metrics.h"
43 #include "content/public/browser/web_contents.h"
44 #include "extensions/browser/extension_registry.h"
45 #include "extensions/common/constants.h"
46 #include "extensions/common/extension_set.h"
47 #include "extensions/common/manifest_constants.h"
48 #include "extensions/common/manifest_handlers/launcher_page_info.h"
49 #include "grit/theme_resources.h"
50 #include "ui/app_list/app_list_switches.h"
51 #include "ui/app_list/app_list_view_delegate_observer.h"
52 #include "ui/app_list/search_box_model.h"
53 #include "ui/app_list/search_controller.h"
54 #include "ui/app_list/speech_ui_model.h"
55 #include "ui/base/resource/resource_bundle.h"
56 #include "ui/views/controls/webview/webview.h"
58 #if defined(TOOLKIT_VIEWS)
59 #include "ui/views/controls/webview/webview.h"
60 #endif
62 #if defined(USE_AURA)
63 #include "ui/keyboard/keyboard_util.h"
64 #endif
66 #if defined(USE_ASH)
67 #include "ash/shell.h"
68 #include "ash/wm/maximize_mode/maximize_mode_controller.h"
69 #include "chrome/browser/ui/ash/app_list/app_sync_ui_state_watcher.h"
70 #endif
72 #if defined(OS_WIN)
73 #include "chrome/browser/web_applications/web_app_win.h"
74 #endif
77 namespace chrome {
78 const char kAppLauncherCategoryTag[] = "AppLauncher";
79 } // namespace chrome
81 namespace {
83 const int kAutoLaunchDefaultTimeoutMilliSec = 50;
85 #if defined(OS_WIN)
86 void CreateShortcutInWebAppDir(
87 const base::FilePath& app_data_dir,
88 base::Callback<void(const base::FilePath&)> callback,
89 const web_app::ShortcutInfo& info) {
90 content::BrowserThread::PostTaskAndReplyWithResult(
91 content::BrowserThread::FILE,
92 FROM_HERE,
93 base::Bind(web_app::CreateShortcutInWebAppDir, app_data_dir, info),
94 callback);
96 #endif
98 void PopulateUsers(const ProfileInfoCache& profile_info,
99 const base::FilePath& active_profile_path,
100 app_list::AppListViewDelegate::Users* users) {
101 users->clear();
102 const size_t count = profile_info.GetNumberOfProfiles();
103 for (size_t i = 0; i < count; ++i) {
104 app_list::AppListViewDelegate::User user;
105 user.name = profile_info.GetNameOfProfileAtIndex(i);
106 user.email = profile_info.GetUserNameOfProfileAtIndex(i);
107 user.profile_path = profile_info.GetPathOfProfileAtIndex(i);
108 user.active = active_profile_path == user.profile_path;
109 users->push_back(user);
113 // Gets a list of URLs of the custom launcher pages to show in the launcher.
114 // Returns a URL for each installed launcher page. If --custom-launcher-page is
115 // specified and valid, also includes that URL.
116 void GetCustomLauncherPageUrls(content::BrowserContext* browser_context,
117 std::vector<GURL>* urls) {
118 // First, check the command line.
119 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
120 if (app_list::switches::IsExperimentalAppListEnabled() &&
121 command_line->HasSwitch(app_list::switches::kCustomLauncherPage)) {
122 GURL custom_launcher_page_url(command_line->GetSwitchValueASCII(
123 app_list::switches::kCustomLauncherPage));
125 if (custom_launcher_page_url.SchemeIs(extensions::kExtensionScheme)) {
126 urls->push_back(custom_launcher_page_url);
127 } else {
128 LOG(ERROR) << "Invalid custom launcher page URL: "
129 << custom_launcher_page_url.possibly_invalid_spec();
133 // Search the list of installed extensions for ones with 'launcher_page'.
134 extensions::ExtensionRegistry* extension_registry =
135 extensions::ExtensionRegistry::Get(browser_context);
136 const extensions::ExtensionSet& enabled_extensions =
137 extension_registry->enabled_extensions();
138 for (extensions::ExtensionSet::const_iterator it = enabled_extensions.begin();
139 it != enabled_extensions.end();
140 ++it) {
141 const extensions::Extension* extension = it->get();
142 extensions::LauncherPageInfo* info =
143 extensions::LauncherPageHandler::GetInfo(extension);
144 if (!info)
145 continue;
147 urls->push_back(extension->GetResourceURL(info->page));
151 } // namespace
153 AppListViewDelegate::AppListViewDelegate(AppListControllerDelegate* controller)
154 : controller_(controller),
155 profile_(NULL),
156 model_(NULL),
157 scoped_observer_(this) {
158 CHECK(controller_);
159 // The SigninManagerFactor and the SigninManagers are observed to keep the
160 // profile switcher menu up to date, with the correct list of profiles and the
161 // correct email address (or none for signed out users) for each.
162 SigninManagerFactory::GetInstance()->AddObserver(this);
164 // Start observing all already-created SigninManagers.
165 ProfileManager* profile_manager = g_browser_process->profile_manager();
166 std::vector<Profile*> profiles = profile_manager->GetLoadedProfiles();
167 for (std::vector<Profile*>::iterator i = profiles.begin();
168 i != profiles.end();
169 ++i) {
170 SigninManagerBase* manager =
171 SigninManagerFactory::GetForProfileIfExists(*i);
172 if (manager) {
173 DCHECK(!scoped_observer_.IsObserving(manager));
174 scoped_observer_.Add(manager);
178 profile_manager->GetProfileInfoCache().AddObserver(this);
179 speech_ui_.reset(new app_list::SpeechUIModel);
181 #if defined(GOOGLE_CHROME_BUILD)
182 speech_ui_->set_logo(
183 *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
184 IDR_APP_LIST_GOOGLE_LOGO_VOICE_SEARCH));
185 #endif
187 registrar_.Add(this,
188 chrome::NOTIFICATION_APP_TERMINATING,
189 content::NotificationService::AllSources());
192 AppListViewDelegate::~AppListViewDelegate() {
193 // Note that the destructor is not always called. E.g. on Mac, this is owned
194 // by a leaky singleton. Essential shutdown work must be done by observing
195 // chrome::NOTIFICATION_APP_TERMINATING.
196 SetProfile(NULL);
197 g_browser_process->profile_manager()->GetProfileInfoCache().RemoveObserver(
198 this);
200 SigninManagerFactory* factory = SigninManagerFactory::GetInstance();
201 if (factory)
202 factory->RemoveObserver(this);
205 void AppListViewDelegate::SetProfile(Profile* new_profile) {
206 if (profile_ == new_profile)
207 return;
209 if (profile_) {
210 // Note: |search_resource_manager_| has a reference to |speech_ui_| so must
211 // be destroyed first.
212 search_resource_manager_.reset();
213 search_controller_.reset();
214 custom_page_contents_.clear();
215 app_list::StartPageService* start_page_service =
216 app_list::StartPageService::Get(profile_);
217 if (start_page_service)
218 start_page_service->RemoveObserver(this);
219 #if defined(USE_ASH)
220 app_sync_ui_state_watcher_.reset();
221 #endif
222 model_ = NULL;
225 profile_ = new_profile;
226 if (!profile_) {
227 speech_ui_->SetSpeechRecognitionState(app_list::SPEECH_RECOGNITION_OFF);
228 return;
231 model_ =
232 app_list::AppListSyncableServiceFactory::GetForProfile(profile_)->model();
234 #if defined(USE_ASH)
235 app_sync_ui_state_watcher_.reset(new AppSyncUIStateWatcher(profile_, model_));
236 #endif
238 SetUpSearchUI();
239 SetUpProfileSwitcher();
240 SetUpCustomLauncherPages();
242 // Clear search query.
243 model_->search_box()->SetText(base::string16());
246 void AppListViewDelegate::SetUpSearchUI() {
247 app_list::StartPageService* start_page_service =
248 app_list::StartPageService::Get(profile_);
249 if (start_page_service)
250 start_page_service->AddObserver(this);
252 speech_ui_->SetSpeechRecognitionState(start_page_service
253 ? start_page_service->state()
254 : app_list::SPEECH_RECOGNITION_OFF);
256 search_resource_manager_.reset(new app_list::SearchResourceManager(
257 profile_,
258 model_->search_box(),
259 speech_ui_.get()));
261 search_controller_ = CreateSearchController(
262 profile_, model_->search_box(), model_->results(), controller_);
265 void AppListViewDelegate::SetUpProfileSwitcher() {
266 // If a profile change is observed when there is no app list, there is nothing
267 // to update until SetProfile() calls this function again.
268 if (!profile_)
269 return;
271 // Don't populate the app list users if we are on the ash desktop.
272 chrome::HostDesktopType desktop = chrome::GetHostDesktopTypeForNativeWindow(
273 controller_->GetAppListWindow());
274 if (desktop == chrome::HOST_DESKTOP_TYPE_ASH)
275 return;
277 // Populate the app list users.
278 PopulateUsers(g_browser_process->profile_manager()->GetProfileInfoCache(),
279 profile_->GetPath(),
280 &users_);
282 FOR_EACH_OBSERVER(
283 app_list::AppListViewDelegateObserver, observers_, OnProfilesChanged());
286 void AppListViewDelegate::SetUpCustomLauncherPages() {
287 std::vector<GURL> custom_launcher_page_urls;
288 GetCustomLauncherPageUrls(profile_, &custom_launcher_page_urls);
289 for (std::vector<GURL>::const_iterator it = custom_launcher_page_urls.begin();
290 it != custom_launcher_page_urls.end();
291 ++it) {
292 std::string extension_id = it->host();
293 apps::CustomLauncherPageContents* page_contents =
294 new apps::CustomLauncherPageContents(
295 scoped_ptr<extensions::AppDelegate>(
296 new ChromeAppDelegate(scoped_ptr<ScopedKeepAlive>())),
297 extension_id);
298 page_contents->Initialize(profile_, *it);
299 custom_page_contents_.push_back(page_contents);
303 void AppListViewDelegate::OnHotwordStateChanged(bool started) {
304 if (started) {
305 if (speech_ui_->state() == app_list::SPEECH_RECOGNITION_READY) {
306 OnSpeechRecognitionStateChanged(
307 app_list::SPEECH_RECOGNITION_HOTWORD_LISTENING);
309 } else {
310 if (speech_ui_->state() == app_list::SPEECH_RECOGNITION_HOTWORD_LISTENING)
311 OnSpeechRecognitionStateChanged(app_list::SPEECH_RECOGNITION_READY);
315 void AppListViewDelegate::OnHotwordRecognized() {
316 DCHECK_EQ(app_list::SPEECH_RECOGNITION_HOTWORD_LISTENING,
317 speech_ui_->state());
318 ToggleSpeechRecognition();
321 void AppListViewDelegate::SigninManagerCreated(SigninManagerBase* manager) {
322 scoped_observer_.Add(manager);
325 void AppListViewDelegate::SigninManagerShutdown(SigninManagerBase* manager) {
326 if (scoped_observer_.IsObserving(manager))
327 scoped_observer_.Remove(manager);
330 void AppListViewDelegate::GoogleSigninFailed(
331 const GoogleServiceAuthError& error) {
332 SetUpProfileSwitcher();
335 void AppListViewDelegate::GoogleSigninSucceeded(const std::string& account_id,
336 const std::string& username,
337 const std::string& password) {
338 SetUpProfileSwitcher();
341 void AppListViewDelegate::GoogleSignedOut(const std::string& account_id,
342 const std::string& username) {
343 SetUpProfileSwitcher();
346 void AppListViewDelegate::OnProfileAdded(const base::FilePath& profile_path) {
347 SetUpProfileSwitcher();
350 void AppListViewDelegate::OnProfileWasRemoved(
351 const base::FilePath& profile_path,
352 const base::string16& profile_name) {
353 SetUpProfileSwitcher();
356 void AppListViewDelegate::OnProfileNameChanged(
357 const base::FilePath& profile_path,
358 const base::string16& old_profile_name) {
359 SetUpProfileSwitcher();
362 bool AppListViewDelegate::ForceNativeDesktop() const {
363 return controller_->ForceNativeDesktop();
366 void AppListViewDelegate::SetProfileByPath(const base::FilePath& profile_path) {
367 DCHECK(model_);
368 // The profile must be loaded before this is called.
369 SetProfile(
370 g_browser_process->profile_manager()->GetProfileByPath(profile_path));
373 app_list::AppListModel* AppListViewDelegate::GetModel() {
374 return model_;
377 app_list::SpeechUIModel* AppListViewDelegate::GetSpeechUI() {
378 return speech_ui_.get();
381 void AppListViewDelegate::GetShortcutPathForApp(
382 const std::string& app_id,
383 const base::Callback<void(const base::FilePath&)>& callback) {
384 #if defined(OS_WIN)
385 const extensions::Extension* extension =
386 extensions::ExtensionRegistry::Get(profile_)->GetExtensionById(
387 app_id, extensions::ExtensionRegistry::EVERYTHING);
388 if (!extension) {
389 callback.Run(base::FilePath());
390 return;
393 base::FilePath app_data_dir(
394 web_app::GetWebAppDataDirectory(profile_->GetPath(),
395 extension->id(),
396 GURL()));
398 web_app::GetShortcutInfoForApp(
399 extension,
400 profile_,
401 base::Bind(CreateShortcutInWebAppDir, app_data_dir, callback));
402 #else
403 callback.Run(base::FilePath());
404 #endif
407 void AppListViewDelegate::StartSearch() {
408 if (search_controller_) {
409 search_controller_->Start();
410 controller_->OnSearchStarted();
414 void AppListViewDelegate::StopSearch() {
415 if (search_controller_)
416 search_controller_->Stop();
419 void AppListViewDelegate::OpenSearchResult(
420 app_list::SearchResult* result,
421 bool auto_launch,
422 int event_flags) {
423 if (auto_launch)
424 base::RecordAction(base::UserMetricsAction("AppList_AutoLaunched"));
425 search_controller_->OpenResult(result, event_flags);
428 void AppListViewDelegate::InvokeSearchResultAction(
429 app_list::SearchResult* result,
430 int action_index,
431 int event_flags) {
432 search_controller_->InvokeResultAction(result, action_index, event_flags);
435 base::TimeDelta AppListViewDelegate::GetAutoLaunchTimeout() {
436 return auto_launch_timeout_;
439 void AppListViewDelegate::AutoLaunchCanceled() {
440 base::RecordAction(base::UserMetricsAction("AppList_AutoLaunchCanceled"));
441 auto_launch_timeout_ = base::TimeDelta();
444 void AppListViewDelegate::ViewInitialized() {
445 app_list::StartPageService* service =
446 app_list::StartPageService::Get(profile_);
447 if (service) {
448 service->AppListShown();
449 if (service->HotwordEnabled()) {
450 HotwordService* hotword_service =
451 HotwordServiceFactory::GetForProfile(profile_);
452 if (hotword_service)
453 hotword_service->RequestHotwordSession(this);
458 void AppListViewDelegate::Dismiss() {
459 controller_->DismissView();
462 void AppListViewDelegate::ViewClosing() {
463 controller_->ViewClosing();
465 if (!profile_)
466 return;
468 app_list::StartPageService* service =
469 app_list::StartPageService::Get(profile_);
470 if (service) {
471 service->AppListHidden();
472 if (service->HotwordEnabled()) {
473 HotwordService* hotword_service =
474 HotwordServiceFactory::GetForProfile(profile_);
475 if (hotword_service) {
476 hotword_service->StopHotwordSession(this);
478 // If we're in always-on mode, we always want to restart hotwording
479 // after closing the launcher window. So, in always-on mode, hotwording
480 // is stopped, and then started again right away. Note that hotwording
481 // may already be stopped. The call to StopHotwordSession() above both
482 // explicitly stops hotwording, if it's running, and clears the
483 // association between the hotword service and |this|. When starting up
484 // hotwording, pass nullptr as the client so that hotword triggers cause
485 // the launcher to open.
486 // TODO(amistry): This only works on ChromeOS since Chrome hides the
487 // launcher instead of destroying it. Make this work on Chrome.
488 if (hotword_service->IsAlwaysOnEnabled())
489 hotword_service->RequestHotwordSession(nullptr);
495 gfx::ImageSkia AppListViewDelegate::GetWindowIcon() {
496 return controller_->GetWindowIcon();
499 void AppListViewDelegate::OpenSettings() {
500 const extensions::Extension* extension =
501 extensions::ExtensionRegistry::Get(profile_)->GetExtensionById(
502 extension_misc::kSettingsAppId,
503 extensions::ExtensionRegistry::EVERYTHING);
504 DCHECK(extension);
505 controller_->ActivateApp(profile_,
506 extension,
507 AppListControllerDelegate::LAUNCH_FROM_UNKNOWN,
511 void AppListViewDelegate::OpenHelp() {
512 chrome::HostDesktopType desktop = chrome::GetHostDesktopTypeForNativeWindow(
513 controller_->GetAppListWindow());
514 chrome::ScopedTabbedBrowserDisplayer displayer(profile_, desktop);
515 content::OpenURLParams params(GURL(chrome::kAppLauncherHelpURL),
516 content::Referrer(),
517 NEW_FOREGROUND_TAB,
518 ui::PAGE_TRANSITION_LINK,
519 false);
520 displayer.browser()->OpenURL(params);
523 void AppListViewDelegate::OpenFeedback() {
524 chrome::HostDesktopType desktop = chrome::GetHostDesktopTypeForNativeWindow(
525 controller_->GetAppListWindow());
526 Browser* browser = chrome::FindTabbedBrowser(profile_, false, desktop);
527 chrome::ShowFeedbackPage(browser, std::string(),
528 chrome::kAppLauncherCategoryTag);
531 void AppListViewDelegate::ToggleSpeechRecognition() {
532 app_list::StartPageService* service =
533 app_list::StartPageService::Get(profile_);
534 if (service)
535 service->ToggleSpeechRecognition();
537 // With the new hotword extension, stop the hotword session. With the launcher
538 // and NTP, this is unnecessary since the hotwording is implicitly stopped.
539 // However, for always on, hotword triggering launches the launcher which
540 // starts a session and hence starts the hotword detector. This results in the
541 // hotword detector and the speech-to-text engine running in parallel, which
542 // will conflict with each other (i.e. saying 'Ok Google' twice in a row
543 // should cause a search to happen for 'Ok Google', not two hotword triggers).
544 // To get around this, always stop the session when switching to speech
545 // recognition.
546 if (HotwordService::IsExperimentalHotwordingEnabled() &&
547 service && service->HotwordEnabled()) {
548 HotwordService* hotword_service =
549 HotwordServiceFactory::GetForProfile(profile_);
550 if (hotword_service)
551 hotword_service->StopHotwordSession(this);
555 void AppListViewDelegate::ShowForProfileByPath(
556 const base::FilePath& profile_path) {
557 controller_->ShowForProfileByPath(profile_path);
560 void AppListViewDelegate::OnSpeechResult(const base::string16& result,
561 bool is_final) {
562 speech_ui_->SetSpeechResult(result, is_final);
563 if (is_final) {
564 auto_launch_timeout_ = base::TimeDelta::FromMilliseconds(
565 kAutoLaunchDefaultTimeoutMilliSec);
566 model_->search_box()->SetText(result);
570 void AppListViewDelegate::OnSpeechSoundLevelChanged(int16 level) {
571 speech_ui_->UpdateSoundLevel(level);
574 void AppListViewDelegate::OnSpeechRecognitionStateChanged(
575 app_list::SpeechRecognitionState new_state) {
576 speech_ui_->SetSpeechRecognitionState(new_state);
578 app_list::StartPageService* service =
579 app_list::StartPageService::Get(profile_);
580 // With the new hotword extension, we need to re-request hotwording after
581 // speech recognition has stopped. Do not request hotwording after the app
582 // list has already closed.
583 if (new_state == app_list::SPEECH_RECOGNITION_READY &&
584 HotwordService::IsExperimentalHotwordingEnabled() &&
585 service && service->HotwordEnabled() &&
586 controller_->GetAppListWindow()) {
587 HotwordService* hotword_service =
588 HotwordServiceFactory::GetForProfile(profile_);
589 if (hotword_service) {
590 hotword_service->RequestHotwordSession(this);
595 #if defined(TOOLKIT_VIEWS)
596 views::View* AppListViewDelegate::CreateStartPageWebView(
597 const gfx::Size& size) {
598 app_list::StartPageService* service =
599 app_list::StartPageService::Get(profile_);
600 if (!service)
601 return NULL;
603 content::WebContents* web_contents = service->GetStartPageContents();
604 if (!web_contents)
605 return NULL;
607 DCHECK_EQ(profile_, web_contents->GetBrowserContext());
608 views::WebView* web_view = new views::WebView(
609 web_contents->GetBrowserContext());
610 web_view->SetPreferredSize(size);
611 web_view->SetWebContents(web_contents);
612 return web_view;
615 std::vector<views::View*> AppListViewDelegate::CreateCustomPageWebViews(
616 const gfx::Size& size) {
617 std::vector<views::View*> web_views;
619 for (ScopedVector<apps::CustomLauncherPageContents>::const_iterator it =
620 custom_page_contents_.begin();
621 it != custom_page_contents_.end();
622 ++it) {
623 content::WebContents* web_contents = (*it)->web_contents();
624 // TODO(mgiuca): DCHECK_EQ(profile_, web_contents->GetBrowserContext())
625 // after http://crbug.com/392763 resolved.
626 views::WebView* web_view =
627 new views::WebView(web_contents->GetBrowserContext());
628 web_view->SetPreferredSize(size);
629 web_view->SetWebContents(web_contents);
630 web_views.push_back(web_view);
633 return web_views;
635 #endif
637 bool AppListViewDelegate::IsSpeechRecognitionEnabled() {
638 app_list::StartPageService* service =
639 app_list::StartPageService::Get(profile_);
640 return service && service->GetSpeechRecognitionContents();
643 const app_list::AppListViewDelegate::Users&
644 AppListViewDelegate::GetUsers() const {
645 return users_;
648 bool AppListViewDelegate::ShouldCenterWindow() const {
649 if (app_list::switches::IsCenteredAppListEnabled())
650 return true;
652 // keyboard depends upon Aura.
653 #if defined(USE_AURA)
654 // If the virtual keyboard is enabled, use the new app list position. The old
655 // position is too tall, and doesn't fit in the left-over screen space.
656 if (keyboard::IsKeyboardEnabled())
657 return true;
658 #endif
660 #if defined(USE_ASH)
661 // If it is at all possible to enter maximize mode in this configuration
662 // (which has a virtual keyboard), we should use the experimental position.
663 // This avoids having the app list change shape and position as the user
664 // enters and exits maximize mode.
665 if (ash::Shell::HasInstance() &&
666 ash::Shell::GetInstance()
667 ->maximize_mode_controller()
668 ->CanEnterMaximizeMode()) {
669 return true;
671 #endif
673 return false;
676 void AppListViewDelegate::AddObserver(
677 app_list::AppListViewDelegateObserver* observer) {
678 observers_.AddObserver(observer);
681 void AppListViewDelegate::RemoveObserver(
682 app_list::AppListViewDelegateObserver* observer) {
683 observers_.RemoveObserver(observer);
686 void AppListViewDelegate::Observe(int type,
687 const content::NotificationSource& source,
688 const content::NotificationDetails& details) {
689 switch (type) {
690 case chrome::NOTIFICATION_APP_TERMINATING:
691 FOR_EACH_OBSERVER(
692 app_list::AppListViewDelegateObserver, observers_, OnShutdown());
694 SetProfile(NULL); // Ensures launcher page web contents are torn down.
696 // SigninManagerFactory is not a leaky singleton (unlike this class), and
697 // its destructor will check that it has no remaining observers.
698 scoped_observer_.RemoveAll();
699 SigninManagerFactory::GetInstance()->RemoveObserver(this);
700 break;
701 default:
702 NOTREACHED();