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