Add an API for hosted apps to check their install and running states.

BUG=107216
TEST=ChromeAppAPITest.*

Review URL: http://codereview.chromium.org/10174001

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@133722 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/extensions/chrome_app_api_browsertest.cc b/chrome/browser/extensions/chrome_app_api_browsertest.cc
index 44e91cc..865c44e 100644
--- a/chrome/browser/extensions/chrome_app_api_browsertest.cc
+++ b/chrome/browser/extensions/chrome_app_api_browsertest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,6 +10,8 @@
 #include "base/string_number_conversions.h"
 #include "base/values.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/extension.h"
@@ -21,14 +23,41 @@
 
 class ChromeAppAPITest : public ExtensionBrowserTest {
  protected:
-  bool IsAppInstalled() {
+  bool IsAppInstalled() { return IsAppInstalled(L""); }
+  bool IsAppInstalled(const std::wstring& frame_xpath) {
     std::wstring get_app_is_installed =
         L"window.domAutomationController.send(window.chrome.app.isInstalled);";
     bool result;
     CHECK(
         ui_test_utils::ExecuteJavaScriptAndExtractBool(
             browser()->GetSelectedWebContents()->GetRenderViewHost(),
-            L"", get_app_is_installed, &result));
+            frame_xpath, get_app_is_installed, &result));
+    return result;
+  }
+
+  std::string InstallState() { return InstallState(L""); }
+  std::string InstallState(const std::wstring& frame_xpath) {
+    std::wstring get_app_install_state =
+        L"window.chrome.app.installState("
+        L"function(s) { window.domAutomationController.send(s); });";
+    std::string result;
+    CHECK(
+        ui_test_utils::ExecuteJavaScriptAndExtractString(
+            browser()->GetSelectedWebContents()->GetRenderViewHost(),
+            frame_xpath, get_app_install_state, &result));
+    return result;
+  }
+
+  std::string RunningState() { return RunningState(L""); }
+  std::string RunningState(const std::wstring& frame_xpath) {
+    std::wstring get_app_install_state =
+        L"window.domAutomationController.send("
+        L"window.chrome.app.runningState());";
+    std::string result;
+    CHECK(
+        ui_test_utils::ExecuteJavaScriptAndExtractString(
+            browser()->GetSelectedWebContents()->GetRenderViewHost(),
+            frame_xpath, get_app_install_state, &result));
     return result;
   }
 
@@ -181,3 +210,97 @@
   EXPECT_TRUE(app_details.get());
   EXPECT_TRUE(app_details->Equals(extension->manifest()->value()));
 }
+
+
+IN_PROC_BROWSER_TEST_F(ChromeAppAPITest, InstallAndRunningState) {
+  std::string app_host("app.com");
+  std::string non_app_host("nonapp.com");
+
+  host_resolver()->AddRule(app_host, "127.0.0.1");
+  host_resolver()->AddRule(non_app_host, "127.0.0.1");
+  ASSERT_TRUE(test_server()->Start());
+
+  GURL test_file_url(test_server()->GetURL(
+      "files/extensions/get_app_details_for_frame.html"));
+  GURL::Replacements replace_host;
+
+  replace_host.SetHostStr(app_host);
+  GURL app_url(test_file_url.ReplaceComponents(replace_host));
+
+  replace_host.SetHostStr(non_app_host);
+  GURL non_app_url(test_file_url.ReplaceComponents(replace_host));
+
+  // Before the app is installed, app.com does not think that it is installed
+  ui_test_utils::NavigateToURL(browser(), app_url);
+
+  EXPECT_EQ("not_installed", InstallState());
+  EXPECT_EQ("cannot_run", RunningState());
+  EXPECT_FALSE(IsAppInstalled());
+
+  const Extension* extension = LoadExtension(
+      test_data_dir_.AppendASCII("app_dot_com_app"));
+  ASSERT_TRUE(extension);
+
+  EXPECT_EQ("installed", InstallState());
+  EXPECT_EQ("ready_to_run", RunningState());
+  EXPECT_FALSE(IsAppInstalled());
+
+  // Reloading the page should put the tab in an app process.
+  ui_test_utils::NavigateToURL(browser(), app_url);
+  EXPECT_EQ("installed", InstallState());
+  EXPECT_EQ("running", RunningState());
+  EXPECT_TRUE(IsAppInstalled());
+
+  // Disable the extension and verify the state.
+  browser()->profile()->GetExtensionService()->DisableExtension(
+      extension->id(), Extension::DISABLE_PERMISSIONS_INCREASE);
+  ui_test_utils::NavigateToURL(browser(), app_url);
+
+  EXPECT_EQ("disabled", InstallState());
+  EXPECT_EQ("cannot_run", RunningState());
+  EXPECT_FALSE(IsAppInstalled());
+
+  browser()->profile()->GetExtensionService()->EnableExtension(extension->id());
+  EXPECT_EQ("installed", InstallState());
+  EXPECT_EQ("ready_to_run", RunningState());
+  EXPECT_FALSE(IsAppInstalled());
+
+  // The non-app URL should still not be installed or running.
+  ui_test_utils::NavigateToURL(browser(), non_app_url);
+
+  EXPECT_EQ("not_installed", InstallState());
+  EXPECT_EQ("cannot_run", RunningState());
+  EXPECT_FALSE(IsAppInstalled());
+
+  EXPECT_EQ("installed", InstallState(L"//html/iframe[1]"));
+  EXPECT_EQ("cannot_run", RunningState(L"//html/iframe[1]"));
+  EXPECT_FALSE(IsAppInstalled(L"//html/iframe[1]"));
+
+}
+
+IN_PROC_BROWSER_TEST_F(ChromeAppAPITest, InstallAndRunningStateFrame) {
+  std::string app_host("app.com");
+  std::string non_app_host("nonapp.com");
+
+  host_resolver()->AddRule(app_host, "127.0.0.1");
+  host_resolver()->AddRule(non_app_host, "127.0.0.1");
+  ASSERT_TRUE(test_server()->Start());
+
+  GURL test_file_url(test_server()->GetURL(
+      "files/extensions/get_app_details_for_frame_reversed.html"));
+  GURL::Replacements replace_host;
+
+  replace_host.SetHostStr(app_host);
+  GURL app_url(test_file_url.ReplaceComponents(replace_host));
+
+  replace_host.SetHostStr(non_app_host);
+  GURL non_app_url(test_file_url.ReplaceComponents(replace_host));
+
+  // Check the install and running state of a non-app iframe running
+  // within an app.
+  ui_test_utils::NavigateToURL(browser(), app_url);
+
+  EXPECT_EQ("not_installed", InstallState(L"//html/iframe[1]"));
+  EXPECT_EQ("cannot_run", RunningState(L"//html/iframe[1]"));
+  EXPECT_FALSE(IsAppInstalled(L"//html/iframe[1]"));
+}
diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h
index 68e2e5f..7ddbca4 100644
--- a/chrome/browser/extensions/extension_service.h
+++ b/chrome/browser/extensions/extension_service.h
@@ -710,7 +710,6 @@
   scoped_ptr<extensions::SettingsFrontend> settings_frontend_;
 
   // The current list of installed extensions.
