Rearchitecting the 'app mode' code on mac for v2 apps.

A lot of the old code assumed an outdated model for v2 packaged apps on mac;
specifically that they used to run in their own Chrome process with their own
user data directory. This is no longer the case, and as such it's not possible
to have apps running in their own process, because they need access to profile
information from Chrome.

This patch changes the code so that the 'app mode' .app bundles function more
like shortcuts to open the app in an existing Chrome browser process (launching
Chrome if it's not already running). This allows the app to be launched via
Spotlight and the /Applications folder, and the user can e.g. drag the app onto
the dock like a regular Mac application.

There is as yet no way to trigger the shortcut creation code.

BUG=168080


Review URL: https://chromiumcodereview.appspot.com/11737014

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@176803 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm
index 9031dc0..7c357fc2 100644
--- a/chrome/browser/app_controller_mac.mm
+++ b/chrome/browser/app_controller_mac.mm
@@ -22,6 +22,8 @@
 #include "chrome/browser/command_updater.h"
 #include "chrome/browser/download/download_service.h"
 #include "chrome/browser/download/download_service_factory.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_system.h"
 #include "chrome/browser/first_run/first_run.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/prefs/pref_service.h"
@@ -55,12 +57,14 @@
 #import "chrome/browser/ui/cocoa/tabs/tab_strip_controller.h"
 #import "chrome/browser/ui/cocoa/tabs/tab_window_controller.h"
 #include "chrome/browser/ui/cocoa/task_manager_mac.h"
+#include "chrome/browser/ui/extensions/application_launch.h"
 #include "chrome/browser/ui/startup/startup_browser_creator.h"
 #include "chrome/browser/ui/startup/startup_browser_creator_impl.h"
 #include "chrome/common/chrome_notification_types.h"
 #include "chrome/common/chrome_paths_internal.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/cloud_print/cloud_print_class_mac.h"
+#include "chrome/common/extensions/extension_constants.h"
 #include "chrome/common/mac/app_mode_common.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/service_messages.h"
@@ -177,6 +181,8 @@
 - (void)getUrl:(NSAppleEventDescriptor*)event
      withReply:(NSAppleEventDescriptor*)reply;
 - (void)submitCloudPrintJob:(NSAppleEventDescriptor*)event;
+- (void)launchPlatformApp:(NSAppleEventDescriptor*)event
+                withReply:(NSAppleEventDescriptor*)reply;
 - (void)windowLayeringDidChange:(NSNotification*)inNotification;
 - (void)windowChangedToProfile:(Profile*)profile;
 - (void)checkForAnyKeyWindows;
@@ -208,6 +214,11 @@
         forEventClass:'WWW!'    // A particularly ancient AppleEvent that dates
            andEventID:'OURL'];  // back to the Spyglass days.
 
+  [em setEventHandler:self
+          andSelector:@selector(launchPlatformApp:withReply:)
+        forEventClass:app_mode::kAEChromeAppClass
+           andEventID:app_mode::kAEChromeAppLaunch];
+
   // Register for various window layering changes. We use these to update
   // various UI elements (command-key equivalents, etc) when the frontmost
   // window changes.
@@ -1098,6 +1109,40 @@
   [self openUrls:gurlVector];
 }
 
+- (void)launchPlatformApp:(NSAppleEventDescriptor*)event
+                withReply:(NSAppleEventDescriptor*)reply {
+  NSString* appId =
+      [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
+  NSString* profileDir =
+      [[event paramDescriptorForKeyword:app_mode::kAEProfileDirKey]
+          stringValue];
+
+  ProfileManager* profileManager = g_browser_process->profile_manager();
+  FilePath path = FilePath(base::SysNSStringToUTF8(profileDir));
+  path = profileManager->user_data_dir().Append(path);
+  Profile* profile = profileManager->GetProfile(path);
+  if (!profile) {
+    LOG(ERROR) << "Unable to locate a suitable profile for profile directory '"
+               << profileDir << "' while trying to load app with id '"
+               << appId << "'.";
+    return;
+  }
+  ExtensionServiceInterface* extensionService =
+      extensions::ExtensionSystem::Get(profile)->extension_service();
+  const extensions::Extension* extension =
+      extensionService->GetExtensionById(
+          base::SysNSStringToUTF8(appId), false);
+  if (!extension) {
+    LOG(ERROR) << "Shortcut attempted to launch nonexistent app with id '"
+               << base::SysNSStringToUTF8(appId) << "'.";
+    return;
+  }
+  application_launch::LaunchParams params(profile, extension,
+                                          extension_misc::LAUNCH_NONE,
+                                          NEW_WINDOW);
+  application_launch::OpenApplication(params);
+}
+
 // Apple Event handler that receives print event from service
 // process, gets the required data and launches Print dialog.
 - (void)submitCloudPrintJob:(NSAppleEventDescriptor*)event {