Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <string>
      6 
      7 #include "base/command_line.h"
      8 #include "base/json/json_reader.h"
      9 #include "base/memory/scoped_ptr.h"
     10 #include "base/strings/string_number_conversions.h"
     11 #include "base/values.h"
     12 #include "chrome/browser/extensions/extension_browsertest.h"
     13 #include "chrome/browser/extensions/extension_service.h"
     14 #include "chrome/browser/profiles/profile.h"
     15 #include "chrome/browser/ui/browser.h"
     16 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     17 #include "chrome/common/chrome_switches.h"
     18 #include "chrome/test/base/ui_test_utils.h"
     19 #include "content/public/browser/web_contents.h"
     20 #include "content/public/test/browser_test_utils.h"
     21 #include "extensions/common/extension.h"
     22 #include "extensions/common/manifest.h"
     23 #include "net/dns/mock_host_resolver.h"
     24 #include "url/gurl.h"
     25 
     26 using extensions::Extension;
     27 
     28 class ChromeAppAPITest : public ExtensionBrowserTest {
     29  protected:
     30   bool IsAppInstalledInMainFrame() {
     31     return IsAppInstalledInFrame(
     32         browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame());
     33   }
     34   bool IsAppInstalledInIFrame() {
     35     return IsAppInstalledInFrame(GetIFrame());
     36   }
     37   bool IsAppInstalledInFrame(content::RenderFrameHost* frame) {
     38     const char kGetAppIsInstalled[] =
     39         "window.domAutomationController.send(window.chrome.app.isInstalled);";
     40     bool result;
     41     CHECK(content::ExecuteScriptAndExtractBool(frame,
     42                                                kGetAppIsInstalled,
     43                                                &result));
     44     return result;
     45   }
     46 
     47   std::string InstallStateInMainFrame() {
     48     return InstallStateInFrame(
     49         browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame());
     50   }
     51   std::string InstallStateInIFrame() {
     52     return InstallStateInFrame(GetIFrame());
     53   }
     54   std::string InstallStateInFrame(content::RenderFrameHost* frame) {
     55     const char kGetAppInstallState[] =
     56         "window.chrome.app.installState("
     57         "    function(s) { window.domAutomationController.send(s); });";
     58     std::string result;
     59     CHECK(content::ExecuteScriptAndExtractString(frame,
     60                                                  kGetAppInstallState,
     61                                                  &result));
     62     return result;
     63   }
     64 
     65   std::string RunningStateInMainFrame() {
     66     return RunningStateInFrame(
     67         browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame());
     68   }
     69   std::string RunningStateInIFrame() {
     70     return RunningStateInFrame(GetIFrame());
     71   }
     72   std::string RunningStateInFrame(content::RenderFrameHost* frame) {
     73     const char kGetAppRunningState[] =
     74         "window.domAutomationController.send("
     75         "    window.chrome.app.runningState());";
     76     std::string result;
     77     CHECK(content::ExecuteScriptAndExtractString(frame,
     78                                                  kGetAppRunningState,
     79                                                  &result));
     80     return result;
     81   }
     82 
     83  private:
     84   content::RenderFrameHost* GetIFrame() {
     85     return content::FrameMatchingPredicate(
     86         browser()->tab_strip_model()->GetActiveWebContents(),
     87         base::Bind(&content::FrameIsChildOfMainFrame));
     88   }
     89 
     90   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
     91     ExtensionBrowserTest::SetUpCommandLine(command_line);
     92     command_line->AppendSwitchASCII(switches::kAppsCheckoutURL,
     93                                     "http://checkout.com:");
     94   }
     95 };
     96 
     97 // Flaky http://crbug.com/238674
     98 #if defined(OS_WIN)
     99 #define MAYBE_IsInstalled DISABLED_IsInstalled
    100 #else
    101 #define MAYBE_IsInstalled IsInstalled
    102 #endif
    103 IN_PROC_BROWSER_TEST_F(ChromeAppAPITest, MAYBE_IsInstalled) {
    104   std::string app_host("app.com");
    105   std::string nonapp_host("nonapp.com");
    106 
    107   host_resolver()->AddRule(app_host, "127.0.0.1");
    108   host_resolver()->AddRule(nonapp_host, "127.0.0.1");
    109   ASSERT_TRUE(test_server()->Start());
    110 
    111   GURL test_file_url(test_server()->GetURL("extensions/test_file.html"));
    112   GURL::Replacements replace_host;
    113 
    114   replace_host.SetHostStr(app_host);
    115   GURL app_url(test_file_url.ReplaceComponents(replace_host));
    116 
    117   replace_host.SetHostStr(nonapp_host);
    118   GURL non_app_url(test_file_url.ReplaceComponents(replace_host));
    119 
    120   // Before the app is installed, app.com does not think that it is installed
    121   ui_test_utils::NavigateToURL(browser(), app_url);
    122   EXPECT_FALSE(IsAppInstalledInMainFrame());
    123 
    124   // Load an app which includes app.com in its extent.
    125   const Extension* extension = LoadExtension(
    126       test_data_dir_.AppendASCII("app_dot_com_app"));
    127   ASSERT_TRUE(extension);
    128 
    129   // Even after the app is installed, the existing app.com tab is not in an
    130   // app process, so chrome.app.isInstalled should return false.
    131   EXPECT_FALSE(IsAppInstalledInMainFrame());
    132 
    133   // Test that a non-app page has chrome.app.isInstalled = false.
    134   ui_test_utils::NavigateToURL(browser(), non_app_url);
    135   EXPECT_FALSE(IsAppInstalledInMainFrame());
    136 
    137   // Test that a non-app page returns null for chrome.app.getDetails().
    138   const char kGetAppDetails[] =
    139       "window.domAutomationController.send("
    140       "    JSON.stringify(window.chrome.app.getDetails()));";
    141   std::string result;
    142   ASSERT_TRUE(
    143       content::ExecuteScriptAndExtractString(
    144           browser()->tab_strip_model()->GetActiveWebContents(),
    145           kGetAppDetails,
    146           &result));
    147   EXPECT_EQ("null", result);
    148 
    149   // Check that an app page has chrome.app.isInstalled = true.
    150   ui_test_utils::NavigateToURL(browser(), app_url);
    151   EXPECT_TRUE(IsAppInstalledInMainFrame());
    152 
    153   // Check that an app page returns the correct result for
    154   // chrome.app.getDetails().
    155   ui_test_utils::NavigateToURL(browser(), app_url);
    156   ASSERT_TRUE(
    157       content::ExecuteScriptAndExtractString(
    158           browser()->tab_strip_model()->GetActiveWebContents(),
    159           kGetAppDetails,
    160           &result));
    161   scoped_ptr<base::DictionaryValue> app_details(
    162       static_cast<base::DictionaryValue*>(base::JSONReader::Read(result)));
    163   // extension->manifest() does not contain the id.
    164   app_details->Remove("id", NULL);
    165   EXPECT_TRUE(app_details.get());
    166   EXPECT_TRUE(app_details->Equals(extension->manifest()->value()));
    167 
    168   // Try to change app.isInstalled.  Should silently fail, so
    169   // that isInstalled should have the initial value.
    170   ASSERT_TRUE(
    171       content::ExecuteScriptAndExtractString(
    172           browser()->tab_strip_model()->GetActiveWebContents(),
    173           "window.domAutomationController.send("
    174           "    function() {"
    175           "        var value = window.chrome.app.isInstalled;"
    176           "        window.chrome.app.isInstalled = !value;"
    177           "        if (window.chrome.app.isInstalled == value) {"
    178           "            return 'true';"
    179           "        } else {"
    180           "            return 'false';"
    181           "        }"
    182           "    }()"
    183           ");",
    184           &result));
    185 
    186   // Should not be able to alter window.chrome.app.isInstalled from javascript";
    187   EXPECT_EQ("true", result);
    188 }
    189 
    190 IN_PROC_BROWSER_TEST_F(ChromeAppAPITest, GetDetailsForFrame) {
    191   std::string app_host("app.com");
    192   std::string nonapp_host("nonapp.com");
    193   std::string checkout_host("checkout.com");
    194 
    195   host_resolver()->AddRule(app_host, "127.0.0.1");
    196   host_resolver()->AddRule(nonapp_host, "127.0.0.1");
    197   host_resolver()->AddRule(checkout_host, "127.0.0.1");
    198   ASSERT_TRUE(test_server()->Start());
    199 
    200   GURL test_file_url(test_server()->GetURL(
    201       "files/extensions/get_app_details_for_frame.html"));
    202   GURL::Replacements replace_host;
    203 
    204   replace_host.SetHostStr(checkout_host);
    205   GURL checkout_url(test_file_url.ReplaceComponents(replace_host));
    206 
    207   replace_host.SetHostStr(app_host);
    208   GURL app_url(test_file_url.ReplaceComponents(replace_host));
    209 
    210   // Load an app which includes app.com in its extent.
    211   const Extension* extension = LoadExtension(
    212       test_data_dir_.AppendASCII("app_dot_com_app"));
    213   ASSERT_TRUE(extension);
    214 
    215   // Test that normal pages (even apps) cannot use getDetailsForFrame().
    216   ui_test_utils::NavigateToURL(browser(), app_url);
    217   const char kTestUnsuccessfulAccess[] =
    218       "window.domAutomationController.send(window.testUnsuccessfulAccess())";
    219   bool result = false;
    220   ASSERT_TRUE(
    221       content::ExecuteScriptAndExtractBool(
    222           browser()->tab_strip_model()->GetActiveWebContents(),
    223           kTestUnsuccessfulAccess,
    224           &result));
    225   EXPECT_TRUE(result);
    226 
    227   // Test that checkout can use getDetailsForFrame() and that it works
    228   // correctly.
    229   ui_test_utils::NavigateToURL(browser(), checkout_url);
    230   const char kGetDetailsForFrame[] =
    231       "window.domAutomationController.send("
    232       "    JSON.stringify(chrome.app.getDetailsForFrame(frames[0])))";
    233   std::string json;
    234   ASSERT_TRUE(
    235       content::ExecuteScriptAndExtractString(
    236           browser()->tab_strip_model()->GetActiveWebContents(),
    237           kGetDetailsForFrame,
    238           &json));
    239 
    240   scoped_ptr<base::DictionaryValue> app_details(
    241       static_cast<base::DictionaryValue*>(base::JSONReader::Read(json)));
    242   // extension->manifest() does not contain the id.
    243   app_details->Remove("id", NULL);
    244   EXPECT_TRUE(app_details.get());
    245   EXPECT_TRUE(app_details->Equals(extension->manifest()->value()));
    246 }
    247 
    248 
    249 IN_PROC_BROWSER_TEST_F(ChromeAppAPITest, InstallAndRunningState) {
    250   std::string app_host("app.com");
    251   std::string non_app_host("nonapp.com");
    252 
    253   host_resolver()->AddRule(app_host, "127.0.0.1");
    254   host_resolver()->AddRule(non_app_host, "127.0.0.1");
    255   ASSERT_TRUE(test_server()->Start());
    256 
    257   GURL test_file_url(test_server()->GetURL(
    258       "files/extensions/get_app_details_for_frame.html"));
    259   GURL::Replacements replace_host;
    260 
    261   replace_host.SetHostStr(app_host);
    262   GURL app_url(test_file_url.ReplaceComponents(replace_host));
    263 
    264   replace_host.SetHostStr(non_app_host);
    265   GURL non_app_url(test_file_url.ReplaceComponents(replace_host));
    266 
    267   // Before the app is installed, app.com does not think that it is installed
    268   ui_test_utils::NavigateToURL(browser(), app_url);
    269 
    270   EXPECT_EQ("not_installed", InstallStateInMainFrame());
    271   EXPECT_EQ("cannot_run", RunningStateInMainFrame());
    272   EXPECT_FALSE(IsAppInstalledInMainFrame());
    273 
    274   const Extension* extension = LoadExtension(
    275       test_data_dir_.AppendASCII("app_dot_com_app"));
    276   ASSERT_TRUE(extension);
    277 
    278   EXPECT_EQ("installed", InstallStateInMainFrame());
    279   EXPECT_EQ("ready_to_run", RunningStateInMainFrame());
    280   EXPECT_FALSE(IsAppInstalledInMainFrame());
    281 
    282   // Reloading the page should put the tab in an app process.
    283   ui_test_utils::NavigateToURL(browser(), app_url);
    284   EXPECT_EQ("installed", InstallStateInMainFrame());
    285   EXPECT_EQ("running", RunningStateInMainFrame());
    286   EXPECT_TRUE(IsAppInstalledInMainFrame());
    287 
    288   // Disable the extension and verify the state.
    289   browser()->profile()->GetExtensionService()->DisableExtension(
    290       extension->id(), Extension::DISABLE_PERMISSIONS_INCREASE);
    291   ui_test_utils::NavigateToURL(browser(), app_url);
    292 
    293   EXPECT_EQ("disabled", InstallStateInMainFrame());
    294   EXPECT_EQ("cannot_run", RunningStateInMainFrame());
    295   EXPECT_FALSE(IsAppInstalledInMainFrame());
    296 
    297   browser()->profile()->GetExtensionService()->EnableExtension(extension->id());
    298   EXPECT_EQ("installed", InstallStateInMainFrame());
    299   EXPECT_EQ("ready_to_run", RunningStateInMainFrame());
    300   EXPECT_FALSE(IsAppInstalledInMainFrame());
    301 
    302   // The non-app URL should still not be installed or running.
    303   ui_test_utils::NavigateToURL(browser(), non_app_url);
    304 
    305   EXPECT_EQ("not_installed", InstallStateInMainFrame());
    306   EXPECT_EQ("cannot_run", RunningStateInMainFrame());
    307   EXPECT_FALSE(IsAppInstalledInMainFrame());
    308 
    309   EXPECT_EQ("installed", InstallStateInIFrame());
    310   EXPECT_EQ("cannot_run", RunningStateInIFrame());
    311   EXPECT_FALSE(IsAppInstalledInIFrame());
    312 
    313 }
    314 
    315 IN_PROC_BROWSER_TEST_F(ChromeAppAPITest, InstallAndRunningStateFrame) {
    316   std::string app_host("app.com");
    317   std::string non_app_host("nonapp.com");
    318 
    319   host_resolver()->AddRule(app_host, "127.0.0.1");
    320   host_resolver()->AddRule(non_app_host, "127.0.0.1");
    321   ASSERT_TRUE(test_server()->Start());
    322 
    323   GURL test_file_url(test_server()->GetURL(
    324       "files/extensions/get_app_details_for_frame_reversed.html"));
    325   GURL::Replacements replace_host;
    326 
    327   replace_host.SetHostStr(app_host);
    328   GURL app_url(test_file_url.ReplaceComponents(replace_host));
    329 
    330   replace_host.SetHostStr(non_app_host);
    331   GURL non_app_url(test_file_url.ReplaceComponents(replace_host));
    332 
    333   // Check the install and running state of a non-app iframe running
    334   // within an app.
    335   ui_test_utils::NavigateToURL(browser(), app_url);
    336 
    337   EXPECT_EQ("not_installed", InstallStateInIFrame());
    338   EXPECT_EQ("cannot_run", RunningStateInIFrame());
    339   EXPECT_FALSE(IsAppInstalledInIFrame());
    340 }
    341