chrome_app_api_browsertest.cc revision effb81e5f8246d0db0270817048dc992db66e9fb
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
26using extensions::Extension;
27
28class 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
103IN_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
190IN_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
249IN_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
315IN_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