-  // TODO(aa): This should use chrome/common/extensions/extension_set.h.
   ExtensionSet extensions_;
 
   // The list of installed extensions that have been disabled.
diff --git a/chrome/browser/extensions/extension_tab_helper.cc b/chrome/browser/extensions/extension_tab_helper.cc
index b45b0adf..ab65db37 100644
--- a/chrome/browser/extensions/extension_tab_helper.cc
+++ b/chrome/browser/extensions/extension_tab_helper.cc
@@ -137,6 +137,8 @@
                         OnInlineWebstoreInstall)
     IPC_MESSAGE_HANDLER(ExtensionHostMsg_GetAppNotifyChannel,
                         OnGetAppNotifyChannel)
+    IPC_MESSAGE_HANDLER(ExtensionHostMsg_GetAppInstallState,
+                        OnGetAppInstallState);
     IPC_MESSAGE_HANDLER(ExtensionHostMsg_Request, OnRequest)
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
@@ -223,6 +225,28 @@
   // We'll get called back in AppNotifyChannelSetupComplete.
 }
 
+void ExtensionTabHelper::OnGetAppInstallState(const GURL& requestor_url,
+                                              int return_route_id,
+                                              int callback_id) {
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
+  ExtensionService* extension_service = profile->GetExtensionService();
+  const ExtensionSet* extensions = extension_service->extensions();
+  const ExtensionSet* disabled = extension_service->disabled_extensions();
+
+  ExtensionURLInfo url(requestor_url);
+  std::string state;
+  if (extensions->GetHostedAppByURL(url))
+    state = extension_misc::kAppStateInstalled;
+  else if (disabled->GetHostedAppByURL(url))
+    state = extension_misc::kAppStateDisabled;
+  else
+    state = extension_misc::kAppStateNotInstalled;
+
+  Send(new ExtensionMsg_GetAppInstallStateResponse(
+      return_route_id, state, callback_id));
+}
+
 void ExtensionTabHelper::AppNotifyChannelSetupComplete(
     const std::string& channel_id,
     const std::string& error,
diff --git a/chrome/browser/extensions/extension_tab_helper.h b/chrome/browser/extensions/extension_tab_helper.h
index 79d108b..e0bbd26 100644
--- a/chrome/browser/extensions/extension_tab_helper.h
+++ b/chrome/browser/extensions/extension_tab_helper.h
@@ -116,6 +116,9 @@
                              const std::string& client_id,
                              int return_route_id,
                              int callback_id);
+  void OnGetAppInstallState(const GURL& requestor_url,
+                            int return_route_id,
+                            int callback_id);
   void OnRequest(const ExtensionHostMsg_Request_Params& params);
 
   // App extensions related methods: