Add a 'simulate browser restart' button for apps to chrome://extensions.

Currently to test how an app behaves when the browser restarts you have to
restart the browser (eg: for an upgrade).  This button lets you simulate that.


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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@163774 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 081827bdd..253810f 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -30,6 +30,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_plugin_service_filter.h"
 #include "chrome/browser/debugger/devtools_window.h"
+#include "chrome/browser/extensions/api/app_runtime/app_runtime_api.h"
 #include "chrome/browser/extensions/api/cookies/cookies_api.h"
 #include "chrome/browser/extensions/api/declarative/rules_registry_service.h"
 #include "chrome/browser/extensions/api/extension_action/extension_actions_api.h"
@@ -686,6 +687,18 @@
 }
 
 void ExtensionService::ReloadExtension(const std::string& extension_id) {
+  int events = HasShellWindows(extension_id) ? EVENT_LAUNCHED : EVENT_NONE;
+  ReloadExtensionWithEvents(extension_id, events);
+}
+
+void ExtensionService::RestartExtension(const std::string& extension_id) {
+  int events = HasShellWindows(extension_id) ? EVENT_RESTARTED : EVENT_NONE;
+  ReloadExtensionWithEvents(extension_id, events);
+}
+
+void ExtensionService::ReloadExtensionWithEvents(
+    const std::string& extension_id,
+    int events) {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   FilePath path;
   const Extension* current_extension = GetExtensionById(extension_id, false);
@@ -711,11 +724,7 @@
         orphaned_dev_tools_[extension_id] = devtools_cookie;
     }
 
-    if (current_extension->is_platform_app() &&
-        !extensions::ShellWindowRegistry::Get(profile_)->
-            GetShellWindowsForApp(extension_id).empty()) {
-      relaunch_app_ids_.insert(extension_id);
-    }
+    on_load_events_[extension_id] = events;
 
     path = current_extension->path();
     DisableExtension(extension_id, Extension::DISABLE_RELOAD);
@@ -2041,7 +2050,7 @@
   extensions_.Insert(scoped_extension);
   SyncExtensionChangeIfNeeded(*extension);
   NotifyExtensionLoaded(extension);
-  QueueRestoreAppWindow(extension);
+  DoPostLoadTasks(extension);
 }
 
 void ExtensionService::InitializePermissions(const Extension* extension) {
@@ -2741,19 +2750,25 @@
   return nacl_module_list_.end();
 }
 
