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_host_manager_mac.h" | 8 #include "apps/app_shim/app_shim_host_manager_mac.h" |
9 #include "apps/app_shim/app_shim_messages.h" | 9 #include "apps/app_shim/app_shim_messages.h" |
10 #include "apps/launcher.h" | 10 #include "apps/launcher.h" |
11 #include "apps/shell_window.h" | 11 #include "apps/shell_window.h" |
12 #include "apps/shell_window_registry.h" | 12 #include "apps/shell_window_registry.h" |
13 #include "apps/ui/native_app_window.h" | 13 #include "apps/ui/native_app_window.h" |
14 #include "base/files/file_path.h" | 14 #include "base/files/file_path.h" |
15 #include "base/logging.h" | 15 #include "base/logging.h" |
16 #include "chrome/browser/browser_process.h" | 16 #include "chrome/browser/browser_process.h" |
17 #include "chrome/browser/chrome_notification_types.h" | 17 #include "chrome/browser/chrome_notification_types.h" |
18 #include "chrome/browser/extensions/extension_host.h" | 18 #include "chrome/browser/extensions/extension_host.h" |
19 #include "chrome/browser/extensions/extension_service.h" | 19 #include "chrome/browser/extensions/extension_service.h" |
20 #include "chrome/browser/extensions/extension_system.h" | 20 #include "chrome/browser/extensions/extension_system.h" |
21 #include "chrome/browser/profiles/profile.h" | 21 #include "chrome/browser/profiles/profile.h" |
22 #include "chrome/browser/profiles/profile_manager.h" | 22 #include "chrome/browser/profiles/profile_manager.h" |
| 23 #include "chrome/browser/ui/extensions/extension_enable_flow.h" |
| 24 #include "chrome/browser/ui/extensions/extension_enable_flow_delegate.h" |
23 #include "chrome/browser/ui/web_applications/web_app_ui.h" | 25 #include "chrome/browser/ui/web_applications/web_app_ui.h" |
24 #include "chrome/browser/ui/webui/ntp/core_app_launcher_handler.h" | 26 #include "chrome/browser/ui/webui/ntp/core_app_launcher_handler.h" |
25 #include "chrome/browser/web_applications/web_app_mac.h" | 27 #include "chrome/browser/web_applications/web_app_mac.h" |
26 #include "chrome/common/extensions/extension_constants.h" | 28 #include "chrome/common/extensions/extension_constants.h" |
27 #include "content/public/browser/notification_details.h" | 29 #include "content/public/browser/notification_details.h" |
28 #include "content/public/browser/notification_service.h" | 30 #include "content/public/browser/notification_service.h" |
29 #include "content/public/browser/notification_source.h" | 31 #include "content/public/browser/notification_source.h" |
30 #include "ui/base/cocoa/focus_window_set.h" | 32 #include "ui/base/cocoa/focus_window_set.h" |
31 | 33 |
32 namespace { | 34 namespace { |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
66 it != windows.end(); ++it) { | 68 it != windows.end(); ++it) { |
67 native_windows.insert((*it)->GetNativeWindow()); | 69 native_windows.insert((*it)->GetNativeWindow()); |
68 } | 70 } |
69 // Allow workspace switching. For the browser process, we can reasonably rely | 71 // Allow workspace switching. For the browser process, we can reasonably rely |
70 // on OS X to switch spaces for us and honor relevant user settings. But shims | 72 // on OS X to switch spaces for us and honor relevant user settings. But shims |
71 // don't have windows, so we have to do it ourselves. | 73 // don't have windows, so we have to do it ourselves. |
72 ui::FocusWindowSet(native_windows, true); | 74 ui::FocusWindowSet(native_windows, true); |
73 return true; | 75 return true; |
74 } | 76 } |
75 | 77 |
| 78 // Attempts to launch a packaged app, prompting the user to enable it if |
| 79 // necessary. The prompt is shown in its own window. |
| 80 // This class manages its own lifetime. |
| 81 class EnableViaPrompt : public ExtensionEnableFlowDelegate { |
| 82 public: |
| 83 EnableViaPrompt(Profile* profile, |
| 84 const std::string& extension_id, |
| 85 const base::Callback<void()>& callback) |
| 86 : profile_(profile), |
| 87 extension_id_(extension_id), |
| 88 callback_(callback) { |
| 89 } |
| 90 |
| 91 virtual ~EnableViaPrompt() { |
| 92 } |
| 93 |
| 94 void Run() { |
| 95 flow_.reset(new ExtensionEnableFlow(profile_, extension_id_, this)); |
| 96 flow_->StartForCurrentlyNonexistentWindow( |
| 97 base::Callback<gfx::NativeWindow(void)>()); |
| 98 } |
| 99 |
| 100 private: |
| 101 // ExtensionEnableFlowDelegate overrides. |
| 102 virtual void ExtensionEnableFlowFinished() OVERRIDE { |
| 103 callback_.Run(); |
| 104 delete this; |
| 105 } |
| 106 |
| 107 virtual void ExtensionEnableFlowAborted(bool user_initiated) OVERRIDE { |
| 108 callback_.Run(); |
| 109 delete this; |
| 110 } |
| 111 |
| 112 Profile* profile_; |
| 113 std::string extension_id_; |
| 114 base::Callback<void()> callback_; |
| 115 scoped_ptr<ExtensionEnableFlow> flow_; |
| 116 |
| 117 DISALLOW_COPY_AND_ASSIGN(EnableViaPrompt); |
| 118 }; |
| 119 |
76 } // namespace | 120 } // namespace |
77 | 121 |
78 namespace apps { | 122 namespace apps { |
79 | 123 |
80 bool ExtensionAppShimHandler::Delegate::ProfileExistsForPath( | 124 bool ExtensionAppShimHandler::Delegate::ProfileExistsForPath( |
81 const base::FilePath& path) { | 125 const base::FilePath& path) { |
82 ProfileManager* profile_manager = g_browser_process->profile_manager(); | 126 ProfileManager* profile_manager = g_browser_process->profile_manager(); |
83 // Check for the profile name in the profile info cache to ensure that we | 127 // Check for the profile name in the profile info cache to ensure that we |
84 // never access any directory that isn't a known profile. | 128 // never access any directory that isn't a known profile. |
85 base::FilePath full_path = profile_manager->user_data_dir().Append(path); | 129 base::FilePath full_path = profile_manager->user_data_dir().Append(path); |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
119 Profile* profile, | 163 Profile* profile, |
120 const std::string& extension_id) { | 164 const std::string& extension_id) { |
121 ExtensionService* extension_service = | 165 ExtensionService* extension_service = |
122 extensions::ExtensionSystem::Get(profile)->extension_service(); | 166 extensions::ExtensionSystem::Get(profile)->extension_service(); |
123 DCHECK(extension_service); | 167 DCHECK(extension_service); |
124 const extensions::Extension* extension = | 168 const extensions::Extension* extension = |
125 extension_service->GetExtensionById(extension_id, false); | 169 extension_service->GetExtensionById(extension_id, false); |
126 return extension && extension->is_platform_app() ? extension : NULL; | 170 return extension && extension->is_platform_app() ? extension : NULL; |
127 } | 171 } |
128 | 172 |
| 173 void ExtensionAppShimHandler::Delegate::EnableExtension( |
| 174 Profile* profile, |
| 175 const std::string& extension_id, |
| 176 const base::Callback<void()>& callback) { |
| 177 (new EnableViaPrompt(profile, extension_id, callback))->Run(); |
| 178 } |
| 179 |
129 void ExtensionAppShimHandler::Delegate::LaunchApp( | 180 void ExtensionAppShimHandler::Delegate::LaunchApp( |
130 Profile* profile, | 181 Profile* profile, |
131 const extensions::Extension* extension, | 182 const extensions::Extension* extension, |
132 const std::vector<base::FilePath>& files) { | 183 const std::vector<base::FilePath>& files) { |
133 CoreAppLauncherHandler::RecordAppLaunchType( | 184 CoreAppLauncherHandler::RecordAppLaunchType( |
134 extension_misc::APP_LAUNCH_CMD_LINE_APP, extension->GetType()); | 185 extension_misc::APP_LAUNCH_CMD_LINE_APP, extension->GetType()); |
135 if (files.empty()) { | 186 if (files.empty()) { |
136 apps::LaunchPlatformApp(profile, extension); | 187 apps::LaunchPlatformApp(profile, extension); |
137 } else { | 188 } else { |
138 for (std::vector<base::FilePath>::const_iterator it = files.begin(); | 189 for (std::vector<base::FilePath>::const_iterator it = files.begin(); |
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
279 | 330 |
280 // Return now. OnAppLaunchComplete will be called when the app is activated. | 331 // Return now. OnAppLaunchComplete will be called when the app is activated. |
281 } | 332 } |
282 | 333 |
283 void ExtensionAppShimHandler::OnProfileLoaded( | 334 void ExtensionAppShimHandler::OnProfileLoaded( |
284 Host* host, | 335 Host* host, |
285 AppShimLaunchType launch_type, | 336 AppShimLaunchType launch_type, |
286 const std::vector<base::FilePath>& files, | 337 const std::vector<base::FilePath>& files, |
287 Profile* profile) { | 338 Profile* profile) { |
288 const std::string& app_id = host->GetAppId(); | 339 const std::string& app_id = host->GetAppId(); |
289 // TODO(jackhou): Add some UI for this case and remove the LOG. | |
290 const extensions::Extension* extension = | |
291 delegate_->GetAppExtension(profile, app_id); | |
292 if (!extension) { | |
293 LOG(ERROR) << "Attempted to launch nonexistent app with id '" | |
294 << app_id << "'."; | |
295 host->OnAppLaunchComplete(APP_SHIM_LAUNCH_APP_NOT_FOUND); | |
296 return; | |
297 } | |
298 | 340 |
299 // The first host to claim this (profile, app_id) becomes the main host. | 341 // The first host to claim this (profile, app_id) becomes the main host. |
300 // For any others, focus or relaunch the app. | 342 // For any others, focus or relaunch the app. |
301 if (!hosts_.insert(make_pair(make_pair(profile, app_id), host)).second) { | 343 if (!hosts_.insert(make_pair(make_pair(profile, app_id), host)).second) { |
302 OnShimFocus(host, | 344 OnShimFocus(host, |
303 launch_type == APP_SHIM_LAUNCH_NORMAL ? | 345 launch_type == APP_SHIM_LAUNCH_NORMAL ? |
304 APP_SHIM_FOCUS_REOPEN : APP_SHIM_FOCUS_NORMAL, | 346 APP_SHIM_FOCUS_REOPEN : APP_SHIM_FOCUS_NORMAL, |
305 files); | 347 files); |
306 host->OnAppLaunchComplete(APP_SHIM_LAUNCH_DUPLICATE_HOST); | 348 host->OnAppLaunchComplete(APP_SHIM_LAUNCH_DUPLICATE_HOST); |
307 return; | 349 return; |
308 } | 350 } |
309 | 351 |
| 352 if (launch_type != APP_SHIM_LAUNCH_NORMAL) { |
| 353 host->OnAppLaunchComplete(APP_SHIM_LAUNCH_SUCCESS); |
| 354 return; |
| 355 } |
| 356 |
310 // TODO(jeremya): Handle the case that launching the app fails. Probably we | 357 // TODO(jeremya): Handle the case that launching the app fails. Probably we |
311 // need to watch for 'app successfully launched' or at least 'background page | 358 // need to watch for 'app successfully launched' or at least 'background page |
312 // exists/was created' and time out with failure if we don't see that sign of | 359 // exists/was created' and time out with failure if we don't see that sign of |
313 // life within a certain window. | 360 // life within a certain window. |
314 if (launch_type == APP_SHIM_LAUNCH_NORMAL) | 361 const extensions::Extension* extension = |
| 362 delegate_->GetAppExtension(profile, app_id); |
| 363 if (extension) { |
315 delegate_->LaunchApp(profile, extension, files); | 364 delegate_->LaunchApp(profile, extension, files); |
316 else | 365 return; |
317 host->OnAppLaunchComplete(APP_SHIM_LAUNCH_SUCCESS); | 366 } |
| 367 |
| 368 delegate_->EnableExtension( |
| 369 profile, app_id, |
| 370 base::Bind(&ExtensionAppShimHandler::OnExtensionEnabled, |
| 371 weak_factory_.GetWeakPtr(), |
| 372 host->GetProfilePath(), app_id, files)); |
318 } | 373 } |
319 | 374 |
| 375 void ExtensionAppShimHandler::OnExtensionEnabled( |
| 376 const base::FilePath& profile_path, |
| 377 const std::string& app_id, |
| 378 const std::vector<base::FilePath>& files) { |
| 379 Profile* profile = delegate_->ProfileForPath(profile_path); |
| 380 if (!profile) |
| 381 return; |
| 382 |
| 383 const extensions::Extension* extension = |
| 384 delegate_->GetAppExtension(profile, app_id); |
| 385 if (!extension || !delegate_->ProfileExistsForPath(profile_path)) { |
| 386 // If !extension, the extension doesn't exist, or was not re-enabled. |
| 387 // If the profile doesn't exist, it may have been deleted during the enable |
| 388 // prompt. In this case, NOTIFICATION_PROFILE_DESTROYED may not be fired |
| 389 // until later, so respond to the host now. |
| 390 Host* host = FindHost(profile, app_id); |
| 391 if (host) |
| 392 host->OnAppLaunchComplete(APP_SHIM_LAUNCH_APP_NOT_FOUND); |
| 393 return; |
| 394 } |
| 395 |
| 396 delegate_->LaunchApp(profile, extension, files); |
| 397 } |
| 398 |
| 399 |
320 void ExtensionAppShimHandler::OnShimClose(Host* host) { | 400 void ExtensionAppShimHandler::OnShimClose(Host* host) { |
321 // This might be called when shutting down. Don't try to look up the profile | 401 // This might be called when shutting down. Don't try to look up the profile |
322 // since profile_manager might not be around. | 402 // since profile_manager might not be around. |
323 for (HostMap::iterator it = hosts_.begin(); it != hosts_.end(); ) { | 403 for (HostMap::iterator it = hosts_.begin(); it != hosts_.end(); ) { |
324 HostMap::iterator current = it++; | 404 HostMap::iterator current = it++; |
325 if (current->second == host) | 405 if (current->second == host) |
326 hosts_.erase(current); | 406 hosts_.erase(current); |
327 } | 407 } |
328 } | 408 } |
329 | 409 |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
393 AppLifetimeMonitorFactory::GetForProfile(profile)->AddObserver(this); | 473 AppLifetimeMonitorFactory::GetForProfile(profile)->AddObserver(this); |
394 break; | 474 break; |
395 } | 475 } |
396 case chrome::NOTIFICATION_PROFILE_DESTROYED: { | 476 case chrome::NOTIFICATION_PROFILE_DESTROYED: { |
397 AppLifetimeMonitorFactory::GetForProfile(profile)->RemoveObserver(this); | 477 AppLifetimeMonitorFactory::GetForProfile(profile)->RemoveObserver(this); |
398 // Shut down every shim associated with this profile. | 478 // Shut down every shim associated with this profile. |
399 for (HostMap::iterator it = hosts_.begin(); it != hosts_.end(); ) { | 479 for (HostMap::iterator it = hosts_.begin(); it != hosts_.end(); ) { |
400 // Increment the iterator first as OnAppClosed may call back to | 480 // Increment the iterator first as OnAppClosed may call back to |
401 // OnShimClose and invalidate the iterator. | 481 // OnShimClose and invalidate the iterator. |
402 HostMap::iterator current = it++; | 482 HostMap::iterator current = it++; |
403 if (profile->IsSameProfile(current->first.first)) | 483 if (profile->IsSameProfile(current->first.first)) { |
404 current->second->OnAppClosed(); | 484 Host* host = current->second; |
| 485 host->OnAppClosed(); |
| 486 } |
405 } | 487 } |
406 break; | 488 break; |
407 } | 489 } |
408 default: { | 490 default: { |
409 NOTREACHED(); // Unexpected notification. | 491 NOTREACHED(); // Unexpected notification. |
410 break; | 492 break; |
411 } | 493 } |
412 } | 494 } |
413 } | 495 } |
414 | 496 |
(...skipping 26 matching lines...) Expand all Loading... |
441 if (hosts_.empty()) | 523 if (hosts_.empty()) |
442 delegate_->MaybeTerminate(); | 524 delegate_->MaybeTerminate(); |
443 } | 525 } |
444 | 526 |
445 void ExtensionAppShimHandler::OnAppStop(Profile* profile, | 527 void ExtensionAppShimHandler::OnAppStop(Profile* profile, |
446 const std::string& app_id) {} | 528 const std::string& app_id) {} |
447 | 529 |
448 void ExtensionAppShimHandler::OnChromeTerminating() {} | 530 void ExtensionAppShimHandler::OnChromeTerminating() {} |
449 | 531 |
450 } // namespace apps | 532 } // namespace apps |
OLD | NEW |