| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "apps/app_shim/extension_app_shim_handler_mac.h" | 5 #include "apps/app_shim/extension_app_shim_handler_mac.h" |
| 6 | 6 |
| 7 #include "apps/app_lifetime_monitor_factory.h" | 7 #include "apps/app_lifetime_monitor_factory.h" |
| 8 #include "apps/app_shim/app_shim_messages.h" | 8 #include "apps/app_shim/app_shim_messages.h" |
| 9 #include "base/files/file_path.h" | 9 #include "base/files/file_path.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "chrome/browser/browser_process.h" | 11 #include "chrome/browser/browser_process.h" |
| 12 #include "chrome/browser/extensions/extension_host.h" | 12 #include "chrome/browser/extensions/extension_host.h" |
| 13 #include "chrome/browser/extensions/extension_service.h" | 13 #include "chrome/browser/extensions/extension_service.h" |
| 14 #include "chrome/browser/extensions/extension_system.h" | 14 #include "chrome/browser/extensions/extension_system.h" |
| 15 #include "chrome/browser/extensions/shell_window_registry.h" | 15 #include "chrome/browser/extensions/shell_window_registry.h" |
| 16 #include "chrome/browser/profiles/profile.h" | 16 #include "chrome/browser/profiles/profile.h" |
| 17 #include "chrome/browser/profiles/profile_manager.h" | 17 #include "chrome/browser/profiles/profile_manager.h" |
| 18 #include "chrome/browser/ui/extensions/application_launch.h" | 18 #include "chrome/browser/ui/extensions/application_launch.h" |
| 19 #include "chrome/browser/ui/extensions/native_app_window.h" | 19 #include "chrome/browser/ui/extensions/native_app_window.h" |
| 20 #include "chrome/browser/ui/extensions/shell_window.h" | 20 #include "chrome/browser/ui/extensions/shell_window.h" |
| 21 #include "chrome/browser/ui/web_applications/web_app_ui.h" | 21 #include "chrome/browser/ui/web_applications/web_app_ui.h" |
| 22 #include "chrome/browser/web_applications/web_app_mac.h" | 22 #include "chrome/browser/web_applications/web_app_mac.h" |
| 23 #include "chrome/common/chrome_notification_types.h" | 23 #include "chrome/common/chrome_notification_types.h" |
| 24 #include "content/public/browser/notification_details.h" | 24 #include "content/public/browser/notification_details.h" |
| 25 #include "content/public/browser/notification_service.h" | 25 #include "content/public/browser/notification_service.h" |
| 26 #include "content/public/browser/notification_source.h" | 26 #include "content/public/browser/notification_source.h" |
| 27 #include "ui/base/cocoa/focus_window_set.h" | 27 #include "ui/base/cocoa/focus_window_set.h" |
| 28 | 28 |
| 29 namespace { | 29 namespace apps { |
| 30 | 30 |
| 31 typedef extensions::ShellWindowRegistry::ShellWindowList ShellWindowList; | 31 typedef extensions::ShellWindowRegistry::ShellWindowList ShellWindowList; |
| 32 | 32 |
| 33 ShellWindowList GetWindows(Profile* profile, const std::string& extension_id) { | 33 bool ExtensionAppShimHandler::Delegate::ProfileExistsForPath( |
| 34 return extensions::ShellWindowRegistry::Get(profile)-> | |
| 35 GetShellWindowsForApp(extension_id); | |
| 36 } | |
| 37 | |
| 38 const extensions::Extension* GetExtension(Profile* profile, | |
| 39 const std::string& extension_id) { | |
| 40 return extensions::ExtensionSystem::Get(profile)->extension_service()-> | |
| 41 GetExtensionById(extension_id, false); | |
| 42 } | |
| 43 | |
| 44 } // namespace | |
| 45 | |
| 46 namespace apps { | |
| 47 | |
| 48 bool ExtensionAppShimHandler::ProfileManagerFacade::ProfileExistsForPath( | |
| 49 const base::FilePath& path) { | 34 const base::FilePath& path) { |
| 50 ProfileManager* profile_manager = g_browser_process->profile_manager(); | 35 ProfileManager* profile_manager = g_browser_process->profile_manager(); |
| 51 // Check for the profile name in the profile info cache to ensure that we | 36 // Check for the profile name in the profile info cache to ensure that we |
| 52 // never access any directory that isn't a known profile. | 37 // never access any directory that isn't a known profile. |
| 53 base::FilePath full_path = profile_manager->user_data_dir().Append(path); | 38 base::FilePath full_path = profile_manager->user_data_dir().Append(path); |
| 54 ProfileInfoCache& cache = profile_manager->GetProfileInfoCache(); | 39 ProfileInfoCache& cache = profile_manager->GetProfileInfoCache(); |
| 55 return cache.GetIndexOfProfileWithPath(full_path) != std::string::npos; | 40 return cache.GetIndexOfProfileWithPath(full_path) != std::string::npos; |
| 56 } | 41 } |
| 57 | 42 |
| 58 Profile* ExtensionAppShimHandler::ProfileManagerFacade::ProfileForPath( | 43 Profile* ExtensionAppShimHandler::Delegate::ProfileForPath( |
| 59 const base::FilePath& path) { | 44 const base::FilePath& path) { |
| 60 ProfileManager* profile_manager = g_browser_process->profile_manager(); | 45 ProfileManager* profile_manager = g_browser_process->profile_manager(); |
| 61 base::FilePath full_path = profile_manager->user_data_dir().Append(path); | 46 base::FilePath full_path = profile_manager->user_data_dir().Append(path); |
| 62 return profile_manager->GetProfile(full_path); | 47 return profile_manager->GetProfile(full_path); |
| 63 } | 48 } |
| 64 | 49 |
| 50 ShellWindowList ExtensionAppShimHandler::Delegate::GetWindows( |
| 51 Profile* profile, |
| 52 const std::string& extension_id) { |
| 53 return extensions::ShellWindowRegistry::Get(profile)-> |
| 54 GetShellWindowsForApp(extension_id); |
| 55 } |
| 56 |
| 57 const extensions::Extension* |
| 58 ExtensionAppShimHandler::Delegate::GetAppExtension( |
| 59 Profile* profile, |
| 60 const std::string& extension_id) { |
| 61 ExtensionService* extension_service = |
| 62 extensions::ExtensionSystem::Get(profile)->extension_service(); |
| 63 DCHECK(extension_service); |
| 64 const extensions::Extension* extension = |
| 65 extension_service->GetExtensionById(extension_id, false); |
| 66 return extension && extension->is_platform_app() ? extension : NULL; |
| 67 } |
| 68 |
| 69 void ExtensionAppShimHandler::Delegate::LaunchApp( |
| 70 Profile* profile, |
| 71 const extensions::Extension* extension) { |
| 72 chrome::OpenApplication( |
| 73 chrome::AppLaunchParams(profile, extension, NEW_FOREGROUND_TAB)); |
| 74 } |
| 75 |
| 76 void ExtensionAppShimHandler::Delegate::LaunchShim( |
| 77 Profile* profile, |
| 78 const extensions::Extension* extension) { |
| 79 web_app::MaybeLaunchShortcut( |
| 80 web_app::ShortcutInfoForExtensionAndProfile(extension, profile)); |
| 81 } |
| 82 |
| 65 ExtensionAppShimHandler::ExtensionAppShimHandler() | 83 ExtensionAppShimHandler::ExtensionAppShimHandler() |
| 66 : profile_manager_facade_(new ProfileManagerFacade) { | 84 : delegate_(new Delegate) { |
| 67 // This is instantiated in BrowserProcessImpl::PreMainMessageLoopRun with | 85 // This is instantiated in BrowserProcessImpl::PreMainMessageLoopRun with |
| 68 // AppShimHostManager. Since PROFILE_CREATED is not fired until | 86 // AppShimHostManager. Since PROFILE_CREATED is not fired until |
| 69 // ProfileManager::GetLastUsedProfile/GetLastOpenedProfiles, this should catch | 87 // ProfileManager::GetLastUsedProfile/GetLastOpenedProfiles, this should catch |
| 70 // notifications for all profiles. | 88 // notifications for all profiles. |
| 71 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CREATED, | 89 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CREATED, |
| 72 content::NotificationService::AllBrowserContextsAndSources()); | 90 content::NotificationService::AllBrowserContextsAndSources()); |
| 73 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED, | 91 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED, |
| 74 content::NotificationService::AllBrowserContextsAndSources()); | 92 content::NotificationService::AllBrowserContextsAndSources()); |
| 75 } | 93 } |
| 76 | 94 |
| 77 ExtensionAppShimHandler::~ExtensionAppShimHandler() {} | 95 ExtensionAppShimHandler::~ExtensionAppShimHandler() {} |
| 78 | 96 |
| 79 bool ExtensionAppShimHandler::OnShimLaunch(Host* host, | 97 bool ExtensionAppShimHandler::OnShimLaunch(Host* host, |
| 80 AppShimLaunchType launch_type) { | 98 AppShimLaunchType launch_type) { |
| 81 const base::FilePath& profile_path = host->GetProfilePath(); | 99 const base::FilePath& profile_path = host->GetProfilePath(); |
| 82 DCHECK(!profile_path.empty()); | 100 DCHECK(!profile_path.empty()); |
| 83 | 101 |
| 84 if (!profile_manager_facade_->ProfileExistsForPath(profile_path)) { | 102 if (!delegate_->ProfileExistsForPath(profile_path)) { |
| 85 // User may have deleted the profile this shim was originally created for. | 103 // User may have deleted the profile this shim was originally created for. |
| 86 // TODO(jackhou): Add some UI for this case and remove the LOG. | 104 // TODO(jackhou): Add some UI for this case and remove the LOG. |
| 87 LOG(ERROR) << "Requested directory is not a known profile '" | 105 LOG(ERROR) << "Requested directory is not a known profile '" |
| 88 << profile_path.value() << "'."; | 106 << profile_path.value() << "'."; |
| 89 return false; | 107 return false; |
| 90 } | 108 } |
| 91 | 109 |
| 92 Profile* profile = profile_manager_facade_->ProfileForPath(profile_path); | 110 Profile* profile = delegate_->ProfileForPath(profile_path); |
| 93 | 111 |
| 94 const std::string& app_id = host->GetAppId(); | 112 const std::string& app_id = host->GetAppId(); |
| 95 if (!extensions::Extension::IdIsValid(app_id)) { | 113 if (!extensions::Extension::IdIsValid(app_id)) { |
| 96 LOG(ERROR) << "Bad app ID from app shim launch message."; | 114 LOG(ERROR) << "Bad app ID from app shim launch message."; |
| 97 return false; | 115 return false; |
| 98 } | 116 } |
| 99 | 117 |
| 100 // TODO(jackhou): Add some UI for this case and remove the LOG. | 118 // TODO(jackhou): Add some UI for this case and remove the LOG. |
| 101 if (!LaunchApp(profile, app_id, launch_type)) | 119 const extensions::Extension* extension = |
| 120 delegate_->GetAppExtension(profile, app_id); |
| 121 if (!extension) { |
| 122 LOG(ERROR) << "Attempted to launch nonexistent app with id '" |
| 123 << app_id << "'."; |
| 102 return false; | 124 return false; |
| 125 } |
| 126 |
| 127 // If the shim was launched in response to a window appearing, but the window |
| 128 // is closed by the time the shim process launched, return false to close the |
| 129 // shim. |
| 130 if (launch_type == APP_SHIM_LAUNCH_REGISTER_ONLY && |
| 131 delegate_->GetWindows(profile, app_id).empty()) { |
| 132 return false; |
| 133 } |
| 134 |
| 135 // TODO(jeremya): Handle the case that launching the app fails. Probably we |
| 136 // need to watch for 'app successfully launched' or at least 'background page |
| 137 // exists/was created' and time out with failure if we don't see that sign of |
| 138 // life within a certain window. |
| 139 if (launch_type == APP_SHIM_LAUNCH_NORMAL) |
| 140 delegate_->LaunchApp(profile, extension); |
| 103 | 141 |
| 104 // The first host to claim this (profile, app_id) becomes the main host. | 142 // The first host to claim this (profile, app_id) becomes the main host. |
| 105 // For any others, focus the app and return false. | 143 // For any others, focus the app and return false. |
| 106 if (!hosts_.insert(make_pair(make_pair(profile, app_id), host)).second) { | 144 if (!hosts_.insert(make_pair(make_pair(profile, app_id), host)).second) { |
| 107 OnShimFocus(host); | 145 OnShimFocus(host); |
| 108 return false; | 146 return false; |
| 109 } | 147 } |
| 110 | 148 |
| 111 return true; | 149 return true; |
| 112 } | 150 } |
| 113 | 151 |
| 114 void ExtensionAppShimHandler::OnShimClose(Host* host) { | 152 void ExtensionAppShimHandler::OnShimClose(Host* host) { |
| 115 DCHECK(profile_manager_facade_->ProfileExistsForPath( | 153 DCHECK(delegate_->ProfileExistsForPath(host->GetProfilePath())); |
| 116 host->GetProfilePath())); | 154 Profile* profile = delegate_->ProfileForPath(host->GetProfilePath()); |
| 117 Profile* profile = | |
| 118 profile_manager_facade_->ProfileForPath(host->GetProfilePath()); | |
| 119 | 155 |
| 120 HostMap::iterator it = hosts_.find(make_pair(profile, host->GetAppId())); | 156 HostMap::iterator it = hosts_.find(make_pair(profile, host->GetAppId())); |
| 121 // Any hosts other than the main host will still call OnShimClose, so ignore | 157 // Any hosts other than the main host will still call OnShimClose, so ignore |
| 122 // them. | 158 // them. |
| 123 if (it != hosts_.end() && it->second == host) | 159 if (it != hosts_.end() && it->second == host) |
| 124 hosts_.erase(it); | 160 hosts_.erase(it); |
| 125 } | 161 } |
| 126 | 162 |
| 127 void ExtensionAppShimHandler::OnShimFocus(Host* host) { | 163 void ExtensionAppShimHandler::OnShimFocus(Host* host) { |
| 128 DCHECK(profile_manager_facade_->ProfileExistsForPath( | 164 DCHECK(delegate_->ProfileExistsForPath(host->GetProfilePath())); |
| 129 host->GetProfilePath())); | 165 Profile* profile = delegate_->ProfileForPath(host->GetProfilePath()); |
| 130 Profile* profile = | |
| 131 profile_manager_facade_->ProfileForPath(host->GetProfilePath()); | |
| 132 | 166 |
| 133 const ShellWindowList windows = GetWindows(profile, host->GetAppId()); | 167 const ShellWindowList windows = |
| 168 delegate_->GetWindows(profile, host->GetAppId()); |
| 134 std::set<gfx::NativeWindow> native_windows; | 169 std::set<gfx::NativeWindow> native_windows; |
| 135 for (ShellWindowList::const_iterator it = windows.begin(); | 170 for (ShellWindowList::const_iterator it = windows.begin(); |
| 136 it != windows.end(); ++it) { | 171 it != windows.end(); ++it) { |
| 137 native_windows.insert((*it)->GetNativeWindow()); | 172 native_windows.insert((*it)->GetNativeWindow()); |
| 138 } | 173 } |
| 139 ui::FocusWindowSet(native_windows); | 174 ui::FocusWindowSet(native_windows); |
| 140 } | 175 } |
| 141 | 176 |
| 142 void ExtensionAppShimHandler::OnShimQuit(Host* host) { | 177 void ExtensionAppShimHandler::OnShimQuit(Host* host) { |
| 143 DCHECK(profile_manager_facade_->ProfileExistsForPath( | 178 DCHECK(delegate_->ProfileExistsForPath(host->GetProfilePath())); |
| 144 host->GetProfilePath())); | 179 Profile* profile = delegate_->ProfileForPath(host->GetProfilePath()); |
| 145 Profile* profile = | |
| 146 profile_manager_facade_->ProfileForPath(host->GetProfilePath()); | |
| 147 | 180 |
| 148 const ShellWindowList windows = GetWindows(profile, host->GetAppId()); | 181 const ShellWindowList windows = |
| 182 delegate_->GetWindows(profile, host->GetAppId()); |
| 149 for (extensions::ShellWindowRegistry::const_iterator it = windows.begin(); | 183 for (extensions::ShellWindowRegistry::const_iterator it = windows.begin(); |
| 150 it != windows.end(); ++it) { | 184 it != windows.end(); ++it) { |
| 151 (*it)->GetBaseWindow()->Close(); | 185 (*it)->GetBaseWindow()->Close(); |
| 152 } | 186 } |
| 153 } | 187 } |
| 154 | 188 |
| 155 void ExtensionAppShimHandler::set_profile_manager_facade( | 189 void ExtensionAppShimHandler::set_delegate(Delegate* delegate) { |
| 156 ProfileManagerFacade* profile_manager_facade) { | 190 delegate_.reset(delegate); |
| 157 profile_manager_facade_.reset(profile_manager_facade); | |
| 158 } | |
| 159 | |
| 160 bool ExtensionAppShimHandler::LaunchApp(Profile* profile, | |
| 161 const std::string& app_id, | |
| 162 AppShimLaunchType launch_type) { | |
| 163 const extensions::Extension* extension = GetExtension(profile, app_id); | |
| 164 if (!extension) { | |
| 165 LOG(ERROR) << "Attempted to launch nonexistent app with id '" | |
| 166 << app_id << "'."; | |
| 167 return false; | |
| 168 } | |
| 169 | |
| 170 if (launch_type == APP_SHIM_LAUNCH_REGISTER_ONLY) | |
| 171 return !GetWindows(profile, app_id).empty(); | |
| 172 | |
| 173 // TODO(jeremya): Handle the case that launching the app fails. Probably we | |
| 174 // need to watch for 'app successfully launched' or at least 'background page | |
| 175 // exists/was created' and time out with failure if we don't see that sign of | |
| 176 // life within a certain window. | |
| 177 chrome::OpenApplication(chrome::AppLaunchParams( | |
| 178 profile, extension, NEW_FOREGROUND_TAB)); | |
| 179 return true; | |
| 180 } | 191 } |
| 181 | 192 |
| 182 void ExtensionAppShimHandler::Observe( | 193 void ExtensionAppShimHandler::Observe( |
| 183 int type, | 194 int type, |
| 184 const content::NotificationSource& source, | 195 const content::NotificationSource& source, |
| 185 const content::NotificationDetails& details) { | 196 const content::NotificationDetails& details) { |
| 186 Profile* profile = content::Source<Profile>(source).ptr(); | 197 Profile* profile = content::Source<Profile>(source).ptr(); |
| 187 if (profile->IsOffTheRecord()) | 198 if (profile->IsOffTheRecord()) |
| 188 return; | 199 return; |
| 189 | 200 |
| (...skipping 16 matching lines...) Expand all Loading... |
| 206 NOTREACHED(); // Unexpected notification. | 217 NOTREACHED(); // Unexpected notification. |
| 207 break; | 218 break; |
| 208 } | 219 } |
| 209 } | 220 } |
| 210 | 221 |
| 211 void ExtensionAppShimHandler::OnAppStart(Profile* profile, | 222 void ExtensionAppShimHandler::OnAppStart(Profile* profile, |
| 212 const std::string& app_id) {} | 223 const std::string& app_id) {} |
| 213 | 224 |
| 214 void ExtensionAppShimHandler::OnAppActivated(Profile* profile, | 225 void ExtensionAppShimHandler::OnAppActivated(Profile* profile, |
| 215 const std::string& app_id) { | 226 const std::string& app_id) { |
| 216 const extensions::Extension* extension = GetExtension(profile, app_id); | 227 const extensions::Extension* extension = |
| 217 if (!extension || !extension->is_platform_app()) | 228 delegate_->GetAppExtension(profile, app_id); |
| 229 if (!extension) |
| 218 return; | 230 return; |
| 219 | 231 |
| 220 if (hosts_.count(make_pair(profile, extension->id()))) | 232 if (hosts_.count(make_pair(profile, app_id))) |
| 221 return; | 233 return; |
| 222 | 234 |
| 223 web_app::MaybeLaunchShortcut( | 235 delegate_->LaunchShim(profile, extension); |
| 224 web_app::ShortcutInfoForExtensionAndProfile(extension, profile)); | |
| 225 } | 236 } |
| 226 | 237 |
| 227 void ExtensionAppShimHandler::OnAppDeactivated(Profile* profile, | 238 void ExtensionAppShimHandler::OnAppDeactivated(Profile* profile, |
| 228 const std::string& app_id) { | 239 const std::string& app_id) { |
| 229 HostMap::const_iterator it = hosts_.find(make_pair(profile, app_id)); | 240 HostMap::const_iterator it = hosts_.find(make_pair(profile, app_id)); |
| 230 if (it != hosts_.end()) | 241 if (it != hosts_.end()) |
| 231 it->second->OnAppClosed(); | 242 it->second->OnAppClosed(); |
| 232 } | 243 } |
| 233 | 244 |
| 234 void ExtensionAppShimHandler::OnAppStop(Profile* profile, | 245 void ExtensionAppShimHandler::OnAppStop(Profile* profile, |
| 235 const std::string& app_id) {} | 246 const std::string& app_id) {} |
| 236 | 247 |
| 237 void ExtensionAppShimHandler::OnChromeTerminating() {} | 248 void ExtensionAppShimHandler::OnChromeTerminating() {} |
| 238 | 249 |
| 239 } // namespace apps | 250 } // namespace apps |
| OLD | NEW |