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
3 // found in the LICENSE file.
5 #include "chrome/browser/apps/app_shim/extension_app_shim_handler_mac.h"
7 #include "apps/app_lifetime_monitor_factory.h"
8 #include "apps/launcher.h"
9 #include "base/files/file_path.h"
10 #include "base/logging.h"
11 #include "chrome/browser/apps/app_shim/app_shim_host_manager_mac.h"
12 #include "chrome/browser/browser_process.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/extensions/launch_util.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/profiles/profile_manager.h"
17 #include "chrome/browser/profiles/profile_window.h"
18 #include "chrome/browser/profiles/profiles_state.h"
19 #include "chrome/browser/ui/browser_list.h"
20 #include "chrome/browser/ui/browser_window.h"
21 #include "chrome/browser/ui/extensions/application_launch.h"
22 #include "chrome/browser/ui/extensions/extension_enable_flow.h"
23 #include "chrome/browser/ui/extensions/extension_enable_flow_delegate.h"
24 #include "chrome/browser/ui/user_manager.h"
25 #include "chrome/browser/web_applications/web_app_mac.h"
26 #include "chrome/common/extensions/extension_constants.h"
27 #include "chrome/common/extensions/extension_metrics.h"
28 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
29 #include "chrome/common/mac/app_shim_messages.h"
30 #include "components/crx_file/id_util.h"
31 #include "content/public/browser/notification_details.h"
32 #include "content/public/browser/notification_service.h"
33 #include "content/public/browser/notification_source.h"
34 #include "extensions/browser/app_window/app_window.h"
35 #include "extensions/browser/app_window/app_window_registry.h"
36 #include "extensions/browser/app_window/native_app_window.h"
37 #include "extensions/browser/extension_host.h"
38 #include "extensions/browser/extension_prefs.h"
39 #include "extensions/browser/extension_registry.h"
40 #include "extensions/common/constants.h"
41 #include "ui/base/cocoa/focus_window_set.h"
43 using extensions::AppWindow
;
44 using extensions::AppWindowRegistry
;
45 using extensions::Extension
;
46 using extensions::ExtensionRegistry
;
50 typedef AppWindowRegistry::AppWindowList AppWindowList
;
52 void ProfileLoadedCallback(base::Callback
<void(Profile
*)> callback
,
54 Profile::CreateStatus status
) {
55 if (status
== Profile::CREATE_STATUS_INITIALIZED
) {
56 callback
.Run(profile
);
60 // This should never get an error since it only loads existing profiles.
61 DCHECK_EQ(Profile::CREATE_STATUS_CREATED
, status
);
64 void SetAppHidden(Profile
* profile
, const std::string
& app_id
, bool hidden
) {
65 AppWindowList windows
=
66 AppWindowRegistry::Get(profile
)->GetAppWindowsForApp(app_id
);
67 for (AppWindowList::const_reverse_iterator it
= windows
.rbegin();
71 (*it
)->GetBaseWindow()->HideWithApp();
73 (*it
)->GetBaseWindow()->ShowWithApp();
77 bool FocusWindows(const AppWindowList
& windows
) {
81 std::set
<gfx::NativeWindow
> native_windows
;
82 for (AppWindowList::const_iterator it
= windows
.begin(); it
!= windows
.end();
84 native_windows
.insert((*it
)->GetNativeWindow());
86 // Allow workspace switching. For the browser process, we can reasonably rely
87 // on OS X to switch spaces for us and honor relevant user settings. But shims
88 // don't have windows, so we have to do it ourselves.
89 ui::FocusWindowSet(native_windows
);
93 bool FocusHostedAppWindows(std::set
<Browser
*>& browsers
) {
97 std::set
<gfx::NativeWindow
> native_windows
;
98 for (const Browser
* browser
: browsers
)
99 native_windows
.insert(browser
->window()->GetNativeWindow());
101 ui::FocusWindowSet(native_windows
);
105 // Attempts to launch a packaged app, prompting the user to enable it if
106 // necessary. The prompt is shown in its own window.
107 // This class manages its own lifetime.
108 class EnableViaPrompt
: public ExtensionEnableFlowDelegate
{
110 EnableViaPrompt(Profile
* profile
,
111 const std::string
& extension_id
,
112 const base::Callback
<void()>& callback
)
114 extension_id_(extension_id
),
115 callback_(callback
) {
118 ~EnableViaPrompt() override
{}
121 flow_
.reset(new ExtensionEnableFlow(profile_
, extension_id_
, this));
122 flow_
->StartForCurrentlyNonexistentWindow(
123 base::Callback
<gfx::NativeWindow(void)>());
127 // ExtensionEnableFlowDelegate overrides.
128 void ExtensionEnableFlowFinished() override
{
133 void ExtensionEnableFlowAborted(bool user_initiated
) override
{
139 std::string extension_id_
;
140 base::Callback
<void()> callback_
;
141 scoped_ptr
<ExtensionEnableFlow
> flow_
;
143 DISALLOW_COPY_AND_ASSIGN(EnableViaPrompt
);
150 bool ExtensionAppShimHandler::Delegate::ProfileExistsForPath(
151 const base::FilePath
& path
) {
152 ProfileManager
* profile_manager
= g_browser_process
->profile_manager();
153 // Check for the profile name in the profile info cache to ensure that we
154 // never access any directory that isn't a known profile.
155 base::FilePath full_path
= profile_manager
->user_data_dir().Append(path
);
156 ProfileInfoCache
& cache
= profile_manager
->GetProfileInfoCache();
157 return cache
.GetIndexOfProfileWithPath(full_path
) != std::string::npos
;
160 Profile
* ExtensionAppShimHandler::Delegate::ProfileForPath(
161 const base::FilePath
& path
) {
162 ProfileManager
* profile_manager
= g_browser_process
->profile_manager();
163 base::FilePath full_path
= profile_manager
->user_data_dir().Append(path
);
164 Profile
* profile
= profile_manager
->GetProfileByPath(full_path
);
166 // Use IsValidProfile to check if the profile has been created.
167 return profile
&& profile_manager
->IsValidProfile(profile
) ? profile
: NULL
;
170 void ExtensionAppShimHandler::Delegate::LoadProfileAsync(
171 const base::FilePath
& path
,
172 base::Callback
<void(Profile
*)> callback
) {
173 ProfileManager
* profile_manager
= g_browser_process
->profile_manager();
174 base::FilePath full_path
= profile_manager
->user_data_dir().Append(path
);
175 profile_manager
->CreateProfileAsync(
177 base::Bind(&ProfileLoadedCallback
, callback
),
178 base::string16(), base::string16(), std::string());
181 bool ExtensionAppShimHandler::Delegate::IsProfileLockedForPath(
182 const base::FilePath
& path
) {
183 ProfileManager
* profile_manager
= g_browser_process
->profile_manager();
184 base::FilePath full_path
= profile_manager
->user_data_dir().Append(path
);
185 return profiles::IsProfileLocked(full_path
);
188 AppWindowList
ExtensionAppShimHandler::Delegate::GetWindows(
190 const std::string
& extension_id
) {
191 return AppWindowRegistry::Get(profile
)->GetAppWindowsForApp(extension_id
);
194 const Extension
* ExtensionAppShimHandler::Delegate::MaybeGetAppExtension(
196 const std::string
& extension_id
) {
197 return ExtensionAppShimHandler::MaybeGetAppExtension(profile
, extension_id
);
200 void ExtensionAppShimHandler::Delegate::EnableExtension(
202 const std::string
& extension_id
,
203 const base::Callback
<void()>& callback
) {
204 (new EnableViaPrompt(profile
, extension_id
, callback
))->Run();
207 void ExtensionAppShimHandler::Delegate::LaunchApp(
209 const Extension
* extension
,
210 const std::vector
<base::FilePath
>& files
) {
211 extensions::RecordAppLaunchType(
212 extension_misc::APP_LAUNCH_CMD_LINE_APP
, extension
->GetType());
213 if (extension
->is_hosted_app()) {
214 AppLaunchParams
launch_params(profile
, extension
, NEW_FOREGROUND_TAB
,
215 extensions::SOURCE_COMMAND_LINE
);
216 OpenApplication(launch_params
);
220 apps::LaunchPlatformApp(
221 profile
, extension
, extensions::SOURCE_COMMAND_LINE
);
223 for (std::vector
<base::FilePath
>::const_iterator it
= files
.begin();
224 it
!= files
.end(); ++it
) {
225 apps::LaunchPlatformAppWithPath(profile
, extension
, *it
);
230 void ExtensionAppShimHandler::Delegate::LaunchShim(Profile
* profile
,
231 const Extension
* extension
) {
232 web_app::MaybeLaunchShortcut(
233 web_app::ShortcutInfoForExtensionAndProfile(extension
, profile
));
236 void ExtensionAppShimHandler::Delegate::LaunchUserManager() {
237 UserManager::Show(base::FilePath(),
238 profiles::USER_MANAGER_NO_TUTORIAL
,
239 profiles::USER_MANAGER_SELECT_PROFILE_APP_LAUNCHER
);
242 void ExtensionAppShimHandler::Delegate::MaybeTerminate() {
243 AppShimHandler::MaybeTerminate();
246 ExtensionAppShimHandler::ExtensionAppShimHandler()
247 : delegate_(new Delegate
),
248 weak_factory_(this) {
249 // This is instantiated in BrowserProcessImpl::PreMainMessageLoopRun with
250 // AppShimHostManager. Since PROFILE_CREATED is not fired until
251 // ProfileManager::GetLastUsedProfile/GetLastOpenedProfiles, this should catch
252 // notifications for all profiles.
253 registrar_
.Add(this, chrome::NOTIFICATION_PROFILE_CREATED
,
254 content::NotificationService::AllBrowserContextsAndSources());
255 registrar_
.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED
,
256 content::NotificationService::AllBrowserContextsAndSources());
257 registrar_
.Add(this, chrome::NOTIFICATION_BROWSER_WINDOW_READY
,
258 content::NotificationService::AllBrowserContextsAndSources());
259 BrowserList::AddObserver(this);
262 ExtensionAppShimHandler::~ExtensionAppShimHandler() {
263 BrowserList::RemoveObserver(this);
266 AppShimHandler::Host
* ExtensionAppShimHandler::FindHost(
268 const std::string
& app_id
) {
269 HostMap::iterator it
= hosts_
.find(make_pair(profile
, app_id
));
270 return it
== hosts_
.end() ? NULL
: it
->second
;
273 void ExtensionAppShimHandler::SetHostedAppHidden(Profile
* profile
,
274 const std::string
& app_id
,
276 const AppBrowserMap::iterator it
= app_browser_windows_
.find(app_id
);
277 if (it
== app_browser_windows_
.end())
280 for (const Browser
* browser
: it
->second
) {
281 if (web_app::GetExtensionIdFromApplicationName(browser
->app_name()) !=
287 browser
->window()->Hide();
289 browser
->window()->Show();
294 const Extension
* ExtensionAppShimHandler::MaybeGetAppExtension(
296 const std::string
& extension_id
) {
300 ExtensionRegistry
* registry
= ExtensionRegistry::Get(profile
);
301 const Extension
* extension
=
302 registry
->GetExtensionById(extension_id
, ExtensionRegistry::ENABLED
);
304 (extension
->is_platform_app() || extension
->is_hosted_app())
310 const Extension
* ExtensionAppShimHandler::MaybeGetAppForBrowser(
312 if (!browser
|| !browser
->is_app())
315 return MaybeGetAppExtension(
317 web_app::GetExtensionIdFromApplicationName(browser
->app_name()));
321 void ExtensionAppShimHandler::QuitAppForWindow(AppWindow
* app_window
) {
322 ExtensionAppShimHandler
* handler
= GetInstance();
323 Host
* host
= handler
->FindHost(
324 Profile::FromBrowserContext(app_window
->browser_context()),
325 app_window
->extension_id());
327 handler
->OnShimQuit(host
);
329 // App shims might be disabled or the shim is still starting up.
330 AppWindowRegistry::Get(
331 Profile::FromBrowserContext(app_window
->browser_context()))
332 ->CloseAllAppWindowsForApp(app_window
->extension_id());
337 void ExtensionAppShimHandler::QuitHostedAppForWindow(
339 const std::string
& app_id
) {
340 ExtensionAppShimHandler
* handler
= GetInstance();
341 Host
* host
= handler
->FindHost(Profile::FromBrowserContext(profile
), app_id
);
343 handler
->OnShimQuit(host
);
345 handler
->CloseBrowsersForApp(app_id
);
348 void ExtensionAppShimHandler::HideAppForWindow(AppWindow
* app_window
) {
349 ExtensionAppShimHandler
* handler
= GetInstance();
350 Profile
* profile
= Profile::FromBrowserContext(app_window
->browser_context());
351 Host
* host
= handler
->FindHost(profile
, app_window
->extension_id());
355 SetAppHidden(profile
, app_window
->extension_id(), true);
358 void ExtensionAppShimHandler::HideHostedApp(Profile
* profile
,
359 const std::string
& app_id
) {
360 ExtensionAppShimHandler
* handler
= GetInstance();
361 Host
* host
= handler
->FindHost(profile
, app_id
);
365 handler
->SetHostedAppHidden(profile
, app_id
, true);
368 void ExtensionAppShimHandler::FocusAppForWindow(AppWindow
* app_window
) {
369 ExtensionAppShimHandler
* handler
= GetInstance();
370 Profile
* profile
= Profile::FromBrowserContext(app_window
->browser_context());
371 const std::string
& app_id
= app_window
->extension_id();
372 Host
* host
= handler
->FindHost(profile
, app_id
);
374 handler
->OnShimFocus(host
,
375 APP_SHIM_FOCUS_NORMAL
,
376 std::vector
<base::FilePath
>());
378 FocusWindows(AppWindowRegistry::Get(profile
)->GetAppWindowsForApp(app_id
));
383 void ExtensionAppShimHandler::UnhideWithoutActivationForWindow(
384 AppWindow
* app_window
) {
385 ExtensionAppShimHandler
* handler
= GetInstance();
386 Profile
* profile
= Profile::FromBrowserContext(app_window
->browser_context());
387 Host
* host
= handler
->FindHost(profile
, app_window
->extension_id());
389 host
->OnAppUnhideWithoutActivation();
393 void ExtensionAppShimHandler::RequestUserAttentionForWindow(
394 AppWindow
* app_window
,
395 AppShimAttentionType attention_type
) {
396 ExtensionAppShimHandler
* handler
= GetInstance();
397 Profile
* profile
= Profile::FromBrowserContext(app_window
->browser_context());
398 Host
* host
= handler
->FindHost(profile
, app_window
->extension_id());
400 host
->OnAppRequestUserAttention(attention_type
);
404 void ExtensionAppShimHandler::OnChromeWillHide() {
405 // Send OnAppHide to all the shims so that they go into the hidden state.
406 // This is necessary so that when the shim is next focused, it will know to
408 ExtensionAppShimHandler
* handler
= GetInstance();
409 for (HostMap::iterator it
= handler
->hosts_
.begin();
410 it
!= handler
->hosts_
.end();
412 it
->second
->OnAppHide();
416 void ExtensionAppShimHandler::OnShimLaunch(
418 AppShimLaunchType launch_type
,
419 const std::vector
<base::FilePath
>& files
) {
420 const std::string
& app_id
= host
->GetAppId();
421 DCHECK(crx_file::id_util::IdIsValid(app_id
));
423 const base::FilePath
& profile_path
= host
->GetProfilePath();
424 DCHECK(!profile_path
.empty());
426 if (!delegate_
->ProfileExistsForPath(profile_path
)) {
427 // User may have deleted the profile this shim was originally created for.
428 // TODO(jackhou): Add some UI for this case and remove the LOG.
429 LOG(ERROR
) << "Requested directory is not a known profile '"
430 << profile_path
.value() << "'.";
431 host
->OnAppLaunchComplete(APP_SHIM_LAUNCH_PROFILE_NOT_FOUND
);
435 if (delegate_
->IsProfileLockedForPath(profile_path
)) {
436 LOG(WARNING
) << "Requested profile is locked. Showing User Manager.";
437 host
->OnAppLaunchComplete(APP_SHIM_LAUNCH_PROFILE_LOCKED
);
438 delegate_
->LaunchUserManager();
442 Profile
* profile
= delegate_
->ProfileForPath(profile_path
);
445 OnProfileLoaded(host
, launch_type
, files
, profile
);
449 // If the profile is not loaded, this must have been a launch by the shim.
450 // Load the profile asynchronously, the host will be registered in
452 DCHECK_EQ(APP_SHIM_LAUNCH_NORMAL
, launch_type
);
453 delegate_
->LoadProfileAsync(
455 base::Bind(&ExtensionAppShimHandler::OnProfileLoaded
,
456 weak_factory_
.GetWeakPtr(),
457 host
, launch_type
, files
));
459 // Return now. OnAppLaunchComplete will be called when the app is activated.
463 ExtensionAppShimHandler
* ExtensionAppShimHandler::GetInstance() {
464 return g_browser_process
->platform_part()
465 ->app_shim_host_manager()
466 ->extension_app_shim_handler();
469 const Extension
* ExtensionAppShimHandler::MaybeGetExtensionOrCloseHost(
471 Profile
** profile_out
) {
472 DCHECK(delegate_
->ProfileExistsForPath(host
->GetProfilePath()));
473 Profile
* profile
= delegate_
->ProfileForPath(host
->GetProfilePath());
475 const Extension
* extension
=
476 delegate_
->MaybeGetAppExtension(profile
, host
->GetAppId());
478 // Extensions may have been uninstalled or disabled since the shim
484 *profile_out
= profile
;
488 void ExtensionAppShimHandler::CloseBrowsersForApp(const std::string
& app_id
) {
489 AppBrowserMap::iterator it
= app_browser_windows_
.find(app_id
);
490 if (it
== app_browser_windows_
.end())
493 for (const Browser
* browser
: it
->second
)
494 browser
->window()->Close();
497 void ExtensionAppShimHandler::OnProfileLoaded(
499 AppShimLaunchType launch_type
,
500 const std::vector
<base::FilePath
>& files
,
502 const std::string
& app_id
= host
->GetAppId();
504 // The first host to claim this (profile, app_id) becomes the main host.
505 // For any others, focus or relaunch the app.
506 if (!hosts_
.insert(make_pair(make_pair(profile
, app_id
), host
)).second
) {
508 launch_type
== APP_SHIM_LAUNCH_NORMAL
?
509 APP_SHIM_FOCUS_REOPEN
: APP_SHIM_FOCUS_NORMAL
,
511 host
->OnAppLaunchComplete(APP_SHIM_LAUNCH_DUPLICATE_HOST
);
515 if (launch_type
!= APP_SHIM_LAUNCH_NORMAL
) {
516 host
->OnAppLaunchComplete(APP_SHIM_LAUNCH_SUCCESS
);
520 // TODO(jeremya): Handle the case that launching the app fails. Probably we
521 // need to watch for 'app successfully launched' or at least 'background page
522 // exists/was created' and time out with failure if we don't see that sign of
523 // life within a certain window.
524 const Extension
* extension
= delegate_
->MaybeGetAppExtension(profile
, app_id
);
526 delegate_
->LaunchApp(profile
, extension
, files
);
527 // If it's a hosted app that opens in a tab, let the shim terminate
529 if (extension
->is_hosted_app() &&
530 extensions::GetLaunchType(extensions::ExtensionPrefs::Get(profile
),
532 extensions::LAUNCH_TYPE_REGULAR
) {
533 host
->OnAppLaunchComplete(APP_SHIM_LAUNCH_DUPLICATE_HOST
);
538 delegate_
->EnableExtension(
540 base::Bind(&ExtensionAppShimHandler::OnExtensionEnabled
,
541 weak_factory_
.GetWeakPtr(),
542 host
->GetProfilePath(), app_id
, files
));
545 void ExtensionAppShimHandler::OnExtensionEnabled(
546 const base::FilePath
& profile_path
,
547 const std::string
& app_id
,
548 const std::vector
<base::FilePath
>& files
) {
549 Profile
* profile
= delegate_
->ProfileForPath(profile_path
);
553 const Extension
* extension
= delegate_
->MaybeGetAppExtension(profile
, app_id
);
554 if (!extension
|| !delegate_
->ProfileExistsForPath(profile_path
)) {
555 // If !extension, the extension doesn't exist, or was not re-enabled.
556 // If the profile doesn't exist, it may have been deleted during the enable
557 // prompt. In this case, NOTIFICATION_PROFILE_DESTROYED may not be fired
558 // until later, so respond to the host now.
559 Host
* host
= FindHost(profile
, app_id
);
561 host
->OnAppLaunchComplete(APP_SHIM_LAUNCH_APP_NOT_FOUND
);
565 delegate_
->LaunchApp(profile
, extension
, files
);
569 void ExtensionAppShimHandler::OnShimClose(Host
* host
) {
570 // This might be called when shutting down. Don't try to look up the profile
571 // since profile_manager might not be around.
572 for (HostMap::iterator it
= hosts_
.begin(); it
!= hosts_
.end(); ) {
573 HostMap::iterator current
= it
++;
574 if (current
->second
== host
)
575 hosts_
.erase(current
);
579 void ExtensionAppShimHandler::OnShimFocus(
581 AppShimFocusType focus_type
,
582 const std::vector
<base::FilePath
>& files
) {
584 const Extension
* extension
= MaybeGetExtensionOrCloseHost(host
, &profile
);
588 bool windows_focused
;
589 const std::string
& app_id
= host
->GetAppId();
590 if (extension
->is_hosted_app()) {
591 AppBrowserMap::iterator it
= app_browser_windows_
.find(app_id
);
592 if (it
== app_browser_windows_
.end())
595 windows_focused
= FocusHostedAppWindows(it
->second
);
597 const AppWindowList windows
=
598 delegate_
->GetWindows(profile
, host
->GetAppId());
599 windows_focused
= FocusWindows(windows
);
602 if (focus_type
== APP_SHIM_FOCUS_NORMAL
||
603 (focus_type
== APP_SHIM_FOCUS_REOPEN
&& windows_focused
)) {
607 delegate_
->LaunchApp(profile
, extension
, files
);
610 void ExtensionAppShimHandler::OnShimSetHidden(Host
* host
, bool hidden
) {
612 const Extension
* extension
= MaybeGetExtensionOrCloseHost(host
, &profile
);
616 if (extension
->is_hosted_app())
617 SetHostedAppHidden(profile
, host
->GetAppId(), hidden
);
619 SetAppHidden(profile
, host
->GetAppId(), hidden
);
622 void ExtensionAppShimHandler::OnShimQuit(Host
* host
) {
623 DCHECK(delegate_
->ProfileExistsForPath(host
->GetProfilePath()));
624 Profile
* profile
= delegate_
->ProfileForPath(host
->GetProfilePath());
626 const std::string
& app_id
= host
->GetAppId();
627 const Extension
* extension
= delegate_
->MaybeGetAppExtension(profile
, app_id
);
631 if (extension
->is_hosted_app())
632 CloseBrowsersForApp(app_id
);
634 const AppWindowList windows
= delegate_
->GetWindows(profile
, app_id
);
635 for (AppWindowRegistry::const_iterator it
= windows
.begin();
636 it
!= windows
.end(); ++it
) {
637 (*it
)->GetBaseWindow()->Close();
640 // Once the last window closes, flow will end up in OnAppDeactivated via
641 // AppLifetimeMonitor.
642 // Otherwise, once the last window closes for a hosted app, OnBrowserRemoved
643 // will call OnAppDeactivated.
646 void ExtensionAppShimHandler::set_delegate(Delegate
* delegate
) {
647 delegate_
.reset(delegate
);
650 void ExtensionAppShimHandler::Observe(
652 const content::NotificationSource
& source
,
653 const content::NotificationDetails
& details
) {
655 case chrome::NOTIFICATION_PROFILE_CREATED
: {
656 Profile
* profile
= content::Source
<Profile
>(source
).ptr();
657 if (profile
->IsOffTheRecord())
660 AppLifetimeMonitorFactory::GetForProfile(profile
)->AddObserver(this);
663 case chrome::NOTIFICATION_PROFILE_DESTROYED
: {
664 Profile
* profile
= content::Source
<Profile
>(source
).ptr();
665 if (profile
->IsOffTheRecord())
668 AppLifetimeMonitorFactory::GetForProfile(profile
)->RemoveObserver(this);
669 // Shut down every shim associated with this profile.
670 for (HostMap::iterator it
= hosts_
.begin(); it
!= hosts_
.end(); ) {
671 // Increment the iterator first as OnAppClosed may call back to
672 // OnShimClose and invalidate the iterator.
673 HostMap::iterator current
= it
++;
674 if (profile
->IsSameProfile(current
->first
.first
)) {
675 Host
* host
= current
->second
;
681 case chrome::NOTIFICATION_BROWSER_WINDOW_READY
: {
682 Browser
* browser
= content::Source
<Browser
>(source
).ptr();
683 // Don't keep track of browsers that are not associated with an app.
684 const Extension
* extension
= MaybeGetAppForBrowser(browser
);
688 BrowserSet
& browsers
= app_browser_windows_
[extension
->id()];
689 browsers
.insert(browser
);
690 if (browsers
.size() == 1)
691 OnAppActivated(browser
->profile(), extension
->id());
696 NOTREACHED(); // Unexpected notification.
702 void ExtensionAppShimHandler::OnAppStart(Profile
* profile
,
703 const std::string
& app_id
) {}
705 void ExtensionAppShimHandler::OnAppActivated(Profile
* profile
,
706 const std::string
& app_id
) {
707 const Extension
* extension
= delegate_
->MaybeGetAppExtension(profile
, app_id
);
711 Host
* host
= FindHost(profile
, app_id
);
713 host
->OnAppLaunchComplete(APP_SHIM_LAUNCH_SUCCESS
);
714 OnShimFocus(host
, APP_SHIM_FOCUS_NORMAL
, std::vector
<base::FilePath
>());
718 delegate_
->LaunchShim(profile
, extension
);
721 void ExtensionAppShimHandler::OnAppDeactivated(Profile
* profile
,
722 const std::string
& app_id
) {
723 Host
* host
= FindHost(profile
, app_id
);
728 delegate_
->MaybeTerminate();
731 void ExtensionAppShimHandler::OnAppStop(Profile
* profile
,
732 const std::string
& app_id
) {}
734 void ExtensionAppShimHandler::OnChromeTerminating() {}
736 // The BrowserWindow may be NULL when this is called.
737 // Therefore we listen for the notification
738 // chrome::NOTIFICATION_BROWSER_WINDOW_READY and then call OnAppActivated.
739 // If this notification is removed, check that OnBrowserAdded is called after
740 // the BrowserWindow is ready.
741 void ExtensionAppShimHandler::OnBrowserAdded(Browser
* browser
) {
744 void ExtensionAppShimHandler::OnBrowserRemoved(Browser
* browser
) {
745 const Extension
* extension
= MaybeGetAppForBrowser(browser
);
749 AppBrowserMap::iterator it
= app_browser_windows_
.find(extension
->id());
750 if (it
!= app_browser_windows_
.end()) {
751 BrowserSet
& browsers
= it
->second
;
752 browsers
.erase(browser
);
753 if (browsers
.empty())
754 OnAppDeactivated(browser
->profile(), extension
->id());