blob: 2196c5520be7c08bc62c9fc821eae999fb58ba37 [file] [log] [blame]
kalman30a92f962015-09-03 18:08:201// Copyright 2015 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
Yutaka Hirano54b166eb2019-12-05 20:59:525#include "base/bind.h"
kalman30a92f962015-09-03 18:08:206#include "base/files/file_path.h"
7#include "base/strings/stringprintf.h"
8#include "chrome/browser/extensions/extension_apitest.h"
thestige80821242015-09-30 23:46:089#include "chrome/browser/ui/browser_navigator_params.h"
kalman30a92f962015-09-03 18:08:2010#include "chrome/browser/ui/tabs/tab_strip_model.h"
11#include "chrome/test/base/ui_test_utils.h"
12#include "content/public/browser/web_contents.h"
13#include "content/public/test/browser_test_utils.h"
14#include "extensions/common/extension.h"
Devlin Cronin4f455a22018-01-25 01:36:4515#include "extensions/test/test_extension_dir.h"
kalman30a92f962015-09-03 18:08:2016#include "net/dns/mock_host_resolver.h"
17#include "net/test/embedded_test_server/embedded_test_server.h"
Yutaka Hirano54b166eb2019-12-05 20:59:5218#include "net/test/embedded_test_server/http_request.h"
19#include "net/test/embedded_test_server/http_response.h"
20#include "services/network/public/cpp/features.h"
kalman30a92f962015-09-03 18:08:2021
22namespace extensions {
23
24namespace {
25
Yutaka Hirano54b166eb2019-12-05 20:59:5226// Returns a response whose body is request's origin.
27std::unique_ptr<net::test_server::HttpResponse> HandleEchoOrigin(
28 const net::test_server::HttpRequest& request) {
29 if (request.relative_url != "/echo-origin")
30 return nullptr;
31
32 auto response = std::make_unique<net::test_server::BasicHttpResponse>();
33 response->set_code(net::HTTP_OK);
34 response->set_content_type("text/plain");
35 auto it = request.headers.find("origin");
36 if (it != request.headers.end()) {
37 response->set_content(it->second);
38 } else {
39 response->set_content("<no origin attached>");
40 }
41 response->AddCustomHeader("access-control-allow-origin", "*");
42
43 return response;
44}
45
kalman30a92f962015-09-03 18:08:2046// JavaScript snippet which performs a fetch given a URL expression to be
47// substituted as %s, then sends back the fetched content using the
48// domAutomationController.
49const char* kFetchScript =
50 "fetch(%s).then(function(result) {\n"
51 " return result.text();\n"
52 "}).then(function(text) {\n"
53 " window.domAutomationController.send(text);\n"
54 "}).catch(function(err) {\n"
55 " window.domAutomationController.send(String(err));\n"
56 "});\n";
57
Yutaka Hirano54b166eb2019-12-05 20:59:5258constexpr char kFetchPostScript[] = R"(
59 fetch($1, {method: 'POST'}).then((result) => {
60 return result.text();
61 }).then((text) => {
62 window.domAutomationController.send(text);
63 }).catch((error) => {
64 window.domAutomationController.send(String(err));
65 });
66)";
67
kalman30a92f962015-09-03 18:08:2068class ExtensionFetchTest : public ExtensionApiTest {
69 protected:
70 // Writes an empty background page and a text file called "text" with content
71 // "text content", then loads and returns the extension. |dir| must already
72 // have a manifest.
73 const Extension* WriteFilesAndLoadTestExtension(TestExtensionDir* dir) {
74 dir->WriteFile(FILE_PATH_LITERAL("text"), "text content");
75 dir->WriteFile(FILE_PATH_LITERAL("bg.js"), "");
vabr9142fe22016-09-08 13:19:2276 return LoadExtension(dir->UnpackedPath());
kalman30a92f962015-09-03 18:08:2077 }
78
79 // Returns |kFetchScript| with |url_expression| substituted as its test URL.
80 std::string GetFetchScript(const std::string& url_expression) {
81 return base::StringPrintf(kFetchScript, url_expression.c_str());
82 }
83
84 // Returns |url| as a string surrounded by single quotes, for passing to
85 // JavaScript as a string literal.
86 std::string GetQuotedURL(const GURL& url) {
87 return base::StringPrintf("'%s'", url.spec().c_str());
88 }
89
90 // Like GetQuotedURL(), but fetching the URL from the test server's |host|
91 // and |path|.
92 std::string GetQuotedTestServerURL(const std::string& host,
93 const std::string& path) {
94 return GetQuotedURL(embedded_test_server()->GetURL(host, path));
95 }
96
97 // Opens a tab, puts it in the foreground, navigates it to |url| then returns
98 // its WebContents.
99 content::WebContents* CreateAndNavigateTab(const GURL& url) {
cm.sanchi2522bc92017-12-04 08:04:13100 NavigateParams params(browser(), url, ui::PAGE_TRANSITION_LINK);
nick3b04f322016-08-31 19:29:19101 params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
kalman30a92f962015-09-03 18:08:20102 ui_test_utils::NavigateToURL(&params);
103 return browser()->tab_strip_model()->GetActiveWebContents();
104 }
105
106 private:
107 void SetUpOnMainThread() override {
108 ExtensionApiTest::SetUpOnMainThread();
109 host_resolver()->AddRule("*", "127.0.0.1");
Yutaka Hirano54b166eb2019-12-05 20:59:52110
111 embedded_test_server()->RegisterRequestHandler(
112 base::BindRepeating(HandleEchoOrigin));
kalman30a92f962015-09-03 18:08:20113 ASSERT_TRUE(StartEmbeddedTestServer());
114 }
115};
116
117IN_PROC_BROWSER_TEST_F(ExtensionFetchTest, ExtensionCanFetchExtensionResource) {
118 TestExtensionDir dir;
119 dir.WriteManifestWithSingleQuotes(
120 "{"
121 "'background': {'scripts': ['bg.js']},"
122 "'manifest_version': 2,"
123 "'name': 'ExtensionCanFetchExtensionResource',"
124 "'version': '1'"
125 "}");
126 const Extension* extension = WriteFilesAndLoadTestExtension(&dir);
127 ASSERT_TRUE(extension);
128
129 EXPECT_EQ(
130 "text content",
131 ExecuteScriptInBackgroundPage(
132 extension->id(), GetFetchScript("chrome.runtime.getURL('text')")));
133}
134
135IN_PROC_BROWSER_TEST_F(ExtensionFetchTest,
136 ExtensionCanFetchHostedResourceWithHostPermissions) {
137 TestExtensionDir dir;
138 dir.WriteManifestWithSingleQuotes(
139 "{"
140 "'background': {'scripts': ['bg.js']},"
141 "'manifest_version': 2,"
142 "'name': 'ExtensionCanFetchHostedResourceWithHostPermissions',"
143 "'permissions': ['http://example.com/*'],"
144 "'version': '1'"
145 "}");
146 const Extension* extension = WriteFilesAndLoadTestExtension(&dir);
147 ASSERT_TRUE(extension);
148
149 EXPECT_EQ("Hello!", ExecuteScriptInBackgroundPage(
150 extension->id(),
151 GetFetchScript(GetQuotedTestServerURL(
152 "example.com", "/extensions/test_file.txt"))));
153}
154
155IN_PROC_BROWSER_TEST_F(
156 ExtensionFetchTest,
157 ExtensionCannotFetchHostedResourceWithoutHostPermissions) {
158 TestExtensionDir dir;
159 dir.WriteManifestWithSingleQuotes(
160 "{"
161 "'background': {'scripts': ['bg.js']},"
162 "'manifest_version': 2,"
163 "'name': 'ExtensionCannotFetchHostedResourceWithoutHostPermissions',"
164 "'version': '1'"
165 "}");
166 const Extension* extension = WriteFilesAndLoadTestExtension(&dir);
167 ASSERT_TRUE(extension);
168
169 // TODO(kalman): Another test would be to configure the test server to work
170 // with CORS, and test that the fetch succeeds.
171 EXPECT_EQ(
172 "TypeError: Failed to fetch",
173 ExecuteScriptInBackgroundPage(
174 extension->id(), GetFetchScript(GetQuotedTestServerURL(
175 "example.com", "/extensions/test_file.txt"))));
176}
177
178IN_PROC_BROWSER_TEST_F(ExtensionFetchTest,
179 HostCanFetchWebAccessibleExtensionResource) {
180 TestExtensionDir dir;
181 dir.WriteManifestWithSingleQuotes(
182 "{"
183 "'background': {'scripts': ['bg.js']},"
184 "'manifest_version': 2,"
185 "'name': 'HostCanFetchWebAccessibleExtensionResource',"
186 "'version': '1',"
187 "'web_accessible_resources': ['text']"
188 "}");
189 const Extension* extension = WriteFilesAndLoadTestExtension(&dir);
190 ASSERT_TRUE(extension);
191
192 content::WebContents* empty_tab = CreateAndNavigateTab(
193 embedded_test_server()->GetURL("example.com", "/empty.html"));
194
195 // TODO(kalman): Test this from a content script too.
196 std::string fetch_result;
197 ASSERT_TRUE(content::ExecuteScriptAndExtractString(
198 empty_tab,
199 GetFetchScript(GetQuotedURL(extension->GetResourceURL("text"))),
200 &fetch_result));
201 EXPECT_EQ("text content", fetch_result);
202}
203
Makoto Shimazub8a7f58f2018-11-07 19:09:31204// Calling fetch() from a http(s) service worker context to a
205// chrome-extensions:// URL since the loading path in a service worker is
206// different from pages.
207// This is a regression test for https://crbug.com/901443.
208IN_PROC_BROWSER_TEST_F(
209 ExtensionFetchTest,
210 HostCanFetchWebAccessibleExtensionResource_FetchFromServiceWorker) {
211 TestExtensionDir dir;
212 dir.WriteManifestWithSingleQuotes(
213 "{"
214 "'background': {'scripts': ['bg.js']},"
215 "'manifest_version': 2,"
216 "'name': 'HostCanFetchWebAccessibleExtensionResource_"
217 "FetchFromServiceWorker',"
218 "'version': '1',"
219 "'web_accessible_resources': ['text']"
220 "}");
221 const Extension* extension = WriteFilesAndLoadTestExtension(&dir);
222 ASSERT_TRUE(extension);
223
224 content::WebContents* tab =
225 CreateAndNavigateTab(embedded_test_server()->GetURL(
226 "/workers/fetch_from_service_worker.html"));
227 EXPECT_EQ("ready", content::EvalJs(tab, "setup();"));
228 EXPECT_EQ("text content",
229 content::EvalJs(
230 tab, base::StringPrintf(
231 "fetch_from_service_worker('%s');",
232 extension->GetResourceURL("text").spec().c_str())));
233}
234
kalman30a92f962015-09-03 18:08:20235IN_PROC_BROWSER_TEST_F(ExtensionFetchTest,
236 HostCannotFetchNonWebAccessibleExtensionResource) {
237 TestExtensionDir dir;
238 dir.WriteManifestWithSingleQuotes(
239 "{"
240 "'background': {'scripts': ['bg.js']},"
241 "'manifest_version': 2,"
242 "'name': 'HostCannotFetchNonWebAccessibleExtensionResource',"
243 "'version': '1'"
244 "}");
245 const Extension* extension = WriteFilesAndLoadTestExtension(&dir);
246 ASSERT_TRUE(extension);
247
248 content::WebContents* empty_tab = CreateAndNavigateTab(
249 embedded_test_server()->GetURL("example.com", "/empty.html"));
250
251 // TODO(kalman): Test this from a content script too.
252 std::string fetch_result;
253 ASSERT_TRUE(content::ExecuteScriptAndExtractString(
254 empty_tab,
255 GetFetchScript(GetQuotedURL(extension->GetResourceURL("text"))),
256 &fetch_result));
257 EXPECT_EQ("TypeError: Failed to fetch", fetch_result);
258}
259
Yutaka Hirano7938c712019-10-15 06:00:42260IN_PROC_BROWSER_TEST_F(ExtensionFetchTest, FetchResponseType) {
261 const std::string script = base::StringPrintf(
262 "fetch(%s).then(function(response) {\n"
263 " window.domAutomationController.send(response.type);\n"
264 "}).catch(function(err) {\n"
265 " window.domAutomationController.send(String(err));\n"
266 "});\n",
267 GetQuotedTestServerURL("example.com", "/extensions/test_file.txt")
268 .data());
269 TestExtensionDir dir;
270 dir.WriteManifestWithSingleQuotes(
271 "{"
272 "'background': {'scripts': ['bg.js']},"
273 "'manifest_version': 2,"
274 "'name': 'FetchResponseType',"
275 "'permissions': ['http://example.com/*'],"
276 "'version': '1'"
277 "}");
278 const Extension* extension = WriteFilesAndLoadTestExtension(&dir);
279 ASSERT_TRUE(extension);
280
281 EXPECT_EQ("basic", ExecuteScriptInBackgroundPage(extension->id(), script));
282}
283
Yutaka Hirano54b166eb2019-12-05 20:59:52284IN_PROC_BROWSER_TEST_F(ExtensionFetchTest, OriginOnPostWithPermissions) {
285 TestExtensionDir dir;
286 dir.WriteManifest(R"JSON(
287 {
288 "background": {"scripts": ["bg.js"]},
289 "manifest_version": 2,
290 "name": "FetchResponseType",
291 "permissions": ["http://example.com/*"],
292 "version": "1"
293 })JSON");
294 const Extension* extension = WriteFilesAndLoadTestExtension(&dir);
295 ASSERT_TRUE(extension);
296
297 // Extension scripts will have the origin of the destination URL only when
298 // OOR-CORS is enabled.
299 GURL destination_url =
300 embedded_test_server()->GetURL("example.com", "/echo-origin");
301 std::string script = content::JsReplace(kFetchPostScript, destination_url);
302 std::string origin_string =
303 network::features::ShouldEnableOutOfBlinkCorsForTesting()
304 ? url::Origin::Create(destination_url).Serialize()
305 : url::Origin::Create(extension->url()).Serialize();
306 EXPECT_EQ(origin_string,
307 ExecuteScriptInBackgroundPage(extension->id(), script));
308}
309
310IN_PROC_BROWSER_TEST_F(ExtensionFetchTest, OriginOnPostWithoutPermissions) {
311 TestExtensionDir dir;
312 dir.WriteManifest(R"JSON(
313 {
314 "background": {"scripts": ["bg.js"]},
315 "manifest_version": 2,
316 "name": "FetchResponseType",
317 "permissions": [],
318 "version": "1"
319 })JSON");
320 const Extension* extension = WriteFilesAndLoadTestExtension(&dir);
321 ASSERT_TRUE(extension);
322
323 const std::string script = content::JsReplace(
324 kFetchPostScript,
325 embedded_test_server()->GetURL("example.com", "/echo-origin"));
326 EXPECT_EQ(url::Origin::Create(extension->url()).Serialize(),
327 ExecuteScriptInBackgroundPage(extension->id(), script));
328}
329
kalman30a92f962015-09-03 18:08:20330} // namespace
331
332} // namespace extensions