-void ExtensionService::QueueRestoreAppWindow(const Extension* extension) {
-  std::set<std::string>::iterator relaunch_iter =
-      relaunch_app_ids_.find(extension->id());
-  if (relaunch_iter != relaunch_app_ids_.end()) {
-    extensions::LazyBackgroundTaskQueue* queue =
-        system_->lazy_background_task_queue();
-    if (queue->ShouldEnqueueTask(profile(), extension)) {
+void ExtensionService::DoPostLoadTasks(const Extension* extension) {
+  std::map<std::string, int>::iterator it =
+      on_load_events_.find(extension->id());
+  if (it == on_load_events_.end())
+    return;
+
+  int events_to_fire = it->second;
+  extensions::LazyBackgroundTaskQueue* queue =
+      system_->lazy_background_task_queue();
+  if (queue->ShouldEnqueueTask(profile(), extension)) {
+    if (events_to_fire & EVENT_LAUNCHED)
       queue->AddPendingTask(profile(), extension->id(),
                             base::Bind(&ExtensionService::LaunchApplication));
-    }
-
-    relaunch_app_ids_.erase(relaunch_iter);
+    if (events_to_fire & EVENT_RESTARTED)
+      queue->AddPendingTask(profile(), extension->id(),
+                            base::Bind(&ExtensionService::RestartApplication));
   }
+
+  on_load_events_.erase(it);
 }
 
 // static
@@ -2769,6 +2784,25 @@
 #endif
 }
 
+// static
+void ExtensionService::RestartApplication(
+    extensions::ExtensionHost* extension_host) {
+  if (!extension_host)
+    return;
+
+#if !defined(OS_ANDROID)
+  extensions::AppEventRouter::DispatchOnRestartedEvent(
+      extension_host->profile(), extension_host->extension());
+#endif
+}
+
+bool ExtensionService::HasShellWindows(const std::string& extension_id) {
+  const Extension* current_extension = GetExtensionById(extension_id, false);
+  return current_extension && current_extension->is_platform_app() &&
+      !extensions::ShellWindowRegistry::Get(profile_)->
+          GetShellWindowsForApp(extension_id).empty();
+}
+
 void ExtensionService::InspectExtensionHost(
     extensions::ExtensionHost* host) {
   if (host)
diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h
index f18d5e01..49f029bd 100644
--- a/chrome/browser/extensions/extension_service.h
+++ b/chrome/browser/extensions/extension_service.h
@@ -321,6 +321,9 @@
   // Reloads the specified extension.
   void ReloadExtension(const std::string& extension_id);
 
+  // Restarts the specified extension.
+  void RestartExtension(const std::string& extension_id);
+
   // Uninstalls the specified extension. Callers should only call this method
   // with extensions that exist. |external_uninstall| is a magical parameter
   // that is only used to send information to ExtensionPrefs, which external
@@ -706,6 +709,13 @@
     INCLUDE_TERMINATED = 1 << 2
   };
 
+  // Events to be fired after an extension is reloaded.
+  enum PostReloadEvents {
+    EVENT_NONE = 0,
+    EVENT_LAUNCHED = 1 << 0,
+    EVENT_RESTARTED = 1 << 1,
+  };
+
   // Look up an extension by ID, optionally including either or both of enabled
   // and disabled extensions.
   const extensions::Extension* GetExtensionByIdInternal(
@@ -727,6 +737,14 @@
   void NotifyExtensionUnloaded(const extensions::Extension* extension,
                                extension_misc::UnloadedExtensionReason reason);
 
+  // Reloads |extension_id| and then dispatches to it the PostReloadEvents
+  // indicated by |events|.
+  void ReloadExtensionWithEvents(const std::string& extension_id,
+                                int events);
+
+  // Returns true if the app with id |extension_id| has any shell windows open.
+  bool HasShellWindows(const std::string& extension_id);
+
   // Helper that updates the active extension list used for crash reporting.
   void UpdateActiveExtensionsInCrashReporter();
 
@@ -745,11 +763,16 @@
 
   NaClModuleInfoList::iterator FindNaClModule(const GURL& url);
 
-  // Enqueues a launch task in the lazy background page queue.
-  void QueueRestoreAppWindow(const extensions::Extension* extension);
+  // Performs tasks requested to occur after |extension| loads.
+  void DoPostLoadTasks(const extensions::Extension* extension);
+
   // Launches the platform app associated with |extension_host|.
   static void LaunchApplication(extensions::ExtensionHost* extension_host);
 
+  // Dispatches a restart event to the platform app associated with
+  // |extension_host|.
+  static void RestartApplication(extensions::ExtensionHost* extension_host);
+
   // Helper to inspect an ExtensionHost after it has been loaded.
   void InspectExtensionHost(extensions::ExtensionHost* host);
 
@@ -823,9 +846,9 @@
   typedef std::map<std::string, int> OrphanedDevTools;
   OrphanedDevTools orphaned_dev_tools_;
 
-  // A set of apps that had an open window the last time they were reloaded.
-  // A new window will be launched when the app is succesfully reloaded.
-  std::set<std::string> relaunch_app_ids_;
+  // Maps extension ids to a bitmask that indicates which events should be
+  // dispatched to the extension when it is loaded.
+  std::map<std::string, int> on_load_events_;
 
   content::NotificationRegistrar registrar_;
   PrefChangeRegistrar pref_change_registrar_;
diff --git a/chrome/browser/tab_contents/render_view_context_menu.cc b/chrome/browser/tab_contents/render_view_context_menu.cc
index 6fa59e4..1bb5296b 100644
--- a/chrome/browser/tab_contents/render_view_context_menu.cc
+++ b/chrome/browser/tab_contents/render_view_context_menu.cc
@@ -541,6 +541,8 @@
                                     IDS_CONTENT_CONTEXT_RELOAD_PAGE);
     menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP,
                                     IDS_CONTENT_CONTEXT_RELOAD_PACKAGED_APP);
+    menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP,
+                                    IDS_CONTENT_CONTEXT_RESTART_APP);
     AppendDeveloperItems();
     menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE,
                                     IDS_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE);
@@ -997,6 +999,7 @@
     case IDC_CONTENT_CONTEXT_INSPECTELEMENT:
     case IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE:
     case IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP:
+    case IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP:
       return IsDevCommandEnabled(id);
 
     case IDC_CONTENT_CONTEXT_VIEWPAGEINFO:
@@ -1518,6 +1521,16 @@
       break;
     }
 
+    case IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP: {
+      const Extension* platform_app = GetExtension();
+      DCHECK(platform_app);
+      DCHECK(platform_app->is_platform_app());
+
+      extensions::ExtensionSystem::Get(profile_)->extension_service()->
+          RestartExtension(platform_app->id());
+      break;
+    }
+
     case IDC_PRINT:
       if (params_.media_type == WebContextMenuData::MediaTypeNone) {
         printing::PrintViewManager* print_view_manager =