Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / manifest / manifest_browsertest.cc
blob0393710aef4f78a50ba3fd1350048c8e5768d306
1 // Copyright 2014 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.
5 #include "base/command_line.h"
6 #include "base/path_service.h"
7 #include "base/strings/string_util.h"
8 #include "base/strings/stringprintf.h"
9 #include "content/public/browser/web_contents.h"
10 #include "content/public/common/content_switches.h"
11 #include "content/public/common/manifest.h"
12 #include "content/public/test/browser_test_utils.h"
13 #include "content/public/test/content_browser_test.h"
14 #include "content/public/test/content_browser_test_utils.h"
15 #include "content/public/test/test_navigation_observer.h"
16 #include "content/shell/browser/shell.h"
17 #include "net/test/embedded_test_server/embedded_test_server.h"
18 #include "net/test/embedded_test_server/http_request.h"
19 #include "net/test/embedded_test_server/http_response.h"
22 namespace content {
24 class ManifestBrowserTest;
26 // Mock of a WebContentsDelegate that catches messages sent to the console.
27 class MockWebContentsDelegate : public WebContentsDelegate {
28 public:
29 MockWebContentsDelegate(WebContents* web_contents, ManifestBrowserTest* test)
30 : web_contents_(web_contents),
31 test_(test) {
34 bool AddMessageToConsole(WebContents* source,
35 int32 level,
36 const base::string16& message,
37 int32 line_no,
38 const base::string16& source_id) override;
40 private:
41 WebContents* web_contents_;
42 ManifestBrowserTest* test_;
45 class ManifestBrowserTest : public ContentBrowserTest {
46 protected:
47 friend MockWebContentsDelegate;
49 ManifestBrowserTest()
50 : console_error_count_(0) {
51 cors_embedded_test_server_.reset(new net::test_server::EmbeddedTestServer);
52 base::FilePath test_data_dir;
53 CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &test_data_dir));
54 cors_embedded_test_server_->ServeFilesFromDirectory(
55 test_data_dir.AppendASCII("content/test/data/"));
58 ~ManifestBrowserTest() override {}
60 void SetUpOnMainThread() override {
61 ContentBrowserTest::SetUpOnMainThread();
62 DCHECK(shell()->web_contents());
64 mock_web_contents_delegate_.reset(
65 new MockWebContentsDelegate(shell()->web_contents(), this));
66 shell()->web_contents()->SetDelegate(mock_web_contents_delegate_.get());
69 void GetManifestAndWait() {
70 shell()->web_contents()->GetManifest(
71 base::Bind(&ManifestBrowserTest::OnGetManifest,
72 base::Unretained(this)));
74 message_loop_runner_ = new MessageLoopRunner();
75 message_loop_runner_->Run();
78 void OnGetManifest(const Manifest& manifest) {
79 manifest_ = manifest;
80 message_loop_runner_->Quit();
83 const Manifest& manifest() const {
84 return manifest_;
87 unsigned int console_error_count() const {
88 return console_error_count_;
91 void OnReceivedConsoleError() {
92 console_error_count_++;
95 net::test_server::EmbeddedTestServer* cors_embedded_test_server() const {
96 return cors_embedded_test_server_.get();
99 private:
100 scoped_refptr<MessageLoopRunner> message_loop_runner_;
101 scoped_ptr<MockWebContentsDelegate> mock_web_contents_delegate_;
102 scoped_ptr<net::test_server::EmbeddedTestServer> cors_embedded_test_server_;
103 Manifest manifest_;
104 int console_error_count_;
106 DISALLOW_COPY_AND_ASSIGN(ManifestBrowserTest);
109 // The implementation of AddMessageToConsole isn't inlined because it needs
110 // to know about |test_|.
111 bool MockWebContentsDelegate::AddMessageToConsole(
112 WebContents* source,
113 int32 level,
114 const base::string16& message,
115 int32 line_no,
116 const base::string16& source_id) {
117 DCHECK(source == web_contents_);
119 if (level == logging::LOG_ERROR)
120 test_->OnReceivedConsoleError();
121 return false;
124 // If a page has no manifest, requesting a manifest should return the empty
125 // manifest.
126 IN_PROC_BROWSER_TEST_F(ManifestBrowserTest, NoManifest) {
127 GURL test_url = GetTestUrl("manifest", "no-manifest.html");
129 TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
130 shell()->LoadURL(test_url);
131 navigation_observer.Wait();
133 GetManifestAndWait();
134 EXPECT_TRUE(manifest().IsEmpty());
135 EXPECT_EQ(0u, console_error_count());
138 // If a page manifest points to a 404 URL, requesting the manifest should return
139 // the empty manifest.
140 IN_PROC_BROWSER_TEST_F(ManifestBrowserTest, 404Manifest) {
141 GURL test_url = GetTestUrl("manifest", "404-manifest.html");
143 TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
144 shell()->LoadURL(test_url);
145 navigation_observer.Wait();
147 GetManifestAndWait();
148 EXPECT_TRUE(manifest().IsEmpty());
149 EXPECT_EQ(0u, console_error_count());
152 // If a page has an empty manifest, requesting the manifest should return the
153 // empty manifest.
154 IN_PROC_BROWSER_TEST_F(ManifestBrowserTest, EmptyManifest) {
155 GURL test_url = GetTestUrl("manifest", "empty-manifest.html");
157 TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
158 shell()->LoadURL(test_url);
159 navigation_observer.Wait();
161 GetManifestAndWait();
162 EXPECT_TRUE(manifest().IsEmpty());
163 EXPECT_EQ(0u, console_error_count());
166 // If a page's manifest can't be parsed correctly, requesting the manifest
167 // should return an empty manifest.
168 IN_PROC_BROWSER_TEST_F(ManifestBrowserTest, ParseErrorManifest) {
169 GURL test_url = GetTestUrl("manifest", "parse-error-manifest.html");
171 TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
172 shell()->LoadURL(test_url);
173 navigation_observer.Wait();
175 GetManifestAndWait();
176 EXPECT_TRUE(manifest().IsEmpty());
177 EXPECT_EQ(1u, console_error_count());
180 // If a page has a manifest that can be fetched and parsed, requesting the
181 // manifest should return a properly filled manifest.
182 IN_PROC_BROWSER_TEST_F(ManifestBrowserTest, DummyManifest) {
183 GURL test_url = GetTestUrl("manifest", "dummy-manifest.html");
185 TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
186 shell()->LoadURL(test_url);
187 navigation_observer.Wait();
189 GetManifestAndWait();
190 EXPECT_FALSE(manifest().IsEmpty());
191 EXPECT_EQ(0u, console_error_count());
194 // If a page changes manifest during its life-time, requesting the manifest
195 // should return the current manifest.
196 IN_PROC_BROWSER_TEST_F(ManifestBrowserTest, DynamicManifest) {
197 GURL test_url = GetTestUrl("manifest", "dynamic-manifest.html");
199 TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
200 shell()->LoadURL(test_url);
201 navigation_observer.Wait();
204 GetManifestAndWait();
205 EXPECT_TRUE(manifest().IsEmpty());
209 std::string manifest_url =
210 GetTestUrl("manifest", "dummy-manifest.json").spec();
211 ASSERT_TRUE(content::ExecuteScript(
212 shell()->web_contents(), "setManifestTo('" + manifest_url + "')"));
214 GetManifestAndWait();
215 EXPECT_FALSE(manifest().IsEmpty());
219 std::string manifest_url =
220 GetTestUrl("manifest", "empty-manifest.json").spec();
221 ASSERT_TRUE(content::ExecuteScript(
222 shell()->web_contents(), "setManifestTo('" + manifest_url + "')"));
224 GetManifestAndWait();
225 EXPECT_TRUE(manifest().IsEmpty());
228 EXPECT_EQ(0u, console_error_count());
231 // If a page's manifest lives in a different origin, it should follow the CORS
232 // rules and requesting the manifest should return an empty manifest (unless the
233 // response contains CORS headers).
234 IN_PROC_BROWSER_TEST_F(ManifestBrowserTest, CORSManifest) {
235 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
236 ASSERT_TRUE(cors_embedded_test_server()->InitializeAndWaitUntilReady());
237 ASSERT_NE(embedded_test_server()->port(),
238 cors_embedded_test_server()->port());
240 GURL test_url =
241 embedded_test_server()->GetURL("/manifest/dynamic-manifest.html");
243 TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
244 shell()->LoadURL(test_url);
245 navigation_observer.Wait();
247 std::string manifest_url = cors_embedded_test_server()->GetURL(
248 "/manifest/dummy-manifest.json").spec();
249 ASSERT_TRUE(content::ExecuteScript(shell()->web_contents(),
250 "setManifestTo('" + manifest_url + "')"));
252 GetManifestAndWait();
253 EXPECT_TRUE(manifest().IsEmpty());
255 EXPECT_EQ(0u, console_error_count());
257 // The purpose of this second load is to make sure the first load is fully
258 // finished. The first load will fail because of Access Control error but the
259 // underlying Blink loader will continue fetching the file. There is no
260 // reliable way to know when the fetch is finished from the browser test
261 // except by fetching the same file from same origin, making it succeed when
262 // it is actually fully loaded.
263 manifest_url =
264 embedded_test_server()->GetURL("/manifest/dummy-manifest.json").spec();
265 ASSERT_TRUE(content::ExecuteScript(shell()->web_contents(),
266 "setManifestTo('" + manifest_url + "')"));
267 GetManifestAndWait();
270 // If a page's manifest lives in a different origin, it should be accessible if
271 // it has valid access controls headers.
272 IN_PROC_BROWSER_TEST_F(ManifestBrowserTest, CORSManifestWithAcessControls) {
273 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
274 ASSERT_TRUE(cors_embedded_test_server()->InitializeAndWaitUntilReady());
275 ASSERT_NE(embedded_test_server()->port(),
276 cors_embedded_test_server()->port());
278 GURL test_url =
279 embedded_test_server()->GetURL("/manifest/dynamic-manifest.html");
281 TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
282 shell()->LoadURL(test_url);
283 navigation_observer.Wait();
285 std::string manifest_url = cors_embedded_test_server()->GetURL(
286 "/manifest/manifest-cors.json").spec();
287 ASSERT_TRUE(content::ExecuteScript(shell()->web_contents(),
288 "setManifestTo('" + manifest_url + "')"));
290 GetManifestAndWait();
291 EXPECT_FALSE(manifest().IsEmpty());
293 EXPECT_EQ(0u, console_error_count());
296 // If a page's manifest is in an insecure origin while the page is in a secure
297 // origin, requesting the manifest should return the empty manifest.
298 IN_PROC_BROWSER_TEST_F(ManifestBrowserTest, MixedContentManifest) {
299 scoped_ptr<net::SpawnedTestServer> https_server(new net::SpawnedTestServer(
300 net::SpawnedTestServer::TYPE_HTTPS,
301 net::BaseTestServer::SSLOptions(net::BaseTestServer::SSLOptions::CERT_OK),
302 base::FilePath(FILE_PATH_LITERAL("content/test/data"))));
304 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
305 ASSERT_TRUE(https_server->Start());
307 GURL test_url =
308 embedded_test_server()->GetURL("/manifest/dynamic-manifest.html");
310 TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
311 shell()->LoadURL(test_url);
312 navigation_observer.Wait();
314 std::string manifest_url =
315 https_server->GetURL("/manifest/dummy-manifest.json").spec();
316 ASSERT_TRUE(content::ExecuteScript(shell()->web_contents(),
317 "setManifestTo('" + manifest_url + "')"));
319 GetManifestAndWait();
320 EXPECT_TRUE(manifest().IsEmpty());
322 EXPECT_EQ(0u, console_error_count());
325 // If a page's manifest has some parsing errors, they should show up in the
326 // developer console.
327 IN_PROC_BROWSER_TEST_F(ManifestBrowserTest, ParsingErrorsManifest) {
328 GURL test_url = GetTestUrl("manifest", "parsing-errors.html");
330 TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
331 shell()->LoadURL(test_url);
332 navigation_observer.Wait();
334 GetManifestAndWait();
335 EXPECT_TRUE(manifest().IsEmpty());
336 EXPECT_EQ(6u, console_error_count());
339 // If a page has a manifest and the page is navigated to a page without a
340 // manifest, the page's manifest should be updated.
341 IN_PROC_BROWSER_TEST_F(ManifestBrowserTest, Navigation) {
342 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
344 GURL test_url =
345 embedded_test_server()->GetURL("/manifest/dummy-manifest.html");
347 TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
348 shell()->LoadURL(test_url);
349 navigation_observer.Wait();
351 GetManifestAndWait();
352 EXPECT_FALSE(manifest().IsEmpty());
353 EXPECT_EQ(0u, console_error_count());
357 GURL test_url =
358 embedded_test_server()->GetURL("/manifest/no-manifest.html");
360 TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
361 shell()->LoadURL(test_url);
362 navigation_observer.Wait();
364 GetManifestAndWait();
365 EXPECT_TRUE(manifest().IsEmpty());
366 EXPECT_EQ(0u, console_error_count());
370 // If a page has a manifest and the page is navigated using pushState (ie. same
371 // page), it should keep its manifest state.
372 IN_PROC_BROWSER_TEST_F(ManifestBrowserTest, PushStateNavigation) {
373 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
374 GURL test_url =
375 embedded_test_server()->GetURL("/manifest/dummy-manifest.html");
378 TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
379 shell()->LoadURL(test_url);
380 navigation_observer.Wait();
384 TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
385 ASSERT_TRUE(content::ExecuteScript(
386 shell()->web_contents(),
387 "history.pushState({foo: \"bar\"}, 'page', 'page.html');"));
388 navigation_observer.Wait();
391 GetManifestAndWait();
392 EXPECT_FALSE(manifest().IsEmpty());
393 EXPECT_EQ(0u, console_error_count());
396 // If a page has a manifest and is navigated using an anchor (ie. same page), it
397 // should keep its manifest state.
398 IN_PROC_BROWSER_TEST_F(ManifestBrowserTest, AnchorNavigation) {
399 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
400 GURL test_url =
401 embedded_test_server()->GetURL("/manifest/dummy-manifest.html");
404 TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
405 shell()->LoadURL(test_url);
406 navigation_observer.Wait();
410 TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
411 ASSERT_TRUE(content::ExecuteScript(
412 shell()->web_contents(),
413 "var a = document.createElement('a'); a.href='#foo';"
414 "document.body.appendChild(a); a.click();"));
415 navigation_observer.Wait();
418 GetManifestAndWait();
419 EXPECT_FALSE(manifest().IsEmpty());
420 EXPECT_EQ(0u, console_error_count());
423 namespace {
425 scoped_ptr<net::test_server::HttpResponse> CustomHandleRequestForCookies(
426 const net::test_server::HttpRequest& request) {
427 if (request.relative_url == "/index.html") {
428 scoped_ptr<net::test_server::BasicHttpResponse> http_response(
429 new net::test_server::BasicHttpResponse());
430 http_response->set_code(net::HTTP_OK);
431 http_response->set_content_type("text/html");
432 http_response->set_content(
433 "<html><head>"
434 "<link rel=manifest crossorigin='use-credentials' href=/manifest.json>"
435 "</head></html>");
436 return http_response.Pass();
439 const auto& iter = request.headers.find("Cookie");
440 if (iter == request.headers.end() || request.relative_url != "/manifest.json")
441 return scoped_ptr<net::test_server::HttpResponse>();
443 scoped_ptr<net::test_server::BasicHttpResponse> http_response(
444 new net::test_server::BasicHttpResponse());
445 http_response->set_code(net::HTTP_OK);
446 http_response->set_content_type("application/json");
447 http_response->set_content(
448 base::StringPrintf("{\"name\": \"%s\"}", iter->second.c_str()));
450 return http_response.Pass();
453 } // anonymous namespace
455 // This tests that when fetching a Manifest with 'use-credentials' set, the
456 // cookies associated with it are passed along the request.
457 IN_PROC_BROWSER_TEST_F(ManifestBrowserTest, UseCredentialsSendCookies) {
458 scoped_ptr<net::test_server::EmbeddedTestServer> custom_embedded_test_server(
459 new net::test_server::EmbeddedTestServer());
460 custom_embedded_test_server->RegisterRequestHandler(
461 base::Bind(&CustomHandleRequestForCookies));
463 ASSERT_TRUE(custom_embedded_test_server->InitializeAndWaitUntilReady());
465 ASSERT_TRUE(SetCookie(shell()->web_contents()->GetBrowserContext(),
466 custom_embedded_test_server->base_url(),
467 "foobar"));
469 TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
470 shell()->LoadURL(custom_embedded_test_server->GetURL("/index.html"));
471 navigation_observer.Wait();
473 GetManifestAndWait();
474 EXPECT_FALSE(manifest().IsEmpty());
475 EXPECT_EQ(0u, console_error_count());
477 // The custom embedded test server will fill the name field with the cookie
478 // content.
479 EXPECT_TRUE(base::EqualsASCII(manifest().name.string(), "foobar"));
482 namespace {
484 scoped_ptr<net::test_server::HttpResponse> CustomHandleRequestForNoCookies(
485 const net::test_server::HttpRequest& request) {
486 if (request.relative_url == "/index.html") {
487 scoped_ptr<net::test_server::BasicHttpResponse> http_response(
488 new net::test_server::BasicHttpResponse());
489 http_response->set_code(net::HTTP_OK);
490 http_response->set_content_type("text/html");
491 http_response->set_content(
492 "<html><head><link rel=manifest href=/manifest.json></head></html>");
493 return http_response.Pass();
496 const auto& iter = request.headers.find("Cookie");
497 if (iter != request.headers.end() || request.relative_url != "/manifest.json")
498 return scoped_ptr<net::test_server::HttpResponse>();
500 scoped_ptr<net::test_server::BasicHttpResponse> http_response(
501 new net::test_server::BasicHttpResponse());
502 http_response->set_code(net::HTTP_OK);
503 http_response->set_content_type("application/json");
504 http_response->set_content("{\"name\": \"no cookies\"}");
506 return http_response.Pass();
509 } // anonymous namespace
511 // This tests that when fetching a Manifest without 'use-credentials' set, the
512 // cookies associated with it are not passed along the request.
513 IN_PROC_BROWSER_TEST_F(ManifestBrowserTest, NoUseCredentialsNoCookies) {
514 scoped_ptr<net::test_server::EmbeddedTestServer> custom_embedded_test_server(
515 new net::test_server::EmbeddedTestServer());
516 custom_embedded_test_server->RegisterRequestHandler(
517 base::Bind(&CustomHandleRequestForNoCookies));
519 ASSERT_TRUE(custom_embedded_test_server->InitializeAndWaitUntilReady());
521 ASSERT_TRUE(SetCookie(shell()->web_contents()->GetBrowserContext(),
522 custom_embedded_test_server->base_url(),
523 "foobar"));
525 TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
526 shell()->LoadURL(custom_embedded_test_server->GetURL("/index.html"));
527 navigation_observer.Wait();
529 GetManifestAndWait();
530 EXPECT_FALSE(manifest().IsEmpty());
531 EXPECT_EQ(0u, console_error_count());
533 // The custom embedded test server will fill set the name to 'no cookies' if
534 // it did not find cookies.
535 EXPECT_TRUE(base::EqualsASCII(manifest().name.string(), "no cookies"));
538 } // namespace content