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/common/extensions/extension.h"
19#include "chrome/common/extensions/manifest.h"
20#include "chrome/test/base/ui_test_utils.h"
21#include "content/public/browser/web_contents.h"
22#include "content/public/test/browser_test_utils.h"
23#include "net/dns/mock_host_resolver.h"
24#include "url/gurl.h"
25
26using extensions::Extension;
27
28class 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
88IN_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
175IN_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
234IN_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
300IN_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