skyostil | d34af14 | 2016-06-23 17:19:45 | [diff] [blame] | 1 | // Copyright 2016 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 <memory> |
| 6 | |
altimin | 93a0240 | 2016-08-03 16:26:43 | [diff] [blame] | 7 | #include "base/files/scoped_temp_dir.h" |
skyostil | d34af14 | 2016-06-23 17:19:45 | [diff] [blame] | 8 | #include "base/memory/ptr_util.h" |
| 9 | #include "base/strings/stringprintf.h" |
altimin | 93a0240 | 2016-08-03 16:26:43 | [diff] [blame] | 10 | #include "base/threading/thread_restrictions.h" |
eseckler | d71fc50 | 2016-09-30 10:07:59 | [diff] [blame^] | 11 | #include "content/public/browser/render_view_host.h" |
| 12 | #include "content/public/browser/web_contents.h" |
skyostil | d34af14 | 2016-06-23 17:19:45 | [diff] [blame] | 13 | #include "content/public/test/browser_test.h" |
eseckler | d71fc50 | 2016-09-30 10:07:59 | [diff] [blame^] | 14 | #include "headless/lib/browser/headless_web_contents_impl.h" |
skyostil | d34af14 | 2016-06-23 17:19:45 | [diff] [blame] | 15 | #include "headless/public/domains/runtime.h" |
| 16 | #include "headless/public/headless_browser.h" |
| 17 | #include "headless/public/headless_browser_context.h" |
| 18 | #include "headless/public/headless_devtools_client.h" |
| 19 | #include "headless/public/headless_devtools_target.h" |
| 20 | #include "headless/public/headless_web_contents.h" |
| 21 | #include "headless/test/headless_browser_test.h" |
| 22 | #include "headless/test/test_protocol_handler.h" |
| 23 | #include "net/base/io_buffer.h" |
| 24 | #include "net/http/http_response_headers.h" |
| 25 | #include "net/test/spawned_test_server/spawned_test_server.h" |
| 26 | #include "net/url_request/url_request_job.h" |
| 27 | #include "testing/gtest/include/gtest/gtest.h" |
| 28 | #include "ui/gfx/geometry/size.h" |
| 29 | |
| 30 | namespace headless { |
| 31 | namespace { |
| 32 | const char kMainPageCookie[] = "mood=quizzical"; |
| 33 | const char kIsolatedPageCookie[] = "mood=quixotic"; |
| 34 | } // namespace |
| 35 | |
| 36 | // This test creates two tabs pointing to the same security origin in two |
| 37 | // different browser contexts and checks that they are isolated by creating two |
| 38 | // cookies with the same name in both tabs. The steps are: |
| 39 | // |
| 40 | // 1. Wait for tab #1 to become ready for DevTools. |
| 41 | // 2. Create tab #2 and wait for it to become ready for DevTools. |
| 42 | // 3. Navigate tab #1 to the test page and wait for it to finish loading. |
| 43 | // 4. Navigate tab #2 to the test page and wait for it to finish loading. |
| 44 | // 5. Set a cookie in tab #1. |
| 45 | // 6. Set the same cookie in tab #2 to a different value. |
| 46 | // 7. Read the cookie in tab #1 and check that it has the first value. |
| 47 | // 8. Read the cookie in tab #2 and check that it has the second value. |
| 48 | // |
| 49 | // If the tabs aren't properly isolated, step 7 will fail. |
| 50 | class HeadlessBrowserContextIsolationTest |
| 51 | : public HeadlessAsyncDevTooledBrowserTest { |
| 52 | public: |
| 53 | HeadlessBrowserContextIsolationTest() |
altimin | bf875c9 | 2016-08-04 17:09:07 | [diff] [blame] | 54 | : browser_context_(nullptr), |
| 55 | web_contents2_(nullptr), |
skyostil | d34af14 | 2016-06-23 17:19:45 | [diff] [blame] | 56 | devtools_client2_(HeadlessDevToolsClient::Create()) { |
| 57 | EXPECT_TRUE(embedded_test_server()->Start()); |
| 58 | } |
| 59 | |
| 60 | // HeadlessWebContentsObserver implementation: |
| 61 | void DevToolsTargetReady() override { |
| 62 | if (!web_contents2_) { |
| 63 | browser_context_ = browser()->CreateBrowserContextBuilder().Build(); |
altimin | 93a0240 | 2016-08-03 16:26:43 | [diff] [blame] | 64 | web_contents2_ = browser_context_->CreateWebContentsBuilder().Build(); |
skyostil | d34af14 | 2016-06-23 17:19:45 | [diff] [blame] | 65 | web_contents2_->AddObserver(this); |
| 66 | return; |
| 67 | } |
| 68 | |
| 69 | web_contents2_->GetDevToolsTarget()->AttachClient(devtools_client2_.get()); |
| 70 | HeadlessAsyncDevTooledBrowserTest::DevToolsTargetReady(); |
| 71 | } |
| 72 | |
| 73 | void RunDevTooledTest() override { |
| 74 | load_observer_.reset(new LoadObserver( |
| 75 | devtools_client_.get(), |
| 76 | base::Bind(&HeadlessBrowserContextIsolationTest::OnFirstLoadComplete, |
| 77 | base::Unretained(this)))); |
| 78 | devtools_client_->GetPage()->Navigate( |
| 79 | embedded_test_server()->GetURL("/hello.html").spec()); |
| 80 | } |
| 81 | |
| 82 | void OnFirstLoadComplete() { |
| 83 | EXPECT_TRUE(load_observer_->navigation_succeeded()); |
| 84 | load_observer_.reset(new LoadObserver( |
| 85 | devtools_client2_.get(), |
| 86 | base::Bind(&HeadlessBrowserContextIsolationTest::OnSecondLoadComplete, |
| 87 | base::Unretained(this)))); |
| 88 | devtools_client2_->GetPage()->Navigate( |
| 89 | embedded_test_server()->GetURL("/hello.html").spec()); |
| 90 | } |
| 91 | |
| 92 | void OnSecondLoadComplete() { |
| 93 | EXPECT_TRUE(load_observer_->navigation_succeeded()); |
| 94 | load_observer_.reset(); |
| 95 | |
| 96 | devtools_client_->GetRuntime()->Evaluate( |
| 97 | base::StringPrintf("document.cookie = '%s'", kMainPageCookie), |
| 98 | base::Bind(&HeadlessBrowserContextIsolationTest::OnFirstSetCookieResult, |
| 99 | base::Unretained(this))); |
| 100 | } |
| 101 | |
| 102 | void OnFirstSetCookieResult(std::unique_ptr<runtime::EvaluateResult> result) { |
| 103 | std::string cookie; |
| 104 | EXPECT_TRUE(result->GetResult()->GetValue()->GetAsString(&cookie)); |
| 105 | EXPECT_EQ(kMainPageCookie, cookie); |
| 106 | |
| 107 | devtools_client2_->GetRuntime()->Evaluate( |
| 108 | base::StringPrintf("document.cookie = '%s'", kIsolatedPageCookie), |
| 109 | base::Bind( |
| 110 | &HeadlessBrowserContextIsolationTest::OnSecondSetCookieResult, |
| 111 | base::Unretained(this))); |
| 112 | } |
| 113 | |
| 114 | void OnSecondSetCookieResult( |
| 115 | std::unique_ptr<runtime::EvaluateResult> result) { |
| 116 | std::string cookie; |
| 117 | EXPECT_TRUE(result->GetResult()->GetValue()->GetAsString(&cookie)); |
| 118 | EXPECT_EQ(kIsolatedPageCookie, cookie); |
| 119 | |
| 120 | devtools_client_->GetRuntime()->Evaluate( |
| 121 | "document.cookie", |
| 122 | base::Bind(&HeadlessBrowserContextIsolationTest::OnFirstGetCookieResult, |
| 123 | base::Unretained(this))); |
| 124 | } |
| 125 | |
| 126 | void OnFirstGetCookieResult(std::unique_ptr<runtime::EvaluateResult> result) { |
| 127 | std::string cookie; |
| 128 | EXPECT_TRUE(result->GetResult()->GetValue()->GetAsString(&cookie)); |
| 129 | EXPECT_EQ(kMainPageCookie, cookie); |
| 130 | |
| 131 | devtools_client2_->GetRuntime()->Evaluate( |
| 132 | "document.cookie", |
| 133 | base::Bind( |
| 134 | &HeadlessBrowserContextIsolationTest::OnSecondGetCookieResult, |
| 135 | base::Unretained(this))); |
| 136 | } |
| 137 | |
| 138 | void OnSecondGetCookieResult( |
| 139 | std::unique_ptr<runtime::EvaluateResult> result) { |
| 140 | std::string cookie; |
| 141 | EXPECT_TRUE(result->GetResult()->GetValue()->GetAsString(&cookie)); |
| 142 | EXPECT_EQ(kIsolatedPageCookie, cookie); |
| 143 | FinishTest(); |
| 144 | } |
| 145 | |
| 146 | void FinishTest() { |
| 147 | web_contents2_->RemoveObserver(this); |
| 148 | web_contents2_->Close(); |
altimin | bf875c9 | 2016-08-04 17:09:07 | [diff] [blame] | 149 | browser_context_->Close(); |
skyostil | d34af14 | 2016-06-23 17:19:45 | [diff] [blame] | 150 | FinishAsynchronousTest(); |
| 151 | } |
| 152 | |
| 153 | private: |
altimin | bf875c9 | 2016-08-04 17:09:07 | [diff] [blame] | 154 | HeadlessBrowserContext* browser_context_; |
skyostil | d34af14 | 2016-06-23 17:19:45 | [diff] [blame] | 155 | HeadlessWebContents* web_contents2_; |
| 156 | std::unique_ptr<HeadlessDevToolsClient> devtools_client2_; |
| 157 | std::unique_ptr<LoadObserver> load_observer_; |
| 158 | }; |
| 159 | |
| 160 | HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessBrowserContextIsolationTest); |
| 161 | |
| 162 | IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, ContextProtocolHandler) { |
| 163 | const std::string kResponseBody = "<p>HTTP response body</p>"; |
| 164 | ProtocolHandlerMap protocol_handlers; |
| 165 | protocol_handlers[url::kHttpScheme] = |
ricea | c4bfd81 | 2016-08-19 12:06:54 | [diff] [blame] | 166 | base::MakeUnique<TestProtocolHandler>(kResponseBody); |
skyostil | d34af14 | 2016-06-23 17:19:45 | [diff] [blame] | 167 | |
| 168 | // Load a page which doesn't actually exist, but which is fetched by our |
| 169 | // custom protocol handler. |
altimin | bf875c9 | 2016-08-04 17:09:07 | [diff] [blame] | 170 | HeadlessBrowserContext* browser_context = |
skyostil | d34af14 | 2016-06-23 17:19:45 | [diff] [blame] | 171 | browser() |
| 172 | ->CreateBrowserContextBuilder() |
| 173 | .SetProtocolHandlers(std::move(protocol_handlers)) |
| 174 | .Build(); |
| 175 | HeadlessWebContents* web_contents = |
altimin | 93a0240 | 2016-08-03 16:26:43 | [diff] [blame] | 176 | browser_context->CreateWebContentsBuilder() |
skyostil | d34af14 | 2016-06-23 17:19:45 | [diff] [blame] | 177 | .SetInitialURL(GURL("http://not-an-actual-domain.tld/hello.html")) |
skyostil | d34af14 | 2016-06-23 17:19:45 | [diff] [blame] | 178 | .Build(); |
| 179 | EXPECT_TRUE(WaitForLoad(web_contents)); |
| 180 | |
| 181 | std::string inner_html; |
| 182 | EXPECT_TRUE(EvaluateScript(web_contents, "document.body.innerHTML") |
| 183 | ->GetResult() |
| 184 | ->GetValue() |
| 185 | ->GetAsString(&inner_html)); |
| 186 | EXPECT_EQ(kResponseBody, inner_html); |
| 187 | web_contents->Close(); |
| 188 | |
altimin | bf875c9 | 2016-08-04 17:09:07 | [diff] [blame] | 189 | HeadlessBrowserContext* another_browser_context = |
altimin | 93a0240 | 2016-08-03 16:26:43 | [diff] [blame] | 190 | browser()->CreateBrowserContextBuilder().Build(); |
| 191 | |
| 192 | // Loading the same non-existent page using a tab with a different context |
skyostil | d34af14 | 2016-06-23 17:19:45 | [diff] [blame] | 193 | // should not work since the protocol handler only exists on the custom |
| 194 | // context. |
| 195 | web_contents = |
altimin | 93a0240 | 2016-08-03 16:26:43 | [diff] [blame] | 196 | another_browser_context->CreateWebContentsBuilder() |
skyostil | d34af14 | 2016-06-23 17:19:45 | [diff] [blame] | 197 | .SetInitialURL(GURL("http://not-an-actual-domain.tld/hello.html")) |
| 198 | .Build(); |
| 199 | EXPECT_TRUE(WaitForLoad(web_contents)); |
| 200 | EXPECT_TRUE(EvaluateScript(web_contents, "document.body.innerHTML") |
| 201 | ->GetResult() |
| 202 | ->GetValue() |
| 203 | ->GetAsString(&inner_html)); |
| 204 | EXPECT_EQ("", inner_html); |
| 205 | web_contents->Close(); |
| 206 | } |
| 207 | |
altimin | 93a0240 | 2016-08-03 16:26:43 | [diff] [blame] | 208 | IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, UserDataDir) { |
| 209 | // We do not want to bother with posting tasks to create a temp dir. |
| 210 | // Just allow IO from main thread for now. |
| 211 | base::ThreadRestrictions::SetIOAllowed(true); |
| 212 | |
| 213 | EXPECT_TRUE(embedded_test_server()->Start()); |
| 214 | |
| 215 | base::ScopedTempDir user_data_dir; |
| 216 | ASSERT_TRUE(user_data_dir.CreateUniqueTempDir()); |
| 217 | |
| 218 | // Newly created temp directory should be empty. |
vabr | 16e5f60 | 2016-09-15 18:14:00 | [diff] [blame] | 219 | EXPECT_TRUE(base::IsDirectoryEmpty(user_data_dir.GetPath())); |
altimin | 93a0240 | 2016-08-03 16:26:43 | [diff] [blame] | 220 | |
altimin | bf875c9 | 2016-08-04 17:09:07 | [diff] [blame] | 221 | HeadlessBrowserContext* browser_context = |
altimin | 93a0240 | 2016-08-03 16:26:43 | [diff] [blame] | 222 | browser() |
| 223 | ->CreateBrowserContextBuilder() |
vabr | 16e5f60 | 2016-09-15 18:14:00 | [diff] [blame] | 224 | .SetUserDataDir(user_data_dir.GetPath()) |
altimin | 1ab63101 | 2016-08-04 18:43:45 | [diff] [blame] | 225 | .SetIncognitoMode(false) |
altimin | 93a0240 | 2016-08-03 16:26:43 | [diff] [blame] | 226 | .Build(); |
| 227 | |
| 228 | HeadlessWebContents* web_contents = |
| 229 | browser_context->CreateWebContentsBuilder() |
| 230 | .SetInitialURL(embedded_test_server()->GetURL("/hello.html")) |
| 231 | .Build(); |
| 232 | |
| 233 | EXPECT_TRUE(WaitForLoad(web_contents)); |
| 234 | |
| 235 | // Something should be written to this directory. |
| 236 | // If it is not the case, more complex page may be needed. |
| 237 | // ServiceWorkers may be a good option. |
vabr | 16e5f60 | 2016-09-15 18:14:00 | [diff] [blame] | 238 | EXPECT_FALSE(base::IsDirectoryEmpty(user_data_dir.GetPath())); |
altimin | 93a0240 | 2016-08-03 16:26:43 | [diff] [blame] | 239 | } |
| 240 | |
altimin | 1ab63101 | 2016-08-04 18:43:45 | [diff] [blame] | 241 | IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, IncognitoMode) { |
| 242 | // We do not want to bother with posting tasks to create a temp dir. |
| 243 | // Just allow IO from main thread for now. |
| 244 | base::ThreadRestrictions::SetIOAllowed(true); |
| 245 | |
| 246 | EXPECT_TRUE(embedded_test_server()->Start()); |
| 247 | |
| 248 | base::ScopedTempDir user_data_dir; |
| 249 | ASSERT_TRUE(user_data_dir.CreateUniqueTempDir()); |
| 250 | |
| 251 | // Newly created temp directory should be empty. |
vabr | 16e5f60 | 2016-09-15 18:14:00 | [diff] [blame] | 252 | EXPECT_TRUE(base::IsDirectoryEmpty(user_data_dir.GetPath())); |
altimin | 1ab63101 | 2016-08-04 18:43:45 | [diff] [blame] | 253 | |
| 254 | HeadlessBrowserContext* browser_context = |
| 255 | browser() |
| 256 | ->CreateBrowserContextBuilder() |
vabr | 16e5f60 | 2016-09-15 18:14:00 | [diff] [blame] | 257 | .SetUserDataDir(user_data_dir.GetPath()) |
altimin | 1ab63101 | 2016-08-04 18:43:45 | [diff] [blame] | 258 | .SetIncognitoMode(true) |
| 259 | .Build(); |
| 260 | |
| 261 | HeadlessWebContents* web_contents = |
| 262 | browser_context->CreateWebContentsBuilder() |
| 263 | .SetInitialURL(embedded_test_server()->GetURL("/hello.html")) |
| 264 | .Build(); |
| 265 | |
| 266 | EXPECT_TRUE(WaitForLoad(web_contents)); |
| 267 | |
| 268 | // Similar to test above, but now we are in incognito mode, |
| 269 | // so nothing should be written to this directory. |
vabr | 16e5f60 | 2016-09-15 18:14:00 | [diff] [blame] | 270 | EXPECT_TRUE(base::IsDirectoryEmpty(user_data_dir.GetPath())); |
altimin | 1ab63101 | 2016-08-04 18:43:45 | [diff] [blame] | 271 | } |
| 272 | |
eseckler | d71fc50 | 2016-09-30 10:07:59 | [diff] [blame^] | 273 | IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, ContextWebPreferences) { |
| 274 | // By default, hide_scrollbars should be false. |
| 275 | EXPECT_FALSE(WebPreferences().hide_scrollbars); |
| 276 | |
| 277 | // Set hide_scrollbars preference to true for a new BrowserContext. |
| 278 | HeadlessBrowserContext* browser_context = |
| 279 | browser() |
| 280 | ->CreateBrowserContextBuilder() |
| 281 | .SetOverrideWebPreferencesCallback( |
| 282 | base::Bind([](headless::WebPreferences* preferences) { |
| 283 | preferences->hide_scrollbars = true; |
| 284 | })) |
| 285 | .Build(); |
| 286 | HeadlessWebContents* web_contents = |
| 287 | browser_context->CreateWebContentsBuilder() |
| 288 | .SetInitialURL(GURL("about:blank")) |
| 289 | .Build(); |
| 290 | |
| 291 | // Verify that the preference takes effect. |
| 292 | HeadlessWebContentsImpl* contents_impl = |
| 293 | HeadlessWebContentsImpl::From(web_contents); |
| 294 | EXPECT_TRUE(contents_impl->web_contents() |
| 295 | ->GetRenderViewHost() |
| 296 | ->GetWebkitPreferences().hide_scrollbars); |
| 297 | } |
| 298 | |
skyostil | d34af14 | 2016-06-23 17:19:45 | [diff] [blame] | 299 | } // namespace headless |