blob: 91fcc95f42855fded9f46adbe2d4fd43a34b2d6c [file] [log] [blame]
[email protected]f2fe87c2012-04-24 17:53:491// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]a964e112011-04-14 21:52:512// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
dchengc963c7142016-04-08 03:55:225#include <memory>
[email protected]a964e112011-04-14 21:52:516#include <string>
7
Sebastien Marchandf1349f52019-01-25 03:16:418#include "base/bind.h"
[email protected]a964e112011-04-14 21:52:519#include "base/command_line.h"
10#include "base/json/json_reader.h"
[email protected]3ea1b182013-02-08 22:38:4111#include "base/strings/string_number_conversions.h"
Lei Zhangfe5b86932019-02-01 17:26:5912#include "base/strings/stringprintf.h"
[email protected]a964e112011-04-14 21:52:5113#include "base/values.h"
14#include "chrome/browser/extensions/extension_browsertest.h"
[email protected]f2fe87c2012-04-24 17:53:4915#include "chrome/browser/extensions/extension_service.h"
16#include "chrome/browser/profiles/profile.h"
[email protected]a964e112011-04-14 21:52:5117#include "chrome/browser/ui/browser.h"
[email protected]47ae23372013-01-29 01:50:4818#include "chrome/browser/ui/tabs/tab_strip_model.h"
[email protected]af44e7fb2011-07-29 18:32:3219#include "chrome/test/base/ui_test_utils.h"
[email protected]4ca15302012-01-03 05:53:2020#include "content/public/browser/web_contents.h"
Dave Tapuskad46d6fd62022-05-18 21:35:5121#include "content/public/common/content_features.h"
Peter Kasting919ce652020-05-07 10:22:3622#include "content/public/test/browser_test.h"
[email protected]7d478cb2012-07-24 17:19:4223#include "content/public/test/browser_test_utils.h"
[email protected]e4452d32013-11-15 23:07:4124#include "extensions/common/extension.h"
[email protected]d42c11152013-08-22 19:36:3225#include "extensions/common/manifest.h"
[email protected]f2cb3cf2013-03-21 01:40:5326#include "net/dns/mock_host_resolver.h"
nickfa2254b2015-08-07 23:27:2927#include "net/test/embedded_test_server/embedded_test_server.h"
Dave Tapuskad46d6fd62022-05-18 21:35:5128#include "third_party/blink/public/common/features.h"
[email protected]a6483d22013-07-03 22:11:0029#include "url/gurl.h"
[email protected]a964e112011-04-14 21:52:5130
[email protected]1c321ee2012-05-21 03:02:3431using extensions::Extension;
32
Devlin Cronin836f545d2018-05-09 00:25:0533class ChromeAppAPITest : public extensions::ExtensionBrowserTest {
Dave Tapuskad46d6fd62022-05-18 21:35:5134 protected:
jambb11ed742017-05-01 17:27:5935 void SetUpOnMainThread() override {
Devlin Cronin836f545d2018-05-09 00:25:0536 extensions::ExtensionBrowserTest::SetUpOnMainThread();
jambb11ed742017-05-01 17:27:5937 host_resolver()->AddRule("*", "127.0.0.1");
38 ASSERT_TRUE(embedded_test_server()->Start());
39 }
40
[email protected]b27c3622014-03-22 00:26:5541 bool IsAppInstalledInMainFrame() {
42 return IsAppInstalledInFrame(
43 browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame());
44 }
45 bool IsAppInstalledInIFrame() {
46 return IsAppInstalledInFrame(GetIFrame());
47 }
48 bool IsAppInstalledInFrame(content::RenderFrameHost* frame) {
[email protected]06bc5d92013-01-02 22:44:1349 const char kGetAppIsInstalled[] =
50 "window.domAutomationController.send(window.chrome.app.isInstalled);";
[email protected]80675fc2011-06-21 02:05:4951 bool result;
[email protected]b27c3622014-03-22 00:26:5552 CHECK(content::ExecuteScriptAndExtractBool(frame,
53 kGetAppIsInstalled,
54 &result));
[email protected]f2fe87c2012-04-24 17:53:4955 return result;
56 }
57
[email protected]b27c3622014-03-22 00:26:5558 std::string InstallStateInMainFrame() {
59 return InstallStateInFrame(
60 browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame());
61 }
62 std::string InstallStateInIFrame() {
63 return InstallStateInFrame(GetIFrame());
64 }
65 std::string InstallStateInFrame(content::RenderFrameHost* frame) {
[email protected]06bc5d92013-01-02 22:44:1366 const char kGetAppInstallState[] =
67 "window.chrome.app.installState("
68 " function(s) { window.domAutomationController.send(s); });";
[email protected]f2fe87c2012-04-24 17:53:4969 std::string result;
[email protected]b27c3622014-03-22 00:26:5570 CHECK(content::ExecuteScriptAndExtractString(frame,
71 kGetAppInstallState,
72 &result));
[email protected]f2fe87c2012-04-24 17:53:4973 return result;
74 }
75
[email protected]b27c3622014-03-22 00:26:5576 std::string RunningStateInMainFrame() {
77 return RunningStateInFrame(
78 browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame());
79 }
80 std::string RunningStateInIFrame() {
81 return RunningStateInFrame(GetIFrame());
82 }
83 std::string RunningStateInFrame(content::RenderFrameHost* frame) {
[email protected]06bc5d92013-01-02 22:44:1384 const char kGetAppRunningState[] =
85 "window.domAutomationController.send("
86 " window.chrome.app.runningState());";
[email protected]f2fe87c2012-04-24 17:53:4987 std::string result;
[email protected]b27c3622014-03-22 00:26:5588 CHECK(content::ExecuteScriptAndExtractString(frame,
89 kGetAppRunningState,
90 &result));
[email protected]80675fc2011-06-21 02:05:4991 return result;
92 }
93
[email protected]a964e112011-04-14 21:52:5194 private:
[email protected]b27c3622014-03-22 00:26:5595 content::RenderFrameHost* GetIFrame() {
96 return content::FrameMatchingPredicate(
David Bokan2c77c33c2021-07-26 23:29:5997 browser()->tab_strip_model()->GetActiveWebContents()->GetPrimaryPage(),
Alex Turnerd9f1b0f2020-10-21 00:05:5098 base::BindRepeating(&content::FrameIsChildOfMainFrame));
[email protected]b27c3622014-03-22 00:26:5599 }
[email protected]a964e112011-04-14 21:52:51100};
101
nickfa2254b2015-08-07 23:27:29102IN_PROC_BROWSER_TEST_F(ChromeAppAPITest, IsInstalled) {
nickfa2254b2015-08-07 23:27:29103 GURL app_url =
104 embedded_test_server()->GetURL("app.com", "/extensions/test_file.html");
105 GURL non_app_url = embedded_test_server()->GetURL(
106 "nonapp.com", "/extensions/test_file.html");
[email protected]a964e112011-04-14 21:52:51107
[email protected]80675fc2011-06-21 02:05:49108 // Before the app is installed, app.com does not think that it is installed
Lukasz Anforowiczb78290c2021-09-08 04:31:38109 ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), app_url));
[email protected]b27c3622014-03-22 00:26:55110 EXPECT_FALSE(IsAppInstalledInMainFrame());
[email protected]a964e112011-04-14 21:52:51111
112 // Load an app which includes app.com in its extent.
113 const Extension* extension = LoadExtension(
114 test_data_dir_.AppendASCII("app_dot_com_app"));
115 ASSERT_TRUE(extension);
116
[email protected]80675fc2011-06-21 02:05:49117 // Even after the app is installed, the existing app.com tab is not in an
118 // app process, so chrome.app.isInstalled should return false.
[email protected]b27c3622014-03-22 00:26:55119 EXPECT_FALSE(IsAppInstalledInMainFrame());
[email protected]a964e112011-04-14 21:52:51120
121 // Test that a non-app page has chrome.app.isInstalled = false.
Lukasz Anforowiczb78290c2021-09-08 04:31:38122 ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), non_app_url));
[email protected]b27c3622014-03-22 00:26:55123 EXPECT_FALSE(IsAppInstalledInMainFrame());
[email protected]a964e112011-04-14 21:52:51124
125 // Test that a non-app page returns null for chrome.app.getDetails().
[email protected]06bc5d92013-01-02 22:44:13126 const char kGetAppDetails[] =
127 "window.domAutomationController.send("
128 " JSON.stringify(window.chrome.app.getDetails()));";
[email protected]80675fc2011-06-21 02:05:49129 std::string result;
[email protected]a964e112011-04-14 21:52:51130 ASSERT_TRUE(
[email protected]b6987e02013-01-04 18:30:43131 content::ExecuteScriptAndExtractString(
[email protected]47ae23372013-01-29 01:50:48132 browser()->tab_strip_model()->GetActiveWebContents(),
[email protected]06bc5d92013-01-02 22:44:13133 kGetAppDetails,
134 &result));
[email protected]a964e112011-04-14 21:52:51135 EXPECT_EQ("null", result);
136
137 // Check that an app page has chrome.app.isInstalled = true.
Lukasz Anforowiczb78290c2021-09-08 04:31:38138 ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), app_url));
[email protected]b27c3622014-03-22 00:26:55139 EXPECT_TRUE(IsAppInstalledInMainFrame());
[email protected]a964e112011-04-14 21:52:51140
141 // Check that an app page returns the correct result for
142 // chrome.app.getDetails().
Lukasz Anforowiczb78290c2021-09-08 04:31:38143 ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), app_url));
[email protected]a964e112011-04-14 21:52:51144 ASSERT_TRUE(
[email protected]b6987e02013-01-04 18:30:43145 content::ExecuteScriptAndExtractString(
[email protected]47ae23372013-01-29 01:50:48146 browser()->tab_strip_model()->GetActiveWebContents(),
[email protected]06bc5d92013-01-02 22:44:13147 kGetAppDetails,
148 &result));
dchengc963c7142016-04-08 03:55:22149 std::unique_ptr<base::DictionaryValue> app_details(
estadeb09312b2015-05-22 16:30:13150 static_cast<base::DictionaryValue*>(
Lei Zhang582ecd12019-02-13 20:28:54151 base::JSONReader::ReadDeprecated(result).release()));
[email protected]953620b2011-12-04 00:55:32152 // extension->manifest() does not contain the id.
Song Fangzhencdbb7062021-07-07 11:13:53153 app_details->RemoveKey("id");
[email protected]a964e112011-04-14 21:52:51154 EXPECT_TRUE(app_details.get());
Ghazale Hosseinabadi1830ef22022-02-08 18:44:35155 EXPECT_EQ(*app_details, *extension->manifest()->value());
[email protected]a964e112011-04-14 21:52:51156
[email protected]9b3a5c22011-05-31 08:03:13157 // Try to change app.isInstalled. Should silently fail, so
158 // that isInstalled should have the initial value.
159 ASSERT_TRUE(
[email protected]b6987e02013-01-04 18:30:43160 content::ExecuteScriptAndExtractString(
[email protected]47ae23372013-01-29 01:50:48161 browser()->tab_strip_model()->GetActiveWebContents(),
[email protected]06bc5d92013-01-02 22:44:13162 "window.domAutomationController.send("
163 " function() {"
164 " var value = window.chrome.app.isInstalled;"
165 " window.chrome.app.isInstalled = !value;"
166 " if (window.chrome.app.isInstalled == value) {"
167 " return 'true';"
168 " } else {"
169 " return 'false';"
170 " }"
171 " }()"
172 ");",
173 &result));
[email protected]9b3a5c22011-05-31 08:03:13174
175 // Should not be able to alter window.chrome.app.isInstalled from javascript";
176 EXPECT_EQ("true", result);
[email protected]a964e112011-04-14 21:52:51177}
178
Devlin Cronine97d4ed2018-06-29 01:38:50179// Test accessing app.isInstalled when the context has been invalidated (e.g.
180// by removing the frame). Regression test for https://crbug.com/855853.
181IN_PROC_BROWSER_TEST_F(ChromeAppAPITest, IsInstalledFromRemovedFrame) {
182 GURL app_url =
183 embedded_test_server()->GetURL("app.com", "/extensions/test_file.html");
184 const Extension* extension =
185 LoadExtension(test_data_dir_.AppendASCII("app_dot_com_app"));
186 ASSERT_TRUE(extension);
Lukasz Anforowiczb78290c2021-09-08 04:31:38187 ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), app_url));
Devlin Cronine97d4ed2018-06-29 01:38:50188
189 constexpr char kScript[] =
190 R"(var i = document.createElement('iframe');
191 i.onload = function() {
192 var frameApp = i.contentWindow.chrome.app;
193 document.body.removeChild(i);
194 var isInstalled = frameApp.isInstalled;
195 window.domAutomationController.send(
196 isInstalled === undefined);
197 };
198 i.src = '%s';
199 document.body.appendChild(i);)";
200 bool result = false;
201 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
202 browser()->tab_strip_model()->GetActiveWebContents(),
203 base::StringPrintf(kScript, app_url.spec().c_str()), &result));
204 EXPECT_TRUE(result);
205}
206
[email protected]f2fe87c2012-04-24 17:53:49207IN_PROC_BROWSER_TEST_F(ChromeAppAPITest, InstallAndRunningState) {
nickfa2254b2015-08-07 23:27:29208 GURL app_url = embedded_test_server()->GetURL(
209 "app.com", "/extensions/get_app_details_for_frame.html");
210 GURL non_app_url = embedded_test_server()->GetURL(
211 "nonapp.com", "/extensions/get_app_details_for_frame.html");
[email protected]f2fe87c2012-04-24 17:53:49212
213 // Before the app is installed, app.com does not think that it is installed
Lukasz Anforowiczb78290c2021-09-08 04:31:38214 ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), app_url));
[email protected]f2fe87c2012-04-24 17:53:49215
[email protected]b27c3622014-03-22 00:26:55216 EXPECT_EQ("not_installed", InstallStateInMainFrame());
217 EXPECT_EQ("cannot_run", RunningStateInMainFrame());
218 EXPECT_FALSE(IsAppInstalledInMainFrame());
[email protected]f2fe87c2012-04-24 17:53:49219
220 const Extension* extension = LoadExtension(
221 test_data_dir_.AppendASCII("app_dot_com_app"));
222 ASSERT_TRUE(extension);
223
[email protected]b27c3622014-03-22 00:26:55224 EXPECT_EQ("installed", InstallStateInMainFrame());
225 EXPECT_EQ("ready_to_run", RunningStateInMainFrame());
226 EXPECT_FALSE(IsAppInstalledInMainFrame());
[email protected]f2fe87c2012-04-24 17:53:49227
228 // Reloading the page should put the tab in an app process.
Lukasz Anforowiczb78290c2021-09-08 04:31:38229 ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), app_url));
[email protected]b27c3622014-03-22 00:26:55230 EXPECT_EQ("installed", InstallStateInMainFrame());
231 EXPECT_EQ("running", RunningStateInMainFrame());
232 EXPECT_TRUE(IsAppInstalledInMainFrame());
[email protected]f2fe87c2012-04-24 17:53:49233
234 // Disable the extension and verify the state.
Devlin Cronin251bd412018-05-30 00:55:42235 extensions::ExtensionService* service =
236 extensions::ExtensionSystem::Get(browser()->profile())
237 ->extension_service();
Minh X. Nguyen45479012017-08-18 21:35:36238 service->DisableExtension(
239 extension->id(),
240 extensions::disable_reason::DISABLE_PERMISSIONS_INCREASE);
Lukasz Anforowiczb78290c2021-09-08 04:31:38241 ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), app_url));
[email protected]f2fe87c2012-04-24 17:53:49242
[email protected]b27c3622014-03-22 00:26:55243 EXPECT_EQ("disabled", InstallStateInMainFrame());
244 EXPECT_EQ("cannot_run", RunningStateInMainFrame());
245 EXPECT_FALSE(IsAppInstalledInMainFrame());
[email protected]f2fe87c2012-04-24 17:53:49246
[email protected]03d25812014-06-22 19:41:55247 service->EnableExtension(extension->id());
[email protected]b27c3622014-03-22 00:26:55248 EXPECT_EQ("installed", InstallStateInMainFrame());
249 EXPECT_EQ("ready_to_run", RunningStateInMainFrame());
250 EXPECT_FALSE(IsAppInstalledInMainFrame());
[email protected]f2fe87c2012-04-24 17:53:49251
252 // The non-app URL should still not be installed or running.
Lukasz Anforowiczb78290c2021-09-08 04:31:38253 ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), non_app_url));
[email protected]f2fe87c2012-04-24 17:53:49254
[email protected]b27c3622014-03-22 00:26:55255 EXPECT_EQ("not_installed", InstallStateInMainFrame());
256 EXPECT_EQ("cannot_run", RunningStateInMainFrame());
257 EXPECT_FALSE(IsAppInstalledInMainFrame());
[email protected]f2fe87c2012-04-24 17:53:49258
[email protected]b27c3622014-03-22 00:26:55259 EXPECT_EQ("installed", InstallStateInIFrame());
260 EXPECT_EQ("cannot_run", RunningStateInIFrame());
Alex Moshchuke63b9e92017-10-14 00:27:22261
262 // With --site-per-process, the iframe on nonapp.com will currently swap
263 // processes and go into the hosted app process.
264 if (content::AreAllSitesIsolatedForTesting())
265 EXPECT_TRUE(IsAppInstalledInIFrame());
266 else
267 EXPECT_FALSE(IsAppInstalledInIFrame());
[email protected]f2fe87c2012-04-24 17:53:49268}
269
270IN_PROC_BROWSER_TEST_F(ChromeAppAPITest, InstallAndRunningStateFrame) {
nickfa2254b2015-08-07 23:27:29271 GURL app_url = embedded_test_server()->GetURL(
272 "app.com", "/extensions/get_app_details_for_frame_reversed.html");
[email protected]f2fe87c2012-04-24 17:53:49273
274 // Check the install and running state of a non-app iframe running
275 // within an app.
Lukasz Anforowiczb78290c2021-09-08 04:31:38276 ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), app_url));
[email protected]f2fe87c2012-04-24 17:53:49277
[email protected]b27c3622014-03-22 00:26:55278 EXPECT_EQ("not_installed", InstallStateInIFrame());
279 EXPECT_EQ("cannot_run", RunningStateInIFrame());
280 EXPECT_FALSE(IsAppInstalledInIFrame());
[email protected]f2fe87c2012-04-24 17:53:49281}
Dave Tapuskad46d6fd62022-05-18 21:35:51282
283class ChromeAppAPIFencedFrameTest
284 : public ChromeAppAPITest,
285 public testing::WithParamInterface<bool /* shadow_dom_fenced_frame */> {
286 public:
287 ChromeAppAPIFencedFrameTest() {
288 // kPrivacySandboxAdsAPIOverride must also be set since kFencedFrames
289 // cannot be enabled independently without it.
290 feature_list_.InitWithFeaturesAndParameters(
291 {{blink::features::kFencedFrames,
292 {{"implementation_type", GetParam() ? "shadow_dom" : "mparch"}}},
293 {features::kPrivacySandboxAdsAPIsOverride, {}}},
294 {/* disabled_features */});
295 }
296
297 ~ChromeAppAPIFencedFrameTest() override = default;
298
299 void SetUpOnMainThread() override {
300 ChromeAppAPITest::SetUpOnMainThread();
301 https_server()->AddDefaultHandlers(GetChromeTestDataDir());
302 https_server()->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
303 ASSERT_TRUE(https_server()->Start());
304 }
305
306 net::EmbeddedTestServer* https_server() { return &https_server_; }
307
308 private:
309 base::test::ScopedFeatureList feature_list_;
310 net::EmbeddedTestServer https_server_{net::EmbeddedTestServer::TYPE_HTTPS};
311};
312
313IN_PROC_BROWSER_TEST_P(ChromeAppAPIFencedFrameTest, NoInfo) {
314 GURL app_url = https_server()->GetURL(
315 "a.test", "/extensions/get_app_details_for_fenced_frame.html");
316
317 // Check the install and running state of a fenced frame running
318 // within an app.
319 ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), app_url));
320
321 auto render_frame_hosts = CollectAllRenderFrameHosts(
322 browser()->tab_strip_model()->GetActiveWebContents());
323 ASSERT_EQ(2u, render_frame_hosts.size());
324
325 content::RenderFrameHost* fenced_frame = render_frame_hosts.at(1);
326 ASSERT_TRUE(fenced_frame);
327 EXPECT_EQ("cannot_run", RunningStateInFrame(fenced_frame));
328}
329
330INSTANTIATE_TEST_SUITE_P(ChromeAppAPIFencedFrameTest,
331 ChromeAppAPIFencedFrameTest,
332 testing::Bool());