| OLD | NEW |
| (Empty) |
| 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. | |
| 4 | |
| 5 #include "apps/app_shim/extension_app_shim_handler_mac.h" | |
| 6 | |
| 7 #include "apps/app_lifetime_monitor_factory.h" | |
| 8 #include "apps/app_shim/app_shim_host_manager_mac.h" | |
| 9 #include "apps/app_shim/app_shim_messages.h" | |
| 10 #include "apps/launcher.h" | |
| 11 #include "base/files/file_path.h" | |
| 12 #include "base/logging.h" | |
| 13 #include "chrome/browser/browser_process.h" | |
| 14 #include "chrome/browser/chrome_notification_types.h" | |
| 15 #include "chrome/browser/profiles/profile.h" | |
| 16 #include "chrome/browser/profiles/profile_manager.h" | |
| 17 #include "chrome/browser/ui/extensions/extension_enable_flow.h" | |
| 18 #include "chrome/browser/ui/extensions/extension_enable_flow_delegate.h" | |
| 19 #include "chrome/browser/ui/webui/ntp/core_app_launcher_handler.h" | |
| 20 #include "chrome/browser/web_applications/web_app_mac.h" | |
| 21 #include "chrome/common/extensions/extension_constants.h" | |
| 22 #include "components/crx_file/id_util.h" | |
| 23 #include "content/public/browser/notification_details.h" | |
| 24 #include "content/public/browser/notification_service.h" | |
| 25 #include "content/public/browser/notification_source.h" | |
| 26 #include "extensions/browser/app_window/app_window.h" | |
| 27 #include "extensions/browser/app_window/app_window_registry.h" | |
| 28 #include "extensions/browser/app_window/native_app_window.h" | |
| 29 #include "extensions/browser/extension_host.h" | |
| 30 #include "extensions/browser/extension_registry.h" | |
| 31 #include "ui/base/cocoa/focus_window_set.h" | |
| 32 | |
| 33 using extensions::AppWindow; | |
| 34 using extensions::AppWindowRegistry; | |
| 35 using extensions::ExtensionRegistry; | |
| 36 | |
| 37 namespace { | |
| 38 | |
| 39 typedef AppWindowRegistry::AppWindowList AppWindowList; | |
| 40 | |
| 41 void ProfileLoadedCallback(base::Callback<void(Profile*)> callback, | |
| 42 Profile* profile, | |
| 43 Profile::CreateStatus status) { | |
| 44 if (status == Profile::CREATE_STATUS_INITIALIZED) { | |
| 45 callback.Run(profile); | |
| 46 return; | |
| 47 } | |
| 48 | |
| 49 // This should never get an error since it only loads existing profiles. | |
| 50 DCHECK_EQ(Profile::CREATE_STATUS_CREATED, status); | |
| 51 } | |
| 52 | |
| 53 void SetAppHidden(Profile* profile, const std::string& app_id, bool hidden) { | |
| 54 AppWindowList windows = | |
| 55 AppWindowRegistry::Get(profile)->GetAppWindowsForApp(app_id); | |
| 56 for (AppWindowList::const_reverse_iterator it = windows.rbegin(); | |
| 57 it != windows.rend(); | |
| 58 ++it) { | |
| 59 if (hidden) | |
| 60 (*it)->GetBaseWindow()->HideWithApp(); | |
| 61 else | |
| 62 (*it)->GetBaseWindow()->ShowWithApp(); | |
| 63 } | |
| 64 } | |
| 65 | |
| 66 bool FocusWindows(const AppWindowList& windows) { | |
| 67 if (windows.empty()) | |
| 68 return false; | |
| 69 | |
| 70 std::set<gfx::NativeWindow> native_windows; | |
| 71 for (AppWindowList::const_iterator it = windows.begin(); it != windows.end(); | |
| 72 ++it) { | |
| 73 native_windows.insert((*it)->GetNativeWindow()); | |
| 74 } | |
| 75 // Allow workspace switching. For the browser process, we can reasonably rely | |
| 76 // on OS X to switch spaces for us and honor relevant user settings. But shims | |
| 77 // don't have windows, so we have to do it ourselves. | |
| 78 ui::FocusWindowSet(native_windows); | |
| 79 return true; | |
| 80 } | |
| 81 | |
| 82 // Attempts to launch a packaged app, prompting the user to enable it if | |
| 83 // necessary. The prompt is shown in its own window. | |
| 84 // This class manages its own lifetime. | |
| 85 class EnableViaPrompt : public ExtensionEnableFlowDelegate { | |
| 86 public: | |
| 87 EnableViaPrompt(Profile* profile, | |
| 88 const std::string& extension_id, | |
| 89 const base::Callback<void()>& callback) | |
| 90 : profile_(profile), | |
| 91 extension_id_(extension_id), | |
| 92 callback_(callback) { | |
| 93 } | |
| 94 | |
| 95 virtual ~EnableViaPrompt() { | |
| 96 } | |
| 97 | |
| 98 void Run() { | |
| 99 flow_.reset(new ExtensionEnableFlow(profile_, extension_id_, this)); | |
| 100 flow_->StartForCurrentlyNonexistentWindow( | |
| 101 base::Callback<gfx::NativeWindow(void)>()); | |
| 102 } | |
| 103 | |
| 104 private: | |
| 105 // ExtensionEnableFlowDelegate overrides. | |
| 106 virtual void ExtensionEnableFlowFinished() OVERRIDE { | |
| 107 callback_.Run(); | |
| 108 delete this; | |
| 109 } | |
| 110 | |
| 111 virtual void ExtensionEnableFlowAborted(bool user_initiated) OVERRIDE { | |
| 112 callback_.Run(); | |
| 113 delete this; | |
| 114 } | |
| 115 | |
| 116 Profile* profile_; | |
| 117 std::string extension_id_; | |
| 118 base::Callback<void()> callback_; | |
| 119 scoped_ptr<ExtensionEnableFlow> flow_; | |
| 120 | |
| 121 DISALLOW_COPY_AND_ASSIGN(EnableViaPrompt); | |
| 122 }; | |
| 123 | |
| 124 } // namespace | |
| 125 | |
| 126 namespace apps { | |
| 127 | |
| 128 bool ExtensionAppShimHandler::Delegate::ProfileExistsForPath( | |
| 129 const base::FilePath& path) { | |
| 130 ProfileManager* profile_manager = g_browser_process->profile_manager(); | |
| 131 // Check for the profile name in the profile info cache to ensure that we | |
| 132 // never access any directory that isn't a known profile. | |
| 133 base::FilePath full_path = profile_manager->user_data_dir().Append(path); | |
| 134 ProfileInfoCache& cache = profile_manager->GetProfileInfoCache(); | |
| 135 return cache.GetIndexOfProfileWithPath(full_path) != std::string::npos; | |
| 136 } | |
| 137 | |
| 138 Profile* ExtensionAppShimHandler::Delegate::ProfileForPath( | |
| 139 const base::FilePath& path) { | |
| 140 ProfileManager* profile_manager = g_browser_process->profile_manager(); | |
| 141 base::FilePath full_path = profile_manager->user_data_dir().Append(path); | |
| 142 Profile* profile = profile_manager->GetProfileByPath(full_path); | |
| 143 | |
| 144 // Use IsValidProfile to check if the profile has been created. | |
| 145 return profile && profile_manager->IsValidProfile(profile) ? profile : NULL; | |
| 146 } | |
| 147 | |
| 148 void ExtensionAppShimHandler::Delegate::LoadProfileAsync( | |
| 149 const base::FilePath& path, | |
| 150 base::Callback<void(Profile*)> callback) { | |
| 151 ProfileManager* profile_manager = g_browser_process->profile_manager(); | |
| 152 base::FilePath full_path = profile_manager->user_data_dir().Append(path); | |
| 153 profile_manager->CreateProfileAsync( | |
| 154 full_path, | |
| 155 base::Bind(&ProfileLoadedCallback, callback), | |
| 156 base::string16(), base::string16(), std::string()); | |
| 157 } | |
| 158 | |
| 159 AppWindowList ExtensionAppShimHandler::Delegate::GetWindows( | |
| 160 Profile* profile, | |
| 161 const std::string& extension_id) { | |
| 162 return AppWindowRegistry::Get(profile)->GetAppWindowsForApp(extension_id); | |
| 163 } | |
| 164 | |
| 165 const extensions::Extension* | |
| 166 ExtensionAppShimHandler::Delegate::GetAppExtension( | |
| 167 Profile* profile, | |
| 168 const std::string& extension_id) { | |
| 169 ExtensionRegistry* registry = ExtensionRegistry::Get(profile); | |
| 170 const extensions::Extension* extension = | |
| 171 registry->GetExtensionById(extension_id, ExtensionRegistry::ENABLED); | |
| 172 return extension && extension->is_platform_app() ? extension : NULL; | |
| 173 } | |
| 174 | |
| 175 void ExtensionAppShimHandler::Delegate::EnableExtension( | |
| 176 Profile* profile, | |
| 177 const std::string& extension_id, | |
| 178 const base::Callback<void()>& callback) { | |
| 179 (new EnableViaPrompt(profile, extension_id, callback))->Run(); | |
| 180 } | |
| 181 | |
| 182 void ExtensionAppShimHandler::Delegate::LaunchApp( | |
| 183 Profile* profile, | |
| 184 const extensions::Extension* extension, | |
| 185 const std::vector<base::FilePath>& files) { | |
| 186 CoreAppLauncherHandler::RecordAppLaunchType( | |
| 187 extension_misc::APP_LAUNCH_CMD_LINE_APP, extension->GetType()); | |
| 188 if (files.empty()) { | |
| 189 apps::LaunchPlatformApp(profile, extension); | |
| 190 } else { | |
| 191 for (std::vector<base::FilePath>::const_iterator it = files.begin(); | |
| 192 it != files.end(); ++it) { | |
| 193 apps::LaunchPlatformAppWithPath(profile, extension, *it); | |
| 194 } | |
| 195 } | |
| 196 } | |
| 197 | |
| 198 void ExtensionAppShimHandler::Delegate::LaunchShim( | |
| 199 Profile* profile, | |
| 200 const extensions::Extension* extension) { | |
| 201 web_app::MaybeLaunchShortcut( | |
| 202 web_app::ShortcutInfoForExtensionAndProfile(extension, profile)); | |
| 203 } | |
| 204 | |
| 205 void ExtensionAppShimHandler::Delegate::MaybeTerminate() { | |
| 206 AppShimHandler::MaybeTerminate(); | |
| 207 } | |
| 208 | |
| 209 ExtensionAppShimHandler::ExtensionAppShimHandler() | |
| 210 : delegate_(new Delegate), | |
| 211 weak_factory_(this) { | |
| 212 // This is instantiated in BrowserProcessImpl::PreMainMessageLoopRun with | |
| 213 // AppShimHostManager. Since PROFILE_CREATED is not fired until | |
| 214 // ProfileManager::GetLastUsedProfile/GetLastOpenedProfiles, this should catch | |
| 215 // notifications for all profiles. | |
| 216 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CREATED, | |
| 217 content::NotificationService::AllBrowserContextsAndSources()); | |
| 218 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED, | |
| 219 content::NotificationService::AllBrowserContextsAndSources()); | |
| 220 } | |
| 221 | |
| 222 ExtensionAppShimHandler::~ExtensionAppShimHandler() {} | |
| 223 | |
| 224 AppShimHandler::Host* ExtensionAppShimHandler::FindHost( | |
| 225 Profile* profile, | |
| 226 const std::string& app_id) { | |
| 227 HostMap::iterator it = hosts_.find(make_pair(profile, app_id)); | |
| 228 return it == hosts_.end() ? NULL : it->second; | |
| 229 } | |
| 230 | |
| 231 // static | |
| 232 void ExtensionAppShimHandler::QuitAppForWindow(AppWindow* app_window) { | |
| 233 ExtensionAppShimHandler* handler = GetInstance(); | |
| 234 Host* host = handler->FindHost( | |
| 235 Profile::FromBrowserContext(app_window->browser_context()), | |
| 236 app_window->extension_id()); | |
| 237 if (host) { | |
| 238 handler->OnShimQuit(host); | |
| 239 } else { | |
| 240 // App shims might be disabled or the shim is still starting up. | |
| 241 AppWindowRegistry::Get( | |
| 242 Profile::FromBrowserContext(app_window->browser_context())) | |
| 243 ->CloseAllAppWindowsForApp(app_window->extension_id()); | |
| 244 } | |
| 245 } | |
| 246 | |
| 247 void ExtensionAppShimHandler::HideAppForWindow(AppWindow* app_window) { | |
| 248 ExtensionAppShimHandler* handler = GetInstance(); | |
| 249 Profile* profile = Profile::FromBrowserContext(app_window->browser_context()); | |
| 250 Host* host = handler->FindHost(profile, app_window->extension_id()); | |
| 251 if (host) | |
| 252 host->OnAppHide(); | |
| 253 else | |
| 254 SetAppHidden(profile, app_window->extension_id(), true); | |
| 255 } | |
| 256 | |
| 257 void ExtensionAppShimHandler::FocusAppForWindow(AppWindow* app_window) { | |
| 258 ExtensionAppShimHandler* handler = GetInstance(); | |
| 259 Profile* profile = Profile::FromBrowserContext(app_window->browser_context()); | |
| 260 const std::string& app_id = app_window->extension_id(); | |
| 261 Host* host = handler->FindHost(profile, app_id); | |
| 262 if (host) { | |
| 263 handler->OnShimFocus(host, | |
| 264 APP_SHIM_FOCUS_NORMAL, | |
| 265 std::vector<base::FilePath>()); | |
| 266 } else { | |
| 267 FocusWindows(AppWindowRegistry::Get(profile)->GetAppWindowsForApp(app_id)); | |
| 268 } | |
| 269 } | |
| 270 | |
| 271 // static | |
| 272 bool ExtensionAppShimHandler::ActivateAndRequestUserAttentionForWindow( | |
| 273 AppWindow* app_window) { | |
| 274 ExtensionAppShimHandler* handler = GetInstance(); | |
| 275 Profile* profile = Profile::FromBrowserContext(app_window->browser_context()); | |
| 276 Host* host = handler->FindHost(profile, app_window->extension_id()); | |
| 277 if (host) { | |
| 278 // Bring the window to the front without showing it. | |
| 279 AppWindowRegistry::Get(profile)->AppWindowActivated(app_window); | |
| 280 host->OnAppRequestUserAttention(APP_SHIM_ATTENTION_INFORMATIONAL); | |
| 281 return true; | |
| 282 } else { | |
| 283 // Just show the app. | |
| 284 SetAppHidden(profile, app_window->extension_id(), false); | |
| 285 return false; | |
| 286 } | |
| 287 } | |
| 288 | |
| 289 // static | |
| 290 void ExtensionAppShimHandler::RequestUserAttentionForWindow( | |
| 291 AppWindow* app_window, | |
| 292 AppShimAttentionType attention_type) { | |
| 293 ExtensionAppShimHandler* handler = GetInstance(); | |
| 294 Profile* profile = Profile::FromBrowserContext(app_window->browser_context()); | |
| 295 Host* host = handler->FindHost(profile, app_window->extension_id()); | |
| 296 if (host) | |
| 297 host->OnAppRequestUserAttention(attention_type); | |
| 298 } | |
| 299 | |
| 300 // static | |
| 301 void ExtensionAppShimHandler::OnChromeWillHide() { | |
| 302 // Send OnAppHide to all the shims so that they go into the hidden state. | |
| 303 // This is necessary so that when the shim is next focused, it will know to | |
| 304 // unhide. | |
| 305 ExtensionAppShimHandler* handler = GetInstance(); | |
| 306 for (HostMap::iterator it = handler->hosts_.begin(); | |
| 307 it != handler->hosts_.end(); | |
| 308 ++it) { | |
| 309 it->second->OnAppHide(); | |
| 310 } | |
| 311 } | |
| 312 | |
| 313 void ExtensionAppShimHandler::OnShimLaunch( | |
| 314 Host* host, | |
| 315 AppShimLaunchType launch_type, | |
| 316 const std::vector<base::FilePath>& files) { | |
| 317 const std::string& app_id = host->GetAppId(); | |
| 318 DCHECK(crx_file::id_util::IdIsValid(app_id)); | |
| 319 | |
| 320 const base::FilePath& profile_path = host->GetProfilePath(); | |
| 321 DCHECK(!profile_path.empty()); | |
| 322 | |
| 323 if (!delegate_->ProfileExistsForPath(profile_path)) { | |
| 324 // User may have deleted the profile this shim was originally created for. | |
| 325 // TODO(jackhou): Add some UI for this case and remove the LOG. | |
| 326 LOG(ERROR) << "Requested directory is not a known profile '" | |
| 327 << profile_path.value() << "'."; | |
| 328 host->OnAppLaunchComplete(APP_SHIM_LAUNCH_PROFILE_NOT_FOUND); | |
| 329 return; | |
| 330 } | |
| 331 | |
| 332 Profile* profile = delegate_->ProfileForPath(profile_path); | |
| 333 | |
| 334 if (profile) { | |
| 335 OnProfileLoaded(host, launch_type, files, profile); | |
| 336 return; | |
| 337 } | |
| 338 | |
| 339 // If the profile is not loaded, this must have been a launch by the shim. | |
| 340 // Load the profile asynchronously, the host will be registered in | |
| 341 // OnProfileLoaded. | |
| 342 DCHECK_EQ(APP_SHIM_LAUNCH_NORMAL, launch_type); | |
| 343 delegate_->LoadProfileAsync( | |
| 344 profile_path, | |
| 345 base::Bind(&ExtensionAppShimHandler::OnProfileLoaded, | |
| 346 weak_factory_.GetWeakPtr(), | |
| 347 host, launch_type, files)); | |
| 348 | |
| 349 // Return now. OnAppLaunchComplete will be called when the app is activated. | |
| 350 } | |
| 351 | |
| 352 // static | |
| 353 ExtensionAppShimHandler* ExtensionAppShimHandler::GetInstance() { | |
| 354 return g_browser_process->platform_part() | |
| 355 ->app_shim_host_manager() | |
| 356 ->extension_app_shim_handler(); | |
| 357 } | |
| 358 | |
| 359 void ExtensionAppShimHandler::OnProfileLoaded( | |
| 360 Host* host, | |
| 361 AppShimLaunchType launch_type, | |
| 362 const std::vector<base::FilePath>& files, | |
| 363 Profile* profile) { | |
| 364 const std::string& app_id = host->GetAppId(); | |
| 365 | |
| 366 // The first host to claim this (profile, app_id) becomes the main host. | |
| 367 // For any others, focus or relaunch the app. | |
| 368 if (!hosts_.insert(make_pair(make_pair(profile, app_id), host)).second) { | |
| 369 OnShimFocus(host, | |
| 370 launch_type == APP_SHIM_LAUNCH_NORMAL ? | |
| 371 APP_SHIM_FOCUS_REOPEN : APP_SHIM_FOCUS_NORMAL, | |
| 372 files); | |
| 373 host->OnAppLaunchComplete(APP_SHIM_LAUNCH_DUPLICATE_HOST); | |
| 374 return; | |
| 375 } | |
| 376 | |
| 377 if (launch_type != APP_SHIM_LAUNCH_NORMAL) { | |
| 378 host->OnAppLaunchComplete(APP_SHIM_LAUNCH_SUCCESS); | |
| 379 return; | |
| 380 } | |
| 381 | |
| 382 // TODO(jeremya): Handle the case that launching the app fails. Probably we | |
| 383 // need to watch for 'app successfully launched' or at least 'background page | |
| 384 // exists/was created' and time out with failure if we don't see that sign of | |
| 385 // life within a certain window. | |
| 386 const extensions::Extension* extension = | |
| 387 delegate_->GetAppExtension(profile, app_id); | |
| 388 if (extension) { | |
| 389 delegate_->LaunchApp(profile, extension, files); | |
| 390 return; | |
| 391 } | |
| 392 | |
| 393 delegate_->EnableExtension( | |
| 394 profile, app_id, | |
| 395 base::Bind(&ExtensionAppShimHandler::OnExtensionEnabled, | |
| 396 weak_factory_.GetWeakPtr(), | |
| 397 host->GetProfilePath(), app_id, files)); | |
| 398 } | |
| 399 | |
| 400 void ExtensionAppShimHandler::OnExtensionEnabled( | |
| 401 const base::FilePath& profile_path, | |
| 402 const std::string& app_id, | |
| 403 const std::vector<base::FilePath>& files) { | |
| 404 Profile* profile = delegate_->ProfileForPath(profile_path); | |
| 405 if (!profile) | |
| 406 return; | |
| 407 | |
| 408 const extensions::Extension* extension = | |
| 409 delegate_->GetAppExtension(profile, app_id); | |
| 410 if (!extension || !delegate_->ProfileExistsForPath(profile_path)) { | |
| 411 // If !extension, the extension doesn't exist, or was not re-enabled. | |
| 412 // If the profile doesn't exist, it may have been deleted during the enable | |
| 413 // prompt. In this case, NOTIFICATION_PROFILE_DESTROYED may not be fired | |
| 414 // until later, so respond to the host now. | |
| 415 Host* host = FindHost(profile, app_id); | |
| 416 if (host) | |
| 417 host->OnAppLaunchComplete(APP_SHIM_LAUNCH_APP_NOT_FOUND); | |
| 418 return; | |
| 419 } | |
| 420 | |
| 421 delegate_->LaunchApp(profile, extension, files); | |
| 422 } | |
| 423 | |
| 424 | |
| 425 void ExtensionAppShimHandler::OnShimClose(Host* host) { | |
| 426 // This might be called when shutting down. Don't try to look up the profile | |
| 427 // since profile_manager might not be around. | |
| 428 for (HostMap::iterator it = hosts_.begin(); it != hosts_.end(); ) { | |
| 429 HostMap::iterator current = it++; | |
| 430 if (current->second == host) | |
| 431 hosts_.erase(current); | |
| 432 } | |
| 433 } | |
| 434 | |
| 435 void ExtensionAppShimHandler::OnShimFocus( | |
| 436 Host* host, | |
| 437 AppShimFocusType focus_type, | |
| 438 const std::vector<base::FilePath>& files) { | |
| 439 DCHECK(delegate_->ProfileExistsForPath(host->GetProfilePath())); | |
| 440 Profile* profile = delegate_->ProfileForPath(host->GetProfilePath()); | |
| 441 | |
| 442 const AppWindowList windows = | |
| 443 delegate_->GetWindows(profile, host->GetAppId()); | |
| 444 bool windows_focused = FocusWindows(windows); | |
| 445 | |
| 446 if (focus_type == APP_SHIM_FOCUS_NORMAL || | |
| 447 (focus_type == APP_SHIM_FOCUS_REOPEN && windows_focused)) { | |
| 448 return; | |
| 449 } | |
| 450 | |
| 451 const extensions::Extension* extension = | |
| 452 delegate_->GetAppExtension(profile, host->GetAppId()); | |
| 453 if (extension) { | |
| 454 delegate_->LaunchApp(profile, extension, files); | |
| 455 } else { | |
| 456 // Extensions may have been uninstalled or disabled since the shim | |
| 457 // started. | |
| 458 host->OnAppClosed(); | |
| 459 } | |
| 460 } | |
| 461 | |
| 462 void ExtensionAppShimHandler::OnShimSetHidden(Host* host, bool hidden) { | |
| 463 DCHECK(delegate_->ProfileExistsForPath(host->GetProfilePath())); | |
| 464 Profile* profile = delegate_->ProfileForPath(host->GetProfilePath()); | |
| 465 | |
| 466 SetAppHidden(profile, host->GetAppId(), hidden); | |
| 467 } | |
| 468 | |
| 469 void ExtensionAppShimHandler::OnShimQuit(Host* host) { | |
| 470 DCHECK(delegate_->ProfileExistsForPath(host->GetProfilePath())); | |
| 471 Profile* profile = delegate_->ProfileForPath(host->GetProfilePath()); | |
| 472 | |
| 473 const std::string& app_id = host->GetAppId(); | |
| 474 const AppWindowList windows = delegate_->GetWindows(profile, app_id); | |
| 475 for (AppWindowRegistry::const_iterator it = windows.begin(); | |
| 476 it != windows.end(); | |
| 477 ++it) { | |
| 478 (*it)->GetBaseWindow()->Close(); | |
| 479 } | |
| 480 // Once the last window closes, flow will end up in OnAppDeactivated via | |
| 481 // AppLifetimeMonitor. | |
| 482 } | |
| 483 | |
| 484 void ExtensionAppShimHandler::set_delegate(Delegate* delegate) { | |
| 485 delegate_.reset(delegate); | |
| 486 } | |
| 487 | |
| 488 void ExtensionAppShimHandler::Observe( | |
| 489 int type, | |
| 490 const content::NotificationSource& source, | |
| 491 const content::NotificationDetails& details) { | |
| 492 Profile* profile = content::Source<Profile>(source).ptr(); | |
| 493 if (profile->IsOffTheRecord()) | |
| 494 return; | |
| 495 | |
| 496 switch (type) { | |
| 497 case chrome::NOTIFICATION_PROFILE_CREATED: { | |
| 498 AppLifetimeMonitorFactory::GetForProfile(profile)->AddObserver(this); | |
| 499 break; | |
| 500 } | |
| 501 case chrome::NOTIFICATION_PROFILE_DESTROYED: { | |
| 502 AppLifetimeMonitorFactory::GetForProfile(profile)->RemoveObserver(this); | |
| 503 // Shut down every shim associated with this profile. | |
| 504 for (HostMap::iterator it = hosts_.begin(); it != hosts_.end(); ) { | |
| 505 // Increment the iterator first as OnAppClosed may call back to | |
| 506 // OnShimClose and invalidate the iterator. | |
| 507 HostMap::iterator current = it++; | |
| 508 if (profile->IsSameProfile(current->first.first)) { | |
| 509 Host* host = current->second; | |
| 510 host->OnAppClosed(); | |
| 511 } | |
| 512 } | |
| 513 break; | |
| 514 } | |
| 515 default: { | |
| 516 NOTREACHED(); // Unexpected notification. | |
| 517 break; | |
| 518 } | |
| 519 } | |
| 520 } | |
| 521 | |
| 522 void ExtensionAppShimHandler::OnAppStart(Profile* profile, | |
| 523 const std::string& app_id) {} | |
| 524 | |
| 525 void ExtensionAppShimHandler::OnAppActivated(Profile* profile, | |
| 526 const std::string& app_id) { | |
| 527 const extensions::Extension* extension = | |
| 528 delegate_->GetAppExtension(profile, app_id); | |
| 529 if (!extension) | |
| 530 return; | |
| 531 | |
| 532 Host* host = FindHost(profile, app_id); | |
| 533 if (host) { | |
| 534 host->OnAppLaunchComplete(APP_SHIM_LAUNCH_SUCCESS); | |
| 535 OnShimFocus(host, APP_SHIM_FOCUS_NORMAL, std::vector<base::FilePath>()); | |
| 536 return; | |
| 537 } | |
| 538 | |
| 539 delegate_->LaunchShim(profile, extension); | |
| 540 } | |
| 541 | |
| 542 void ExtensionAppShimHandler::OnAppDeactivated(Profile* profile, | |
| 543 const std::string& app_id) { | |
| 544 Host* host = FindHost(profile, app_id); | |
| 545 if (host) | |
| 546 host->OnAppClosed(); | |
| 547 | |
| 548 if (hosts_.empty()) | |
| 549 delegate_->MaybeTerminate(); | |
| 550 } | |
| 551 | |
| 552 void ExtensionAppShimHandler::OnAppStop(Profile* profile, | |
| 553 const std::string& app_id) {} | |
| 554 | |
| 555 void ExtensionAppShimHandler::OnChromeTerminating() {} | |
| 556 | |
| 557 } // namespace apps | |
| OLD | NEW |