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/ui/browser_list.h"
18 #include "chrome/browser/ui/browser_window.h"
19 #include "chrome/browser/ui/extensions/application_launch.h"
20 #include "chrome/browser/ui/extensions/extension_enable_flow.h"
21 #include "chrome/browser/ui/extensions/extension_enable_flow_delegate.h"
22 #include "chrome/browser/web_applications/web_app_mac.h"
23 #include "chrome/common/extensions/extension_constants.h"
24 #include "chrome/common/extensions/extension_metrics.h"
25 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
26 #include "chrome/common/mac/app_shim_messages.h"
27 #include "components/crx_file/id_util.h"
28 #include "content/public/browser/notification_details.h"
29 #include "content/public/browser/notification_service.h"
30 #include "content/public/browser/notification_source.h"
31 #include "extensions/browser/app_window/app_window.h"
32 #include "extensions/browser/app_window/app_window_registry.h"
33 #include "extensions/browser/app_window/native_app_window.h"
34 #include "extensions/browser/extension_host.h"
35 #include "extensions/browser/extension_prefs.h"
36 #include "extensions/browser/extension_registry.h"
37 #include "extensions/common/constants.h"
38 #include "ui/base/cocoa/focus_window_set.h"
40 using extensions::AppWindow
;
41 using extensions::AppWindowRegistry
;
42 using extensions::ExtensionRegistry
;
46 typedef AppWindowRegistry::AppWindowList AppWindowList
;
48 void ProfileLoadedCallback(base::Callback
<void(Profile
*)> callback
,
50 Profile::CreateStatus status
) {
51 if (status
== Profile::CREATE_STATUS_INITIALIZED
) {
52 callback
.Run(profile
);
56 // This should never get an error since it only loads existing profiles.
57 DCHECK_EQ(Profile::CREATE_STATUS_CREATED
, status
);
60 void SetAppHidden(Profile
* profile
, const std::string
& app_id
, bool hidden
) {
61 AppWindowList windows
=
62 AppWindowRegistry::Get(profile
)->GetAppWindowsForApp(app_id
);
63 for (AppWindowList::const_reverse_iterator it
= windows
.rbegin();
67 (*it
)->GetBaseWindow()->HideWithApp();
69 (*it
)->GetBaseWindow()->ShowWithApp();
73 bool FocusWindows(const AppWindowList
& windows
) {
77 std::set
<gfx::NativeWindow
> native_windows
;
78 for (AppWindowList::const_iterator it
= windows
.begin(); it
!= windows
.end();
80 native_windows
.insert((*it
)->GetNativeWindow());
82 // Allow workspace switching. For the browser process, we can reasonably rely
83 // on OS X to switch spaces for us and honor relevant user settings. But shims
84 // don't have windows, so we have to do it ourselves.
85 ui::FocusWindowSet(native_windows
);
89 bool FocusHostedAppWindows(std::set
<Browser
*>& browsers
) {
93 std::set
<gfx::NativeWindow
> native_windows
;
94 for (const Browser
* browser
: browsers
)
95 native_windows
.insert(browser
->window()->GetNativeWindow());
97 ui::FocusWindowSet(native_windows
);
101 // Attempts to launch a packaged app, prompting the user to enable it if
102 // necessary. The prompt is shown in its own window.
103 // This class manages its own lifetime.
104 class EnableViaPrompt
: public ExtensionEnableFlowDelegate
{
106 EnableViaPrompt(Profile
* profile
,
107 const std::string
& extension_id
,
108 const base::Callback
<void()>& callback
)
110 extension_id_(extension_id
),
111 callback_(callback
) {
114 ~EnableViaPrompt() override
{}
117 flow_
.reset(new ExtensionEnableFlow(profile_
, extension_id_
, this));
118 flow_
->StartForCurrentlyNonexistentWindow(
119 base::Callback
<gfx::NativeWindow(void)>());
123 // ExtensionEnableFlowDelegate overrides.
124 void ExtensionEnableFlowFinished() override
{
129 void ExtensionEnableFlowAborted(bool user_initiated
) override
{
135 std::string extension_id_
;
136 base::Callback
<void()> callback_
;
137 scoped_ptr
<ExtensionEnableFlow
> flow_
;
139 DISALLOW_COPY_AND_ASSIGN(EnableViaPrompt
);
146 bool ExtensionAppShimHandler::Delegate::ProfileExistsForPath(
147 const base::FilePath
& path
) {
148 ProfileManager
* profile_manager
= g_browser_process
->profile_manager();
149 // Check for the profile name in the profile info cache to ensure that we
150 // never access any directory that isn't a known profile.
151 base::FilePath full_path
= profile_manager
->user_data_dir().Append(path
);
152 ProfileInfoCache
& cache
= profile_manager
->GetProfileInfoCache();
153 return cache
.GetIndexOfProfileWithPath(full_path
) != std::string::npos
;
156 Profile
* ExtensionAppShimHandler::Delegate::ProfileForPath(
157 const base::FilePath
& path
) {
158 ProfileManager
* profile_manager
= g_browser_process
->profile_manager();
159 base::FilePath full_path
= profile_manager
->user_data_dir().Append(path
);
160 Profile
* profile
= profile_manager
->GetProfileByPath(full_path
);
162 // Use IsValidProfile to check if the profile has been created.
163 return profile
&& profile_manager
->IsValidProfile(profile
) ? profile
: NULL
;
166 void ExtensionAppShimHandler::Delegate::LoadProfileAsync(
167 const base::FilePath
& path
,
168 base::Callback
<void(Profile
*)> callback
) {
169 ProfileManager
* profile_manager
= g_browser_process
->profile_manager();
170 base::FilePath full_path
= profile_manager
->user_data_dir().Append(path
);
171 profile_manager
->CreateProfileAsync(
173 base::Bind(&ProfileLoadedCallback
, callback
),
174 base::string16(), base::string16(), std::string());
177 AppWindowList
ExtensionAppShimHandler::Delegate::GetWindows(
179 const std::string
& extension_id
) {
180 return AppWindowRegistry::Get(profile
)->GetAppWindowsForApp(extension_id
);
183 const extensions::Extension
*
184 ExtensionAppShimHandler::Delegate::GetAppExtension(
186 const std::string
& extension_id
) {
187 return ExtensionAppShimHandler::GetAppExtension(profile
, extension_id
);
190 void ExtensionAppShimHandler::Delegate::EnableExtension(
192 const std::string
& extension_id
,
193 const base::Callback
<void()>& callback
) {
194 (new EnableViaPrompt(profile
, extension_id
, callback
))->Run();
197 void ExtensionAppShimHandler::Delegate::LaunchApp(
199 const extensions::Extension
* extension
,
200 const std::vector
<base::FilePath
>& files
) {
201 extensions::RecordAppLaunchType(
202 extension_misc::APP_LAUNCH_CMD_LINE_APP
, extension
->GetType());
203 if (extension
->is_hosted_app()) {
204 AppLaunchParams
launch_params(profile
, extension
, NEW_FOREGROUND_TAB
,
205 extensions::SOURCE_COMMAND_LINE
);
206 OpenApplication(launch_params
);
210 apps::LaunchPlatformApp(
211 profile
, extension
, extensions::SOURCE_COMMAND_LINE
);
213 for (std::vector
<base::FilePath
>::const_iterator it
= files
.begin();
214 it
!= files
.end(); ++it
) {
215 apps::LaunchPlatformAppWithPath(profile
, extension
, *it
);
220 void ExtensionAppShimHandler::Delegate::LaunchShim(
222 const extensions::Extension
* extension
) {
223 web_app::MaybeLaunchShortcut(
224 web_app::ShortcutInfoForExtensionAndProfile(extension
, profile
));
227 void ExtensionAppShimHandler::Delegate::MaybeTerminate() {
228 AppShimHandler::MaybeTerminate();
231 ExtensionAppShimHandler::ExtensionAppShimHandler()
232 : delegate_(new Delegate
),
233 weak_factory_(this) {
234 // This is instantiated in BrowserProcessImpl::PreMainMessageLoopRun with
235 // AppShimHostManager. Since PROFILE_CREATED is not fired until
236 // ProfileManager::GetLastUsedProfile/GetLastOpenedProfiles, this should catch
237 // notifications for all profiles.
238 registrar_
.Add(this, chrome::NOTIFICATION_PROFILE_CREATED
,
239 content::NotificationService::AllBrowserContextsAndSources());
240 registrar_
.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED
,
241 content::NotificationService::AllBrowserContextsAndSources());
242 registrar_
.Add(this, chrome::NOTIFICATION_BROWSER_WINDOW_READY
,
243 content::NotificationService::AllBrowserContextsAndSources());
244 BrowserList::AddObserver(this);
247 ExtensionAppShimHandler::~ExtensionAppShimHandler() {
248 BrowserList::RemoveObserver(this);
251 AppShimHandler::Host
* ExtensionAppShimHandler::FindHost(
253 const std::string
& app_id
) {
254 HostMap::iterator it
= hosts_
.find(make_pair(profile
, app_id
));
255 return it
== hosts_
.end() ? NULL
: it
->second
;
258 void ExtensionAppShimHandler::SetHostedAppHidden(Profile
* profile
,
259 const std::string
& app_id
,
261 const AppBrowserMap::iterator it
= app_browser_windows_
.find(app_id
);
262 if (it
== app_browser_windows_
.end())
265 for (const Browser
* browser
: it
->second
) {
266 if (web_app::GetExtensionIdFromApplicationName(browser
->app_name()) !=
272 browser
->window()->Hide();
274 browser
->window()->Show();
279 const extensions::Extension
* ExtensionAppShimHandler::GetAppExtension(
281 const std::string
& extension_id
) {
285 ExtensionRegistry
* registry
= ExtensionRegistry::Get(profile
);
286 const extensions::Extension
* extension
=
287 registry
->GetExtensionById(extension_id
, ExtensionRegistry::ENABLED
);
289 (extension
->is_platform_app() || extension
->is_hosted_app())
295 const extensions::Extension
* ExtensionAppShimHandler::GetAppForBrowser(
297 if (!browser
|| !browser
->is_app())
300 return GetAppExtension(
302 web_app::GetExtensionIdFromApplicationName(browser
->app_name()));
306 void ExtensionAppShimHandler::QuitAppForWindow(AppWindow
* app_window
) {
307 ExtensionAppShimHandler
* handler
= GetInstance();
308 Host
* host
= handler
->FindHost(
309 Profile::FromBrowserContext(app_window
->browser_context()),
310 app_window
->extension_id());
312 handler
->OnShimQuit(host
);
314 // App shims might be disabled or the shim is still starting up.
315 AppWindowRegistry::Get(
316 Profile::FromBrowserContext(app_window
->browser_context()))
317 ->CloseAllAppWindowsForApp(app_window
->extension_id());
322 void ExtensionAppShimHandler::QuitHostedAppForWindow(
324 const std::string
& app_id
) {
325 ExtensionAppShimHandler
* handler
= GetInstance();
326 Host
* host
= handler
->FindHost(Profile::FromBrowserContext(profile
), app_id
);
328 handler
->OnShimQuit(host
);
330 handler
->CloseBrowsersForApp(app_id
);
333 void ExtensionAppShimHandler::HideAppForWindow(AppWindow
* app_window
) {
334 ExtensionAppShimHandler
* handler
= GetInstance();
335 Profile
* profile
= Profile::FromBrowserContext(app_window
->browser_context());
336 Host
* host
= handler
->FindHost(profile
, app_window
->extension_id());
340 SetAppHidden(profile
, app_window
->extension_id(), true);
343 void ExtensionAppShimHandler::HideHostedApp(Profile
* profile
,
344 const std::string
& app_id
) {
345 ExtensionAppShimHandler
* handler
= GetInstance();
346 Host
* host
= handler
->FindHost(profile
, app_id
);
350 handler
->SetHostedAppHidden(profile
, app_id
, true);
353 void ExtensionAppShimHandler::FocusAppForWindow(AppWindow
* app_window
) {
354 ExtensionAppShimHandler
* handler
= GetInstance();
355 Profile
* profile
= Profile::FromBrowserContext(app_window
->browser_context());
356 const std::string
& app_id
= app_window
->extension_id();
357 Host
* host
= handler
->FindHost(profile
, app_id
);
359 handler
->OnShimFocus(host
,
360 APP_SHIM_FOCUS_NORMAL
,
361 std::vector
<base::FilePath
>());
363 FocusWindows(AppWindowRegistry::Get(profile
)->GetAppWindowsForApp(app_id
));
368 bool ExtensionAppShimHandler::ActivateAndRequestUserAttentionForWindow(
369 AppWindow
* app_window
) {
370 ExtensionAppShimHandler
* handler
= GetInstance();
371 Profile
* profile
= Profile::FromBrowserContext(app_window
->browser_context());
372 Host
* host
= handler
->FindHost(profile
, app_window
->extension_id());
374 // Bring the window to the front without showing it.
375 AppWindowRegistry::Get(profile
)->AppWindowActivated(app_window
);
376 host
->OnAppRequestUserAttention(APP_SHIM_ATTENTION_INFORMATIONAL
);
379 // Just show the app.
380 SetAppHidden(profile
, app_window
->extension_id(), false);
386 void ExtensionAppShimHandler::RequestUserAttentionForWindow(
387 AppWindow
* app_window
,
388 AppShimAttentionType attention_type
) {
389 ExtensionAppShimHandler
* handler
= GetInstance();
390 Profile
* profile
= Profile::FromBrowserContext(app_window
->browser_context());
391 Host
* host
= handler
->FindHost(profile
, app_window
->extension_id());
393 host
->OnAppRequestUserAttention(attention_type
);
397 void ExtensionAppShimHandler::OnChromeWillHide() {
398 // Send OnAppHide to all the shims so that they go into the hidden state.
399 // This is necessary so that when the shim is next focused, it will know to
401 ExtensionAppShimHandler
* handler
= GetInstance();
402 for (HostMap::iterator it
= handler
->hosts_
.begin();
403 it
!= handler
->hosts_
.end();
405 it
->second
->OnAppHide();
409 void ExtensionAppShimHandler::OnShimLaunch(
411 AppShimLaunchType launch_type
,
412 const std::vector
<base::FilePath
>& files
) {
413 const std::string
& app_id
= host
->GetAppId();
414 DCHECK(crx_file::id_util::IdIsValid(app_id
));
416 const base::FilePath
& profile_path
= host
->GetProfilePath();
417 DCHECK(!profile_path
.empty());
419 if (!delegate_
->ProfileExistsForPath(profile_path
)) {
420 // User may have deleted the profile this shim was originally created for.
421 // TODO(jackhou): Add some UI for this case and remove the LOG.
422 LOG(ERROR
) << "Requested directory is not a known profile '"
423 << profile_path
.value() << "'.";
424 host
->OnAppLaunchComplete(APP_SHIM_LAUNCH_PROFILE_NOT_FOUND
);
428 Profile
* profile
= delegate_
->ProfileForPath(profile_path
);
431 OnProfileLoaded(host
, launch_type
, files
, profile
);
435 // If the profile is not loaded, this must have been a launch by the shim.
436 // Load the profile asynchronously, the host will be registered in
438 DCHECK_EQ(APP_SHIM_LAUNCH_NORMAL
, launch_type
);
439 delegate_
->LoadProfileAsync(
441 base::Bind(&ExtensionAppShimHandler::OnProfileLoaded
,
442 weak_factory_
.GetWeakPtr(),
443 host
, launch_type
, files
));
445 // Return now. OnAppLaunchComplete will be called when the app is activated.
449 ExtensionAppShimHandler
* ExtensionAppShimHandler::GetInstance() {
450 return g_browser_process
->platform_part()
451 ->app_shim_host_manager()
452 ->extension_app_shim_handler();
455 void ExtensionAppShimHandler::CloseBrowsersForApp(const std::string
& app_id
) {
456 AppBrowserMap::iterator it
= app_browser_windows_
.find(app_id
);
457 if (it
== app_browser_windows_
.end())
460 for (const Browser
* browser
: it
->second
)
461 browser
->window()->Close();
464 void ExtensionAppShimHandler::OnProfileLoaded(
466 AppShimLaunchType launch_type
,
467 const std::vector
<base::FilePath
>& files
,
469 const std::string
& app_id
= host
->GetAppId();
471 // The first host to claim this (profile, app_id) becomes the main host.
472 // For any others, focus or relaunch the app.
473 if (!hosts_
.insert(make_pair(make_pair(profile
, app_id
), host
)).second
) {
475 launch_type
== APP_SHIM_LAUNCH_NORMAL
?
476 APP_SHIM_FOCUS_REOPEN
: APP_SHIM_FOCUS_NORMAL
,
478 host
->OnAppLaunchComplete(APP_SHIM_LAUNCH_DUPLICATE_HOST
);
482 if (launch_type
!= APP_SHIM_LAUNCH_NORMAL
) {
483 host
->OnAppLaunchComplete(APP_SHIM_LAUNCH_SUCCESS
);
487 // TODO(jeremya): Handle the case that launching the app fails. Probably we
488 // need to watch for 'app successfully launched' or at least 'background page
489 // exists/was created' and time out with failure if we don't see that sign of
490 // life within a certain window.
491 const extensions::Extension
* extension
=
492 delegate_
->GetAppExtension(profile
, app_id
);
494 delegate_
->LaunchApp(profile
, extension
, files
);
495 // If it's a hosted app that opens in a tab, let the shim terminate
497 if (extension
->is_hosted_app() &&
498 extensions::GetLaunchType(extensions::ExtensionPrefs::Get(profile
),
500 extensions::LAUNCH_TYPE_REGULAR
) {
501 host
->OnAppLaunchComplete(APP_SHIM_LAUNCH_DUPLICATE_HOST
);
506 delegate_
->EnableExtension(
508 base::Bind(&ExtensionAppShimHandler::OnExtensionEnabled
,
509 weak_factory_
.GetWeakPtr(),
510 host
->GetProfilePath(), app_id
, files
));
513 void ExtensionAppShimHandler::OnExtensionEnabled(
514 const base::FilePath
& profile_path
,
515 const std::string
& app_id
,
516 const std::vector
<base::FilePath
>& files
) {
517 Profile
* profile
= delegate_
->ProfileForPath(profile_path
);
521 const extensions::Extension
* extension
=
522 delegate_
->GetAppExtension(profile
, app_id
);
523 if (!extension
|| !delegate_
->ProfileExistsForPath(profile_path
)) {
524 // If !extension, the extension doesn't exist, or was not re-enabled.
525 // If the profile doesn't exist, it may have been deleted during the enable
526 // prompt. In this case, NOTIFICATION_PROFILE_DESTROYED may not be fired
527 // until later, so respond to the host now.
528 Host
* host
= FindHost(profile
, app_id
);
530 host
->OnAppLaunchComplete(APP_SHIM_LAUNCH_APP_NOT_FOUND
);
534 delegate_
->LaunchApp(profile
, extension
, files
);
538 void ExtensionAppShimHandler::OnShimClose(Host
* host
) {
539 // This might be called when shutting down. Don't try to look up the profile
540 // since profile_manager might not be around.
541 for (HostMap::iterator it
= hosts_
.begin(); it
!= hosts_
.end(); ) {
542 HostMap::iterator current
= it
++;
543 if (current
->second
== host
)
544 hosts_
.erase(current
);
548 void ExtensionAppShimHandler::OnShimFocus(
550 AppShimFocusType focus_type
,
551 const std::vector
<base::FilePath
>& files
) {
552 DCHECK(delegate_
->ProfileExistsForPath(host
->GetProfilePath()));
553 Profile
* profile
= delegate_
->ProfileForPath(host
->GetProfilePath());
555 bool windows_focused
;
556 const std::string
& app_id
= host
->GetAppId();
557 if (delegate_
->GetAppExtension(profile
, app_id
)->is_hosted_app()) {
558 AppBrowserMap::iterator it
= app_browser_windows_
.find(app_id
);
559 if (it
== app_browser_windows_
.end())
562 windows_focused
= FocusHostedAppWindows(it
->second
);
564 const AppWindowList windows
=
565 delegate_
->GetWindows(profile
, host
->GetAppId());
566 windows_focused
= FocusWindows(windows
);
569 if (focus_type
== APP_SHIM_FOCUS_NORMAL
||
570 (focus_type
== APP_SHIM_FOCUS_REOPEN
&& windows_focused
)) {
574 const extensions::Extension
* extension
=
575 delegate_
->GetAppExtension(profile
, host
->GetAppId());
577 delegate_
->LaunchApp(profile
, extension
, files
);
579 // Extensions may have been uninstalled or disabled since the shim
585 void ExtensionAppShimHandler::OnShimSetHidden(Host
* host
, bool hidden
) {
586 DCHECK(delegate_
->ProfileExistsForPath(host
->GetProfilePath()));
587 Profile
* profile
= delegate_
->ProfileForPath(host
->GetProfilePath());
589 const std::string
& app_id
= host
->GetAppId();
590 if (delegate_
->GetAppExtension(profile
, app_id
)->is_hosted_app())
591 SetHostedAppHidden(profile
, app_id
, hidden
);
593 SetAppHidden(profile
, app_id
, hidden
);
596 void ExtensionAppShimHandler::OnShimQuit(Host
* host
) {
597 DCHECK(delegate_
->ProfileExistsForPath(host
->GetProfilePath()));
598 Profile
* profile
= delegate_
->ProfileForPath(host
->GetProfilePath());
600 const std::string
& app_id
= host
->GetAppId();
601 const extensions::Extension
* extension
=
602 delegate_
->GetAppExtension(profile
, app_id
);
606 if (extension
->is_hosted_app())
607 CloseBrowsersForApp(app_id
);
609 const AppWindowList windows
= delegate_
->GetWindows(profile
, app_id
);
610 for (AppWindowRegistry::const_iterator it
= windows
.begin();
611 it
!= windows
.end(); ++it
) {
612 (*it
)->GetBaseWindow()->Close();
615 // Once the last window closes, flow will end up in OnAppDeactivated via
616 // AppLifetimeMonitor.
617 // Otherwise, once the last window closes for a hosted app, OnBrowserRemoved
618 // will call OnAppDeactivated.
621 void ExtensionAppShimHandler::set_delegate(Delegate
* delegate
) {
622 delegate_
.reset(delegate
);
625 void ExtensionAppShimHandler::Observe(
627 const content::NotificationSource
& source
,
628 const content::NotificationDetails
& details
) {
630 case chrome::NOTIFICATION_PROFILE_CREATED
: {
631 Profile
* profile
= content::Source
<Profile
>(source
).ptr();
632 if (profile
->IsOffTheRecord())
635 AppLifetimeMonitorFactory::GetForProfile(profile
)->AddObserver(this);
638 case chrome::NOTIFICATION_PROFILE_DESTROYED
: {
639 Profile
* profile
= content::Source
<Profile
>(source
).ptr();
640 if (profile
->IsOffTheRecord())
643 AppLifetimeMonitorFactory::GetForProfile(profile
)->RemoveObserver(this);
644 // Shut down every shim associated with this profile.
645 for (HostMap::iterator it
= hosts_
.begin(); it
!= hosts_
.end(); ) {
646 // Increment the iterator first as OnAppClosed may call back to
647 // OnShimClose and invalidate the iterator.
648 HostMap::iterator current
= it
++;
649 if (profile
->IsSameProfile(current
->first
.first
)) {
650 Host
* host
= current
->second
;
656 case chrome::NOTIFICATION_BROWSER_WINDOW_READY
: {
657 Browser
* browser
= content::Source
<Browser
>(source
).ptr();
658 // Don't keep track of browsers that are not associated with an app.
659 const extensions::Extension
* extension
= GetAppForBrowser(browser
);
663 BrowserSet
& browsers
= app_browser_windows_
[extension
->id()];
664 browsers
.insert(browser
);
665 if (browsers
.size() == 1)
666 OnAppActivated(browser
->profile(), extension
->id());
671 NOTREACHED(); // Unexpected notification.
677 void ExtensionAppShimHandler::OnAppStart(Profile
* profile
,
678 const std::string
& app_id
) {}
680 void ExtensionAppShimHandler::OnAppActivated(Profile
* profile
,
681 const std::string
& app_id
) {
682 const extensions::Extension
* extension
=
683 delegate_
->GetAppExtension(profile
, app_id
);
687 Host
* host
= FindHost(profile
, app_id
);
689 host
->OnAppLaunchComplete(APP_SHIM_LAUNCH_SUCCESS
);
690 OnShimFocus(host
, APP_SHIM_FOCUS_NORMAL
, std::vector
<base::FilePath
>());
694 delegate_
->LaunchShim(profile
, extension
);
697 void ExtensionAppShimHandler::OnAppDeactivated(Profile
* profile
,
698 const std::string
& app_id
) {
699 Host
* host
= FindHost(profile
, app_id
);
704 delegate_
->MaybeTerminate();
707 void ExtensionAppShimHandler::OnAppStop(Profile
* profile
,
708 const std::string
& app_id
) {}
710 void ExtensionAppShimHandler::OnChromeTerminating() {}
712 // The BrowserWindow may be NULL when this is called.
713 // Therefore we listen for the notification
714 // chrome::NOTIFICATION_BROWSER_WINDOW_READY and then call OnAppActivated.
715 // If this notification is removed, check that OnBrowserAdded is called after
716 // the BrowserWindow is ready.
717 void ExtensionAppShimHandler::OnBrowserAdded(Browser
* browser
) {
720 void ExtensionAppShimHandler::OnBrowserRemoved(Browser
* browser
) {
721 const extensions::Extension
* extension
= GetAppForBrowser(browser
);
725 AppBrowserMap::iterator it
= app_browser_windows_
.find(extension
->id());
726 if (it
!= app_browser_windows_
.end()) {
727 BrowserSet
& browsers
= it
->second
;
728 browsers
.erase(browser
);
729 if (browsers
.empty())
730 OnAppDeactivated(browser
->profile(), extension
->id());