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 
7 #include "base/base_paths.h"
8 #include "base/bind.h"
9 #include "base/files/file_util.h"
10 #include "base/json/json_reader.h"
11 #include "base/json/json_writer.h"
12 #include "base/path_service.h"
13 #include "base/run_loop.h"
14 #include "base/strings/string_util.h"
15 #include "base/threading/thread_restrictions.h"
16 #include "build/build_config.h"
17 #include "content/public/browser/render_widget_host_view.h"
18 #include "content/public/browser/web_contents.h"
19 #include "content/public/common/url_constants.h"
20 #include "content/public/test/browser_test.h"
21 #include "content/public/test/no_renderer_crashes_assertion.h"
22 #include "headless/lib/browser/headless_web_contents_impl.h"
23 #include "headless/public/devtools/domains/browser.h"
24 #include "headless/public/devtools/domains/dom.h"
25 #include "headless/public/devtools/domains/dom_snapshot.h"
26 #include "headless/public/devtools/domains/emulation.h"
27 #include "headless/public/devtools/domains/inspector.h"
28 #include "headless/public/devtools/domains/network.h"
29 #include "headless/public/devtools/domains/page.h"
30 #include "headless/public/devtools/domains/runtime.h"
31 #include "headless/public/devtools/domains/target.h"
32 #include "headless/public/headless_browser.h"
33 #include "headless/public/headless_devtools_client.h"
34 #include "headless/public/headless_devtools_target.h"
35 #include "headless/test/headless_browser_test.h"
36 #include "testing/gmock/include/gmock/gmock.h"
37 #include "testing/gtest/include/gtest/gtest.h"
38 #include "url/gurl.h"
39 
40 #define EXPECT_SIZE_EQ(expected, actual)               \
41   do {                                                 \
42     EXPECT_EQ((expected).width(), (actual).width());   \
43     EXPECT_EQ((expected).height(), (actual).height()); \
44   } while (false)
45 
46 using testing::ElementsAre;
47 using testing::NotNull;
48 using testing::UnorderedElementsAre;
49 
50 namespace headless {
51 
52 class HeadlessDevToolsClientNavigationTest
53     : public HeadlessAsyncDevTooledBrowserTest,
54       page::ExperimentalObserver {
55  public:
RunDevTooledTest()56   void RunDevTooledTest() override {
57     EXPECT_TRUE(embedded_test_server()->Start());
58     std::unique_ptr<page::NavigateParams> params =
59         page::NavigateParams::Builder()
60             .SetUrl(embedded_test_server()->GetURL("/hello.html").spec())
61             .Build();
62     devtools_client_->GetPage()->GetExperimental()->AddObserver(this);
63     base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
64     devtools_client_->GetPage()->Enable(run_loop.QuitClosure());
65     run_loop.Run();
66     devtools_client_->GetPage()->Navigate(std::move(params));
67   }
68 
OnLoadEventFired(const page::LoadEventFiredParams & params)69   void OnLoadEventFired(const page::LoadEventFiredParams& params) override {
70     devtools_client_->GetPage()->Disable();
71     devtools_client_->GetPage()->GetExperimental()->RemoveObserver(this);
72     FinishAsynchronousTest();
73   }
74 
75   // Check that events with no parameters still get a parameters object.
OnFrameResized(const page::FrameResizedParams & params)76   void OnFrameResized(const page::FrameResizedParams& params) override {}
77 };
78 
79 #if defined(OS_WIN)
80 // TODO(crbug.com/1045980): Disabled due to flakiness.
81 DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientNavigationTest);
82 #else
83 HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientNavigationTest);
84 #endif
85 
86 class HeadlessDevToolsClientWindowManagementTest
87     : public HeadlessAsyncDevTooledBrowserTest {
88  public:
SetWindowBounds(const gfx::Rect & rect,base::OnceCallback<void (std::unique_ptr<browser::SetWindowBoundsResult>)> callback)89   void SetWindowBounds(
90       const gfx::Rect& rect,
91       base::OnceCallback<void(std::unique_ptr<browser::SetWindowBoundsResult>)>
92           callback) {
93     std::unique_ptr<browser::Bounds> bounds =
94         browser::Bounds::Builder()
95             .SetLeft(rect.x())
96             .SetTop(rect.y())
97             .SetWidth(rect.width())
98             .SetHeight(rect.height())
99             .SetWindowState(browser::WindowState::NORMAL)
100             .Build();
101     int window_id = HeadlessWebContentsImpl::From(web_contents_)->window_id();
102     std::unique_ptr<browser::SetWindowBoundsParams> params =
103         browser::SetWindowBoundsParams::Builder()
104             .SetWindowId(window_id)
105             .SetBounds(std::move(bounds))
106             .Build();
107     browser_devtools_client_->GetBrowser()->GetExperimental()->SetWindowBounds(
108         std::move(params), std::move(callback));
109   }
110 
SetWindowState(const browser::WindowState state,base::OnceCallback<void (std::unique_ptr<browser::SetWindowBoundsResult>)> callback)111   void SetWindowState(
112       const browser::WindowState state,
113       base::OnceCallback<void(std::unique_ptr<browser::SetWindowBoundsResult>)>
114           callback) {
115     std::unique_ptr<browser::Bounds> bounds =
116         browser::Bounds::Builder().SetWindowState(state).Build();
117     int window_id = HeadlessWebContentsImpl::From(web_contents_)->window_id();
118     std::unique_ptr<browser::SetWindowBoundsParams> params =
119         browser::SetWindowBoundsParams::Builder()
120             .SetWindowId(window_id)
121             .SetBounds(std::move(bounds))
122             .Build();
123     browser_devtools_client_->GetBrowser()->GetExperimental()->SetWindowBounds(
124         std::move(params), std::move(callback));
125   }
126 
GetWindowBounds(base::OnceCallback<void (std::unique_ptr<browser::GetWindowBoundsResult>)> callback)127   void GetWindowBounds(
128       base::OnceCallback<void(std::unique_ptr<browser::GetWindowBoundsResult>)>
129           callback) {
130     int window_id = HeadlessWebContentsImpl::From(web_contents_)->window_id();
131     std::unique_ptr<browser::GetWindowBoundsParams> params =
132         browser::GetWindowBoundsParams::Builder()
133             .SetWindowId(window_id)
134             .Build();
135 
136     browser_devtools_client_->GetBrowser()->GetExperimental()->GetWindowBounds(
137         std::move(params), std::move(callback));
138   }
139 
CheckWindowBounds(const gfx::Rect & bounds,const browser::WindowState state,std::unique_ptr<browser::GetWindowBoundsResult> result)140   void CheckWindowBounds(
141       const gfx::Rect& bounds,
142       const browser::WindowState state,
143       std::unique_ptr<browser::GetWindowBoundsResult> result) {
144     const browser::Bounds* actual_bounds = result->GetBounds();
145 // Mac does not support repositioning, as we don't show any actual window.
146 #if !defined(OS_MACOSX)
147     EXPECT_EQ(bounds.x(), actual_bounds->GetLeft());
148     EXPECT_EQ(bounds.y(), actual_bounds->GetTop());
149 #endif  // !defined(OS_MACOSX)
150     EXPECT_EQ(bounds.width(), actual_bounds->GetWidth());
151     EXPECT_EQ(bounds.height(), actual_bounds->GetHeight());
152     EXPECT_EQ(state, actual_bounds->GetWindowState());
153   }
154 };
155 
156 class HeadlessDevToolsClientChangeWindowBoundsTest
157     : public HeadlessDevToolsClientWindowManagementTest {
RunDevTooledTest()158   void RunDevTooledTest() override {
159     SetWindowBounds(
160         gfx::Rect(100, 200, 300, 400),
161         base::BindOnce(
162             &HeadlessDevToolsClientChangeWindowBoundsTest::OnSetWindowBounds,
163             base::Unretained(this)));
164   }
165 
OnSetWindowBounds(std::unique_ptr<browser::SetWindowBoundsResult> result)166   void OnSetWindowBounds(
167       std::unique_ptr<browser::SetWindowBoundsResult> result) {
168     GetWindowBounds(base::BindOnce(
169         &HeadlessDevToolsClientChangeWindowBoundsTest::OnGetWindowBounds,
170         base::Unretained(this)));
171   }
172 
OnGetWindowBounds(std::unique_ptr<browser::GetWindowBoundsResult> result)173   void OnGetWindowBounds(
174       std::unique_ptr<browser::GetWindowBoundsResult> result) {
175     CheckWindowBounds(gfx::Rect(100, 200, 300, 400),
176                       browser::WindowState::NORMAL, std::move(result));
177     FinishAsynchronousTest();
178   }
179 };
180 
181 #if defined(OS_WIN)
182 // TODO(crbug.com/1045980): Disabled due to flakiness.
183 DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(
184     HeadlessDevToolsClientChangeWindowBoundsTest);
185 #else
186 HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientChangeWindowBoundsTest);
187 #endif
188 
189 class HeadlessDevToolsClientChangeWindowStateTest
190     : public HeadlessDevToolsClientWindowManagementTest {
191  public:
HeadlessDevToolsClientChangeWindowStateTest(browser::WindowState state)192   explicit HeadlessDevToolsClientChangeWindowStateTest(
193       browser::WindowState state)
194       : state_(state) {}
195 
RunDevTooledTest()196   void RunDevTooledTest() override {
197     SetWindowState(
198         state_,
199         base::BindOnce(
200             &HeadlessDevToolsClientChangeWindowStateTest::OnSetWindowState,
201             base::Unretained(this)));
202   }
203 
OnSetWindowState(std::unique_ptr<browser::SetWindowBoundsResult> result)204   void OnSetWindowState(
205       std::unique_ptr<browser::SetWindowBoundsResult> result) {
206     GetWindowBounds(base::BindOnce(
207         &HeadlessDevToolsClientChangeWindowStateTest::OnGetWindowState,
208         base::Unretained(this)));
209   }
210 
OnGetWindowState(std::unique_ptr<browser::GetWindowBoundsResult> result)211   void OnGetWindowState(
212       std::unique_ptr<browser::GetWindowBoundsResult> result) {
213     HeadlessBrowser::Options::Builder builder;
214     const HeadlessBrowser::Options kDefaultOptions = builder.Build();
215     CheckWindowBounds(gfx::Rect(kDefaultOptions.window_size), state_,
216                       std::move(result));
217     FinishAsynchronousTest();
218   }
219 
220  protected:
221   browser::WindowState state_;
222 };
223 
224 class HeadlessDevToolsClientMinimizeWindowTest
225     : public HeadlessDevToolsClientChangeWindowStateTest {
226  public:
HeadlessDevToolsClientMinimizeWindowTest()227   HeadlessDevToolsClientMinimizeWindowTest()
228       : HeadlessDevToolsClientChangeWindowStateTest(
229             browser::WindowState::MINIMIZED) {}
230 };
231 
232 #if defined(OS_WIN)
233 // TODO(crbug.com/1045980): Disabled due to flakiness.
234 DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(
235     HeadlessDevToolsClientMinimizeWindowTest);
236 #else
237 HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientMinimizeWindowTest);
238 #endif
239 
240 class HeadlessDevToolsClientMaximizeWindowTest
241     : public HeadlessDevToolsClientChangeWindowStateTest {
242  public:
HeadlessDevToolsClientMaximizeWindowTest()243   HeadlessDevToolsClientMaximizeWindowTest()
244       : HeadlessDevToolsClientChangeWindowStateTest(
245             browser::WindowState::MAXIMIZED) {}
246 };
247 
248 #if defined(OS_WIN)
249 // TODO(crbug.com/1045980): Disabled due to flakiness.
250 DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(
251     HeadlessDevToolsClientMaximizeWindowTest);
252 #else
253 HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientMaximizeWindowTest);
254 #endif
255 
256 class HeadlessDevToolsClientFullscreenWindowTest
257     : public HeadlessDevToolsClientChangeWindowStateTest {
258  public:
HeadlessDevToolsClientFullscreenWindowTest()259   HeadlessDevToolsClientFullscreenWindowTest()
260       : HeadlessDevToolsClientChangeWindowStateTest(
261             browser::WindowState::FULLSCREEN) {}
262 };
263 
264 #if defined(OS_WIN)
265 // TODO(crbug.com/1045980): Disabled due to flakiness.
266 DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(
267     HeadlessDevToolsClientFullscreenWindowTest);
268 #else
269 HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientFullscreenWindowTest);
270 #endif
271 
272 class HeadlessDevToolsClientEvalTest
273     : public HeadlessAsyncDevTooledBrowserTest {
274  public:
RunDevTooledTest()275   void RunDevTooledTest() override {
276     std::unique_ptr<runtime::EvaluateParams> params =
277         runtime::EvaluateParams::Builder().SetExpression("1 + 2").Build();
278     devtools_client_->GetRuntime()->Evaluate(
279         std::move(params),
280         base::BindOnce(&HeadlessDevToolsClientEvalTest::OnFirstResult,
281                        base::Unretained(this)));
282     // Test the convenience overload which only takes the required command
283     // parameters.
284     devtools_client_->GetRuntime()->Evaluate(
285         "24 * 7",
286         base::BindOnce(&HeadlessDevToolsClientEvalTest::OnSecondResult,
287                        base::Unretained(this)));
288   }
289 
OnFirstResult(std::unique_ptr<runtime::EvaluateResult> result)290   void OnFirstResult(std::unique_ptr<runtime::EvaluateResult> result) {
291     EXPECT_TRUE(result->GetResult()->HasValue());
292     EXPECT_EQ(3, result->GetResult()->GetValue()->GetInt());
293   }
294 
OnSecondResult(std::unique_ptr<runtime::EvaluateResult> result)295   void OnSecondResult(std::unique_ptr<runtime::EvaluateResult> result) {
296     EXPECT_TRUE(result->GetResult()->HasValue());
297     EXPECT_EQ(168, result->GetResult()->GetValue()->GetInt());
298     FinishAsynchronousTest();
299   }
300 };
301 
302 #if defined(OS_WIN)
303 // TODO(crbug.com/1045980): Disabled due to flakiness.
304 DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientEvalTest);
305 #else
306 HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientEvalTest);
307 #endif
308 
309 class HeadlessDevToolsClientCallbackTest
310     : public HeadlessAsyncDevTooledBrowserTest {
311  public:
HeadlessDevToolsClientCallbackTest()312   HeadlessDevToolsClientCallbackTest() : first_result_received_(false) {}
313 
RunDevTooledTest()314   void RunDevTooledTest() override {
315     // Null callback without parameters.
316     devtools_client_->GetPage()->Enable();
317     // Null callback with parameters.
318     devtools_client_->GetRuntime()->Evaluate("true");
319     // Non-null callback without parameters.
320     devtools_client_->GetPage()->Disable(
321         base::BindOnce(&HeadlessDevToolsClientCallbackTest::OnFirstResult,
322                        base::Unretained(this)));
323     // Non-null callback with parameters.
324     devtools_client_->GetRuntime()->Evaluate(
325         "true",
326         base::BindOnce(&HeadlessDevToolsClientCallbackTest::OnSecondResult,
327                        base::Unretained(this)));
328   }
329 
OnFirstResult()330   void OnFirstResult() {
331     EXPECT_FALSE(first_result_received_);
332     first_result_received_ = true;
333   }
334 
OnSecondResult(std::unique_ptr<runtime::EvaluateResult> result)335   void OnSecondResult(std::unique_ptr<runtime::EvaluateResult> result) {
336     EXPECT_TRUE(first_result_received_);
337     FinishAsynchronousTest();
338   }
339 
340  private:
341   bool first_result_received_;
342 };
343 
344 #if defined(OS_WIN)
345 // TODO(crbug.com/1045980): Disabled due to flakiness.
346 DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientCallbackTest);
347 #else
348 HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientCallbackTest);
349 #endif
350 
351 class HeadlessDevToolsClientObserverTest
352     : public HeadlessAsyncDevTooledBrowserTest,
353       network::Observer {
354  public:
RunDevTooledTest()355   void RunDevTooledTest() override {
356     EXPECT_TRUE(embedded_test_server()->Start());
357     base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
358     devtools_client_->GetNetwork()->AddObserver(this);
359     devtools_client_->GetNetwork()->Enable(run_loop.QuitClosure());
360     run_loop.Run();
361 
362     devtools_client_->GetPage()->Navigate(
363         embedded_test_server()->GetURL("/hello.html").spec());
364   }
365 
OnRequestWillBeSent(const network::RequestWillBeSentParams & params)366   void OnRequestWillBeSent(
367       const network::RequestWillBeSentParams& params) override {
368     EXPECT_EQ("GET", params.GetRequest()->GetMethod());
369     EXPECT_EQ(embedded_test_server()->GetURL("/hello.html").spec(),
370               params.GetRequest()->GetUrl());
371   }
372 
OnResponseReceived(const network::ResponseReceivedParams & params)373   void OnResponseReceived(
374       const network::ResponseReceivedParams& params) override {
375     EXPECT_EQ(200, params.GetResponse()->GetStatus());
376     EXPECT_EQ("OK", params.GetResponse()->GetStatusText());
377     const base::Value* content_type_value =
378         params.GetResponse()->GetHeaders()->FindKey("Content-Type");
379     ASSERT_THAT(content_type_value, NotNull());
380     EXPECT_EQ("text/html", content_type_value->GetString());
381 
382     devtools_client_->GetNetwork()->Disable();
383     devtools_client_->GetNetwork()->RemoveObserver(this);
384     FinishAsynchronousTest();
385   }
386 };
387 
388 #if defined(OS_WIN)
389 // TODO(crbug.com/1045980): Disabled due to flakiness.
390 DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientObserverTest);
391 #else
392 HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientObserverTest);
393 #endif
394 
395 class HeadlessDevToolsClientExperimentalTest
396     : public HeadlessAsyncDevTooledBrowserTest,
397       page::ExperimentalObserver {
398  public:
RunDevTooledTest()399   void RunDevTooledTest() override {
400     EXPECT_TRUE(embedded_test_server()->Start());
401     base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
402     devtools_client_->GetPage()->GetExperimental()->AddObserver(this);
403     devtools_client_->GetPage()->Enable(run_loop.QuitClosure());
404     run_loop.Run();
405     // Check that experimental commands require parameter objects.
406     devtools_client_->GetRuntime()
407         ->GetExperimental()
408         ->SetCustomObjectFormatterEnabled(
409             runtime::SetCustomObjectFormatterEnabledParams::Builder()
410                 .SetEnabled(false)
411                 .Build());
412 
413     // Check that a previously experimental command which takes no parameters
414     // still works by giving it a parameter object.
415     devtools_client_->GetRuntime()->GetExperimental()->RunIfWaitingForDebugger(
416         runtime::RunIfWaitingForDebuggerParams::Builder().Build());
417 
418     devtools_client_->GetPage()->Navigate(
419         embedded_test_server()->GetURL("/hello.html").spec());
420   }
421 
OnFrameStoppedLoading(const page::FrameStoppedLoadingParams & params)422   void OnFrameStoppedLoading(
423       const page::FrameStoppedLoadingParams& params) override {
424     devtools_client_->GetPage()->Disable();
425     devtools_client_->GetPage()->GetExperimental()->RemoveObserver(this);
426 
427     // Check that a non-experimental command which has no return value can be
428     // called with a void() callback.
429     devtools_client_->GetPage()->Reload(
430         page::ReloadParams::Builder().Build(),
431         base::BindOnce(&HeadlessDevToolsClientExperimentalTest::OnReloadStarted,
432                        base::Unretained(this)));
433   }
434 
OnReloadStarted()435   void OnReloadStarted() { FinishAsynchronousTest(); }
436 };
437 
438 #if defined(OS_WIN)
439 // TODO(crbug.com/1045980): Disabled due to flakiness.
440 DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(
441     HeadlessDevToolsClientExperimentalTest);
442 #else
443 HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientExperimentalTest);
444 #endif
445 
446 class HeadlessDevToolsNavigationControlTest
447     : public HeadlessAsyncDevTooledBrowserTest,
448       network::ExperimentalObserver,
449       page::ExperimentalObserver {
450  public:
RunDevTooledTest()451   void RunDevTooledTest() override {
452     EXPECT_TRUE(embedded_test_server()->Start());
453     base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
454     devtools_client_->GetPage()->GetExperimental()->AddObserver(this);
455     devtools_client_->GetNetwork()->GetExperimental()->AddObserver(this);
456     devtools_client_->GetPage()->Enable(run_loop.QuitClosure());
457     run_loop.Run();
458     devtools_client_->GetNetwork()->Enable();
459 
460     std::unique_ptr<headless::network::RequestPattern> match_all =
461         headless::network::RequestPattern::Builder().SetUrlPattern("*").Build();
462     std::vector<std::unique_ptr<headless::network::RequestPattern>> patterns;
463     patterns.push_back(std::move(match_all));
464     devtools_client_->GetNetwork()->GetExperimental()->SetRequestInterception(
465         network::SetRequestInterceptionParams::Builder()
466             .SetPatterns(std::move(patterns))
467             .Build());
468     devtools_client_->GetPage()->Navigate(
469         embedded_test_server()->GetURL("/hello.html").spec());
470   }
471 
OnRequestIntercepted(const network::RequestInterceptedParams & params)472   void OnRequestIntercepted(
473       const network::RequestInterceptedParams& params) override {
474     if (params.GetIsNavigationRequest())
475       navigation_requested_ = true;
476     // Allow the navigation to proceed.
477     devtools_client_->GetNetwork()
478         ->GetExperimental()
479         ->ContinueInterceptedRequest(
480             network::ContinueInterceptedRequestParams::Builder()
481                 .SetInterceptionId(params.GetInterceptionId())
482                 .Build());
483   }
484 
OnFrameStoppedLoading(const page::FrameStoppedLoadingParams & params)485   void OnFrameStoppedLoading(
486       const page::FrameStoppedLoadingParams& params) override {
487     EXPECT_TRUE(navigation_requested_);
488     FinishAsynchronousTest();
489   }
490 
491  private:
492   bool navigation_requested_ = false;
493 };
494 
495 #if defined(OS_WIN)
496 // TODO(crbug.com/1045980): Disabled due to flakiness.
497 DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsNavigationControlTest);
498 #else
499 HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsNavigationControlTest);
500 #endif
501 
502 class HeadlessCrashObserverTest : public HeadlessAsyncDevTooledBrowserTest,
503                                   inspector::ExperimentalObserver {
504  public:
RunDevTooledTest()505   void RunDevTooledTest() override {
506     devtools_client_->GetInspector()->GetExperimental()->AddObserver(this);
507     devtools_client_->GetInspector()->GetExperimental()->Enable(
508         inspector::EnableParams::Builder().Build());
509     devtools_client_->GetPage()->Enable();
510     devtools_client_->GetPage()->Navigate(content::kChromeUICrashURL);
511   }
512 
OnTargetCrashed(const inspector::TargetCrashedParams & params)513   void OnTargetCrashed(const inspector::TargetCrashedParams& params) override {
514     FinishAsynchronousTest();
515     render_process_exited_ = true;
516   }
517 
518   // Make sure we don't fail because the renderer crashed!
RenderProcessExited(base::TerminationStatus status,int exit_code)519   void RenderProcessExited(base::TerminationStatus status,
520                            int exit_code) override {
521 #if defined(OS_WIN) && defined(ADDRESS_SANITIZER)
522     // TODO(crbug.com/845011): Make ASan not interfere and expect a crash.
523     // ASan's normal error exit code is 1, which base categorizes as the process
524     // being killed.
525     EXPECT_EQ(base::TERMINATION_STATUS_PROCESS_WAS_KILLED, status);
526 #elif defined(OS_WIN) || defined(OS_MACOSX)
527     EXPECT_EQ(base::TERMINATION_STATUS_PROCESS_CRASHED, status);
528 #else
529     EXPECT_EQ(base::TERMINATION_STATUS_ABNORMAL_TERMINATION, status);
530 #endif
531   }
532 
533  private:
534   content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes_;
535 };
536 
537 #if defined(OS_WIN)
538 // TODO(crbug.com/1045980): Disabled due to flakiness.
539 DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessCrashObserverTest);
540 #else
541 HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessCrashObserverTest);
542 #endif
543 
544 class HeadlessDevToolsClientAttachTest
545     : public HeadlessAsyncDevTooledBrowserTest {
546  public:
RunDevTooledTest()547   void RunDevTooledTest() override {
548     other_devtools_client_ = HeadlessDevToolsClient::Create();
549     HeadlessDevToolsTarget* devtools_target =
550         web_contents_->GetDevToolsTarget();
551 
552     EXPECT_TRUE(devtools_target->IsAttached());
553     // Detach the existing client, attach the other client.
554     devtools_target->DetachClient(devtools_client_.get());
555     EXPECT_FALSE(devtools_target->IsAttached());
556     devtools_target->AttachClient(other_devtools_client_.get());
557     EXPECT_TRUE(devtools_target->IsAttached());
558 
559     // Now, let's make sure this devtools client works.
560     other_devtools_client_->GetRuntime()->Evaluate(
561         "24 * 7",
562         base::BindOnce(&HeadlessDevToolsClientAttachTest::OnFirstResult,
563                        base::Unretained(this)));
564   }
565 
OnFirstResult(std::unique_ptr<runtime::EvaluateResult> result)566   void OnFirstResult(std::unique_ptr<runtime::EvaluateResult> result) {
567     EXPECT_TRUE(result->GetResult()->HasValue());
568     EXPECT_EQ(24 * 7, result->GetResult()->GetValue()->GetInt());
569 
570     HeadlessDevToolsTarget* devtools_target =
571         web_contents_->GetDevToolsTarget();
572 
573     EXPECT_TRUE(devtools_target->IsAttached());
574     devtools_target->DetachClient(other_devtools_client_.get());
575     EXPECT_FALSE(devtools_target->IsAttached());
576     devtools_target->AttachClient(devtools_client_.get());
577     EXPECT_TRUE(devtools_target->IsAttached());
578 
579     devtools_client_->GetRuntime()->Evaluate(
580         "27 * 4",
581         base::BindOnce(&HeadlessDevToolsClientAttachTest::OnSecondResult,
582                        base::Unretained(this)));
583   }
584 
OnSecondResult(std::unique_ptr<runtime::EvaluateResult> result)585   void OnSecondResult(std::unique_ptr<runtime::EvaluateResult> result) {
586     EXPECT_TRUE(result->GetResult()->HasValue());
587     EXPECT_EQ(27 * 4, result->GetResult()->GetValue()->GetInt());
588 
589     // If everything worked, this call will not crash, since it
590     // detaches devtools_client_.
591     FinishAsynchronousTest();
592   }
593 
594  protected:
595   std::unique_ptr<HeadlessDevToolsClient> other_devtools_client_;
596 };
597 
598 #if defined(OS_WIN)
599 // TODO(crbug.com/1045980): Disabled due to flakiness.
600 DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientAttachTest);
601 #else
602 HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientAttachTest);
603 #endif
604 
605 class HeadlessDevToolsMethodCallErrorTest
606     : public HeadlessAsyncDevTooledBrowserTest,
607       public page::Observer {
608  public:
RunDevTooledTest()609   void RunDevTooledTest() override {
610     EXPECT_TRUE(embedded_test_server()->Start());
611     base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
612     devtools_client_->GetPage()->AddObserver(this);
613     devtools_client_->GetPage()->Enable(run_loop.QuitClosure());
614     run_loop.Run();
615     devtools_client_->GetPage()->Navigate(
616         embedded_test_server()->GetURL("/hello.html").spec());
617   }
618 
OnLoadEventFired(const page::LoadEventFiredParams & params)619   void OnLoadEventFired(const page::LoadEventFiredParams& params) override {
620     devtools_client_->GetPage()->GetExperimental()->RemoveObserver(this);
621     devtools_client_->GetDOM()->GetDocument(
622         base::BindOnce(&HeadlessDevToolsMethodCallErrorTest::OnGetDocument,
623                        base::Unretained(this)));
624   }
625 
OnGetDocument(std::unique_ptr<dom::GetDocumentResult> result)626   void OnGetDocument(std::unique_ptr<dom::GetDocumentResult> result) {
627     devtools_client_->GetDOM()->QuerySelector(
628         dom::QuerySelectorParams::Builder()
629             .SetNodeId(result->GetRoot()->GetNodeId())
630             .SetSelector("<o_O>")
631             .Build(),
632         base::BindOnce(&HeadlessDevToolsMethodCallErrorTest::OnQuerySelector,
633                        base::Unretained(this)));
634   }
635 
OnQuerySelector(std::unique_ptr<dom::QuerySelectorResult> result)636   void OnQuerySelector(std::unique_ptr<dom::QuerySelectorResult> result) {
637     EXPECT_EQ(nullptr, result);
638     FinishAsynchronousTest();
639   }
640 };
641 
642 #if defined(OS_WIN)
643 // TODO(crbug.com/1045980): Disabled due to flakiness.
644 DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsMethodCallErrorTest);
645 #else
646 HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsMethodCallErrorTest);
647 #endif
648 
649 class HeadlessDevToolsNetworkBlockedUrlTest
650     : public HeadlessAsyncDevTooledBrowserTest,
651       public page::Observer,
652       public network::Observer {
653  public:
RunDevTooledTest()654   void RunDevTooledTest() override {
655     EXPECT_TRUE(embedded_test_server()->Start());
656     base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
657     devtools_client_->GetPage()->AddObserver(this);
658     devtools_client_->GetPage()->Enable();
659     devtools_client_->GetNetwork()->AddObserver(this);
660     devtools_client_->GetNetwork()->Enable(run_loop.QuitClosure());
661     run_loop.Run();
662     std::vector<std::string> blockedUrls;
663     blockedUrls.push_back("dom_tree_test.css");
664     devtools_client_->GetNetwork()->GetExperimental()->SetBlockedURLs(
665         network::SetBlockedURLsParams::Builder().SetUrls(blockedUrls).Build());
666     devtools_client_->GetPage()->Navigate(
667         embedded_test_server()->GetURL("/dom_tree_test.html").spec());
668   }
669 
GetUrlPath(const std::string & url) const670   std::string GetUrlPath(const std::string& url) const {
671     GURL gurl(url);
672     return gurl.path();
673   }
674 
OnRequestWillBeSent(const network::RequestWillBeSentParams & params)675   void OnRequestWillBeSent(
676       const network::RequestWillBeSentParams& params) override {
677     std::string path = GetUrlPath(params.GetRequest()->GetUrl());
678     requests_to_be_sent_.push_back(path);
679     request_id_to_path_[params.GetRequestId()] = path;
680   }
681 
OnResponseReceived(const network::ResponseReceivedParams & params)682   void OnResponseReceived(
683       const network::ResponseReceivedParams& params) override {
684     responses_received_.push_back(GetUrlPath(params.GetResponse()->GetUrl()));
685   }
686 
OnLoadingFailed(const network::LoadingFailedParams & failed)687   void OnLoadingFailed(const network::LoadingFailedParams& failed) override {
688     failures_.push_back(request_id_to_path_[failed.GetRequestId()]);
689     EXPECT_EQ(network::BlockedReason::INSPECTOR, failed.GetBlockedReason());
690   }
691 
OnLoadEventFired(const page::LoadEventFiredParams &)692   void OnLoadEventFired(const page::LoadEventFiredParams&) override {
693     EXPECT_THAT(
694         requests_to_be_sent_,
695         testing::UnorderedElementsAre("/dom_tree_test.html",
696                                       "/dom_tree_test.css", "/iframe.html"));
697     EXPECT_THAT(responses_received_,
698                 ElementsAre("/dom_tree_test.html", "/iframe.html"));
699     EXPECT_THAT(failures_, ElementsAre("/dom_tree_test.css"));
700     FinishAsynchronousTest();
701   }
702 
703   std::map<std::string, std::string> request_id_to_path_;
704   std::vector<std::string> requests_to_be_sent_;
705   std::vector<std::string> responses_received_;
706   std::vector<std::string> failures_;
707 };
708 
709 #if defined(OS_WIN)
710 // TODO(crbug.com/1045980): Disabled due to flakiness.
711 DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsNetworkBlockedUrlTest);
712 #else
713 HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsNetworkBlockedUrlTest);
714 #endif
715 
716 class DevToolsNetworkOfflineEmulationTest
717     : public HeadlessAsyncDevTooledBrowserTest,
718       public page::Observer,
719       public network::Observer {
RunDevTooledTest()720   void RunDevTooledTest() override {
721     EXPECT_TRUE(embedded_test_server()->Start());
722     base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
723     devtools_client_->GetPage()->AddObserver(this);
724     devtools_client_->GetPage()->Enable();
725     devtools_client_->GetNetwork()->AddObserver(this);
726     devtools_client_->GetNetwork()->Enable(run_loop.QuitClosure());
727     run_loop.Run();
728     std::unique_ptr<network::EmulateNetworkConditionsParams> params =
729         network::EmulateNetworkConditionsParams::Builder()
730             .SetOffline(true)
731             .SetLatency(0)
732             .SetDownloadThroughput(0)
733             .SetUploadThroughput(0)
734             .Build();
735     devtools_client_->GetNetwork()->EmulateNetworkConditions(std::move(params));
736     devtools_client_->GetPage()->Navigate(
737         embedded_test_server()->GetURL("/hello.html").spec());
738   }
739 
OnLoadingFailed(const network::LoadingFailedParams & failed)740   void OnLoadingFailed(const network::LoadingFailedParams& failed) override {
741     EXPECT_EQ("net::ERR_INTERNET_DISCONNECTED", failed.GetErrorText());
742     FinishAsynchronousTest();
743   }
744 };
745 
746 #if defined(OS_WIN)
747 // TODO(crbug.com/1045980): Disabled due to flakiness.
748 DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(DevToolsNetworkOfflineEmulationTest);
749 #else
750 HEADLESS_ASYNC_DEVTOOLED_TEST_F(DevToolsNetworkOfflineEmulationTest);
751 #endif
752 
753 class RawDevtoolsProtocolTest
754     : public HeadlessAsyncDevTooledBrowserTest,
755       public HeadlessDevToolsClient::RawProtocolListener {
756  public:
RunDevTooledTest()757   void RunDevTooledTest() override {
758     devtools_client_->SetRawProtocolListener(this);
759 
760     base::DictionaryValue message;
761     message.SetInteger("id", devtools_client_->GetNextRawDevToolsMessageId());
762     message.SetString("method", "Runtime.evaluate");
763     std::unique_ptr<base::DictionaryValue> params(new base::DictionaryValue());
764     params->SetString("expression", "1+1");
765     message.Set("params", std::move(params));
766     std::string json_message;
767     base::JSONWriter::Write(message, &json_message);
768     devtools_client_->SendRawDevToolsMessage(json_message);
769   }
770 
OnProtocolMessage(base::span<const uint8_t> json_message,const base::DictionaryValue & parsed_message)771   bool OnProtocolMessage(base::span<const uint8_t> json_message,
772                          const base::DictionaryValue& parsed_message) override {
773     EXPECT_EQ(
774         "{\"id\":1,\"result\":{\"result\":{\"type\":\"number\","
775         "\"value\":2,\"description\":\"2\"}}}",
776         std::string(json_message.begin(), json_message.end()));
777 
778     FinishAsynchronousTest();
779     return true;
780   }
781 };
782 
783 #if defined(OS_WIN)
784 // TODO(crbug.com/1045980): Disabled due to flakiness.
785 DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(RawDevtoolsProtocolTest);
786 #else
787 HEADLESS_ASYNC_DEVTOOLED_TEST_F(RawDevtoolsProtocolTest);
788 #endif
789 
790 class DevToolsAttachAndDetachNotifications
791     : public HeadlessAsyncDevTooledBrowserTest {
792  public:
DevToolsClientAttached()793   void DevToolsClientAttached() override { dev_tools_client_attached_ = true; }
794 
RunDevTooledTest()795   void RunDevTooledTest() override {
796     EXPECT_TRUE(dev_tools_client_attached_);
797     FinishAsynchronousTest();
798   }
799 
DevToolsClientDetached()800   void DevToolsClientDetached() override { dev_tools_client_detached_ = true; }
801 
TearDownOnMainThread()802   void TearDownOnMainThread() override {
803     EXPECT_TRUE(dev_tools_client_detached_);
804   }
805 
806  private:
807   bool dev_tools_client_attached_ = false;
808   bool dev_tools_client_detached_ = false;
809 };
810 
811 #if defined(OS_WIN)
812 // TODO(crbug.com/1045980): Disabled due to flakiness.
813 DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(DevToolsAttachAndDetachNotifications);
814 #else
815 HEADLESS_ASYNC_DEVTOOLED_TEST_F(DevToolsAttachAndDetachNotifications);
816 #endif
817 
818 class DomTreeExtractionBrowserTest : public HeadlessAsyncDevTooledBrowserTest,
819                                      public page::Observer {
820  public:
RunDevTooledTest()821   void RunDevTooledTest() override {
822     EXPECT_TRUE(embedded_test_server()->Start());
823     devtools_client_->GetPage()->AddObserver(this);
824     devtools_client_->GetPage()->Enable();
825     devtools_client_->GetPage()->Navigate(
826         embedded_test_server()->GetURL("/dom_tree_test.html").spec());
827   }
828 
OnLoadEventFired(const page::LoadEventFiredParams & params)829   void OnLoadEventFired(const page::LoadEventFiredParams& params) override {
830     devtools_client_->GetPage()->Disable();
831     devtools_client_->GetPage()->RemoveObserver(this);
832 
833     std::vector<std::string> css_whitelist = {
834         "color",       "display",      "font-style", "font-family",
835         "margin-left", "margin-right", "margin-top", "margin-bottom"};
836     devtools_client_->GetDOMSnapshot()->GetExperimental()->GetSnapshot(
837         dom_snapshot::GetSnapshotParams::Builder()
838             .SetComputedStyleWhitelist(std::move(css_whitelist))
839             .Build(),
840         base::BindOnce(&DomTreeExtractionBrowserTest::OnGetSnapshotResult,
841                        base::Unretained(this)));
842   }
843 
OnGetSnapshotResult(std::unique_ptr<dom_snapshot::GetSnapshotResult> result)844   void OnGetSnapshotResult(
845       std::unique_ptr<dom_snapshot::GetSnapshotResult> result) {
846     GURL::Replacements replace_port;
847     replace_port.SetPortStr("");
848 
849     std::vector<std::unique_ptr<base::DictionaryValue>> dom_nodes(
850         result->GetDomNodes()->size());
851 
852     // For convenience, flatten the dom tree into an array of dicts.
853     for (size_t i = 0; i < result->GetDomNodes()->size(); i++) {
854       dom_snapshot::DOMNode* node = (*result->GetDomNodes())[i].get();
855 
856       dom_nodes[i].reset(
857           static_cast<base::DictionaryValue*>(node->Serialize().release()));
858       base::DictionaryValue* node_dict = dom_nodes[i].get();
859 
860       // Node IDs are assigned in a non deterministic way.
861       if (node_dict->FindKey("backendNodeId"))
862         node_dict->SetString("backendNodeId", "?");
863 
864       // Frame IDs are random.
865       if (node_dict->FindKey("frameId"))
866         node_dict->SetString("frameId", "?");
867 
868       // Ports are random.
869       if (base::Value* base_url_value = node_dict->FindKey("baseURL")) {
870         node_dict->SetString("baseURL", GURL(base_url_value->GetString())
871                                             .ReplaceComponents(replace_port)
872                                             .spec());
873       }
874 
875       if (base::Value* document_url_value = node_dict->FindKey("documentURL")) {
876         node_dict->SetString("documentURL",
877                              GURL(document_url_value->GetString())
878                                  .ReplaceComponents(replace_port)
879                                  .spec());
880       }
881 
882       // Merge LayoutTreeNode data into the dictionary.
883       if (base::Value* layout_node_index_value =
884           node_dict->FindKey("layoutNodeIndex")) {
885         int layout_node_index = layout_node_index_value->GetInt();
886         ASSERT_LE(0, layout_node_index);
887         ASSERT_GT(result->GetLayoutTreeNodes()->size(),
888                   static_cast<size_t>(layout_node_index));
889         const std::unique_ptr<dom_snapshot::LayoutTreeNode>& layout_node =
890             (*result->GetLayoutTreeNodes())[layout_node_index];
891 
892         node_dict->Set("boundingBox",
893                        layout_node->GetBoundingBox()->Serialize());
894 
895         if (layout_node->HasLayoutText())
896           node_dict->SetString("layoutText", layout_node->GetLayoutText());
897 
898         if (layout_node->HasStyleIndex())
899           node_dict->SetInteger("styleIndex", layout_node->GetStyleIndex());
900 
901         if (layout_node->HasInlineTextNodes()) {
902           std::unique_ptr<base::ListValue> inline_text_nodes(
903               new base::ListValue());
904           for (const std::unique_ptr<dom_snapshot::InlineTextBox>&
905                    inline_text_box : *layout_node->GetInlineTextNodes()) {
906             size_t index = inline_text_nodes->GetSize();
907             inline_text_nodes->Set(index, inline_text_box->Serialize());
908           }
909           node_dict->Set("inlineTextNodes", std::move(inline_text_nodes));
910         }
911       }
912     }
913 
914     std::vector<std::unique_ptr<base::DictionaryValue>> computed_styles(
915         result->GetComputedStyles()->size());
916 
917     for (size_t i = 0; i < result->GetComputedStyles()->size(); i++) {
918       std::unique_ptr<base::DictionaryValue> style(new base::DictionaryValue());
919       for (const auto& style_property :
920            *(*result->GetComputedStyles())[i]->GetProperties()) {
921         style->SetString(style_property->GetName(), style_property->GetValue());
922       }
923       computed_styles[i] = std::move(style);
924     }
925 
926     base::ThreadRestrictions::SetIOAllowed(true);
927     base::FilePath source_root_dir;
928     base::PathService::Get(base::DIR_SOURCE_ROOT, &source_root_dir);
929     base::FilePath expected_dom_nodes_path =
930         source_root_dir.Append(FILE_PATH_LITERAL(
931             "headless/lib/dom_tree_extraction_expected_nodes.txt"));
932     std::string expected_dom_nodes;
933     ASSERT_TRUE(
934         base::ReadFileToString(expected_dom_nodes_path, &expected_dom_nodes));
935 
936     std::string dom_nodes_result;
937     for (size_t i = 0; i < dom_nodes.size(); i++) {
938       std::string result_json;
939       base::JSONWriter::WriteWithOptions(
940           *dom_nodes[i], base::JSONWriter::OPTIONS_PRETTY_PRINT, &result_json);
941 
942       dom_nodes_result += result_json;
943     }
944 
945 #if defined(OS_WIN)
946     ASSERT_TRUE(base::RemoveChars(dom_nodes_result, "\r", &dom_nodes_result));
947 #endif
948 
949     EXPECT_EQ(expected_dom_nodes, dom_nodes_result);
950 
951     base::FilePath expected_styles_path =
952         source_root_dir.Append(FILE_PATH_LITERAL(
953             "headless/lib/dom_tree_extraction_expected_styles.txt"));
954     std::string expected_computed_styles;
955     ASSERT_TRUE(base::ReadFileToString(expected_styles_path,
956                                        &expected_computed_styles));
957 
958     std::string computed_styles_result;
959     for (size_t i = 0; i < computed_styles.size(); i++) {
960       std::string result_json;
961       base::JSONWriter::WriteWithOptions(*computed_styles[i],
962                                          base::JSONWriter::OPTIONS_PRETTY_PRINT,
963                                          &result_json);
964 
965       computed_styles_result += result_json;
966     }
967 
968 #if defined(OS_WIN)
969     ASSERT_TRUE(base::RemoveChars(computed_styles_result, "\r",
970                                   &computed_styles_result));
971 #endif
972 
973     EXPECT_EQ(expected_computed_styles, computed_styles_result);
974     FinishAsynchronousTest();
975   }
976 };
977 
978 #if defined(OS_WIN)
979 // TODO(crbug.com/1045980): Disabled due to flakiness.
980 DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(DomTreeExtractionBrowserTest);
981 #else
982 HEADLESS_ASYNC_DEVTOOLED_TEST_F(DomTreeExtractionBrowserTest);
983 #endif
984 
985 // This feature uses network observation and works exactly and only for
986 // network::ErrorReason::BLOCKED_BY_CLIENT modifications that are initiated
987 // via network::ExperimentalObserver.
988 class BlockedByClient_NetworkObserver_Test
989     : public HeadlessAsyncDevTooledBrowserTest,
990       public network::ExperimentalObserver,
991       public page::Observer {
992  public:
RunDevTooledTest()993   void RunDevTooledTest() override {
994     ASSERT_TRUE(embedded_test_server()->Start());
995 
996     // Intercept all network requests.
997     devtools_client_->GetNetwork()->GetExperimental()->AddObserver(this);
998     devtools_client_->GetNetwork()->Enable();
999     std::vector<std::unique_ptr<network::RequestPattern>> patterns;
1000     patterns.emplace_back(
1001         network::RequestPattern::Builder().SetUrlPattern("*").Build());
1002     devtools_client_->GetNetwork()->GetExperimental()->SetRequestInterception(
1003         network::SetRequestInterceptionParams::Builder()
1004             .SetPatterns(std::move(patterns))
1005             .Build());
1006 
1007     // For observing OnLoadEventFired.
1008     devtools_client_->GetPage()->AddObserver(this);
1009     devtools_client_->GetPage()->Enable();
1010 
1011     devtools_client_->GetPage()->Navigate(
1012         embedded_test_server()->GetURL("/resource_cancel_test.html").spec());
1013   }
1014 
1015   // Overrides network::ExperimentalObserver.
OnRequestIntercepted(const network::RequestInterceptedParams & params)1016   void OnRequestIntercepted(
1017       const network::RequestInterceptedParams& params) override {
1018     urls_seen_.push_back(GURL(params.GetRequest()->GetUrl()).ExtractFileName());
1019 
1020     auto continue_intercept_params =
1021         network::ContinueInterceptedRequestParams::Builder()
1022             .SetInterceptionId(params.GetInterceptionId())
1023             .Build();
1024 
1025     // We *abort* fetching Ahem.ttf, and *fail* for test.jpg
1026     // to verify that both ways result in a failed loading event,
1027     // which we'll observe in OnLoadingFailed below.
1028     // Also, we abort iframe2.html because it turns out frame interception
1029     // uses a very different codepath than other resources.
1030     if (EndsWith(params.GetRequest()->GetUrl(), "/test.jpg",
1031                  base::CompareCase::SENSITIVE)) {
1032       continue_intercept_params->SetErrorReason(
1033           network::ErrorReason::BLOCKED_BY_CLIENT);
1034     } else if (EndsWith(params.GetRequest()->GetUrl(), "/Ahem.ttf",
1035                         base::CompareCase::SENSITIVE)) {
1036       continue_intercept_params->SetErrorReason(
1037           network::ErrorReason::BLOCKED_BY_CLIENT);
1038     } else if (EndsWith(params.GetRequest()->GetUrl(), "/iframe2.html",
1039                         base::CompareCase::SENSITIVE)) {
1040       continue_intercept_params->SetErrorReason(
1041           network::ErrorReason::BLOCKED_BY_CLIENT);
1042     }
1043 
1044     devtools_client_->GetNetwork()
1045         ->GetExperimental()
1046         ->ContinueInterceptedRequest(std::move(continue_intercept_params));
1047   }
1048 
1049   // Overrides network::ExperimentalObserver.
OnRequestWillBeSent(const network::RequestWillBeSentParams & params)1050   void OnRequestWillBeSent(
1051       const network::RequestWillBeSentParams& params) override {
1052     // Here, we just record the URLs (filenames) for each request ID, since
1053     // we won't have access to them in ::OnLoadingFailed below.
1054     urls_by_id_[params.GetRequestId()] =
1055         GURL(params.GetRequest()->GetUrl()).ExtractFileName();
1056   }
1057 
1058   // Overrides network::ExperimentalObserver.
OnLoadingFailed(const network::LoadingFailedParams & params)1059   void OnLoadingFailed(const network::LoadingFailedParams& params) override {
1060     // Record the failed loading events so we can verify below that we
1061     // received the events.
1062     urls_that_failed_to_load_.push_back(urls_by_id_[params.GetRequestId()]);
1063     EXPECT_EQ(network::BlockedReason::INSPECTOR, params.GetBlockedReason());
1064   }
1065 
1066   // Overrides page::ExperimentalObserver.
OnLoadEventFired(const page::LoadEventFiredParams &)1067   void OnLoadEventFired(const page::LoadEventFiredParams&) override {
1068     EXPECT_THAT(urls_that_failed_to_load_,
1069                 UnorderedElementsAre("test.jpg", "Ahem.ttf", "iframe2.html"));
1070     EXPECT_THAT(urls_seen_, UnorderedElementsAre("resource_cancel_test.html",
1071                                                  "dom_tree_test.css",
1072                                                  "test.jpg", "iframe.html",
1073                                                  "iframe2.html", "Ahem.ttf"));
1074     FinishAsynchronousTest();
1075   }
1076 
1077  private:
1078   std::vector<std::string> urls_seen_;
1079   std::vector<std::string> urls_that_failed_to_load_;
1080   std::map<std::string, std::string> urls_by_id_;
1081 };
1082 
1083 DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(BlockedByClient_NetworkObserver_Test);
1084 
1085 class DevToolsSetCookieTest : public HeadlessAsyncDevTooledBrowserTest,
1086                               public network::Observer {
1087  public:
RunDevTooledTest()1088   void RunDevTooledTest() override {
1089     EXPECT_TRUE(embedded_test_server()->Start());
1090     devtools_client_->GetNetwork()->AddObserver(this);
1091 
1092     base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
1093     devtools_client_->GetNetwork()->Enable(run_loop.QuitClosure());
1094     run_loop.Run();
1095 
1096     devtools_client_->GetPage()->Navigate(
1097         embedded_test_server()->GetURL("/set-cookie?cookie1").spec());
1098   }
1099 
OnResponseReceived(const network::ResponseReceivedParams & params)1100   void OnResponseReceived(
1101       const network::ResponseReceivedParams& params) override {
1102     EXPECT_NE(std::string::npos, params.GetResponse()->GetHeadersText().find(
1103                                      "Set-Cookie: cookie1"));
1104     FinishAsynchronousTest();
1105   }
1106 };
1107 
1108 #if defined(OS_WIN)
1109 // TODO(crbug.com/1045980): Disabled due to flakiness.
1110 DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(DevToolsSetCookieTest);
1111 #else
1112 HEADLESS_ASYNC_DEVTOOLED_TEST_F(DevToolsSetCookieTest);
1113 #endif
1114 
1115 class DevtoolsInterceptionWithAuthProxyTest
1116     : public HeadlessAsyncDevTooledBrowserTest,
1117       public network::ExperimentalObserver,
1118       public page::Observer {
1119  public:
DevtoolsInterceptionWithAuthProxyTest()1120   DevtoolsInterceptionWithAuthProxyTest()
1121       : proxy_server_(net::SpawnedTestServer::TYPE_BASIC_AUTH_PROXY,
1122                       base::FilePath(FILE_PATH_LITERAL("headless/test/data"))) {
1123   }
1124 
SetUp()1125   void SetUp() override {
1126     ASSERT_TRUE(proxy_server_.Start());
1127     HeadlessAsyncDevTooledBrowserTest::SetUp();
1128   }
1129 
RunDevTooledTest()1130   void RunDevTooledTest() override {
1131     EXPECT_TRUE(embedded_test_server()->Start());
1132     devtools_client_->GetNetwork()->GetExperimental()->AddObserver(this);
1133     devtools_client_->GetNetwork()->Enable();
1134     std::unique_ptr<headless::network::RequestPattern> match_all =
1135         headless::network::RequestPattern::Builder().SetUrlPattern("*").Build();
1136     std::vector<std::unique_ptr<headless::network::RequestPattern>> patterns;
1137     patterns.push_back(std::move(match_all));
1138     devtools_client_->GetNetwork()->GetExperimental()->SetRequestInterception(
1139         network::SetRequestInterceptionParams::Builder()
1140             .SetPatterns(std::move(patterns))
1141             .Build());
1142 
1143     devtools_client_->GetPage()->AddObserver(this);
1144 
1145     base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
1146     devtools_client_->GetPage()->Enable(run_loop.QuitClosure());
1147     run_loop.Run();
1148 
1149     devtools_client_->GetPage()->Navigate(
1150         embedded_test_server()->GetURL("/dom_tree_test.html").spec());
1151   }
1152 
OnRequestIntercepted(const network::RequestInterceptedParams & params)1153   void OnRequestIntercepted(
1154       const network::RequestInterceptedParams& params) override {
1155     if (params.HasAuthChallenge()) {
1156       auth_challenge_seen_ = true;
1157       devtools_client_->GetNetwork()
1158           ->GetExperimental()
1159           ->ContinueInterceptedRequest(
1160               network::ContinueInterceptedRequestParams::Builder()
1161                   .SetInterceptionId(params.GetInterceptionId())
1162                   .SetAuthChallengeResponse(
1163                       network::AuthChallengeResponse::Builder()
1164                           .SetResponse(network::AuthChallengeResponseResponse::
1165                                            PROVIDE_CREDENTIALS)
1166                           .SetUsername("foo")  // These are tested by the proxy.
1167                           .SetPassword("bar")
1168                           .Build())
1169                   .Build());
1170     } else {
1171       devtools_client_->GetNetwork()
1172           ->GetExperimental()
1173           ->ContinueInterceptedRequest(
1174               network::ContinueInterceptedRequestParams::Builder()
1175                   .SetInterceptionId(params.GetInterceptionId())
1176                   .Build());
1177       GURL url(params.GetRequest()->GetUrl());
1178       files_loaded_.insert(url.path());
1179     }
1180   }
1181 
OnLoadEventFired(const page::LoadEventFiredParams &)1182   void OnLoadEventFired(const page::LoadEventFiredParams&) override {
1183     EXPECT_TRUE(auth_challenge_seen_);
1184     EXPECT_THAT(files_loaded_,
1185                 ElementsAre("/Ahem.ttf", "/dom_tree_test.css",
1186                             "/dom_tree_test.html", "/iframe.html"));
1187     FinishAsynchronousTest();
1188   }
1189 
CustomizeHeadlessBrowserContext(HeadlessBrowserContext::Builder & builder)1190   void CustomizeHeadlessBrowserContext(
1191       HeadlessBrowserContext::Builder& builder) override {
1192     std::unique_ptr<net::ProxyConfig> proxy_config(new net::ProxyConfig);
1193     proxy_config->proxy_rules().ParseFromString(
1194         proxy_server_.host_port_pair().ToString());
1195     // TODO(https://crbug.com/901896): Don't rely on proxying localhost.
1196     proxy_config->proxy_rules().bypass_rules.AddRulesToSubtractImplicit();
1197     builder.SetProxyConfig(std::move(proxy_config));
1198   }
1199 
1200  private:
1201   net::SpawnedTestServer proxy_server_;
1202   bool auth_challenge_seen_ = false;
1203   std::set<std::string> files_loaded_;
1204 };
1205 
1206 #if defined(OS_WIN)
1207 // TODO(crbug.com/1045980): Disabled due to flakiness.
1208 DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(DevtoolsInterceptionWithAuthProxyTest);
1209 #else
1210 HEADLESS_ASYNC_DEVTOOLED_TEST_F(DevtoolsInterceptionWithAuthProxyTest);
1211 #endif
1212 
1213 class NavigatorLanguages : public HeadlessAsyncDevTooledBrowserTest {
1214  public:
RunDevTooledTest()1215   void RunDevTooledTest() override {
1216     devtools_client_->GetRuntime()->Evaluate(
1217         "JSON.stringify(navigator.languages)",
1218         base::BindOnce(&NavigatorLanguages::OnResult, base::Unretained(this)));
1219   }
1220 
OnResult(std::unique_ptr<runtime::EvaluateResult> result)1221   void OnResult(std::unique_ptr<runtime::EvaluateResult> result) {
1222     EXPECT_TRUE(result->GetResult()->HasValue());
1223     EXPECT_EQ("[\"en-UK\",\"DE\",\"FR\"]",
1224               result->GetResult()->GetValue()->GetString());
1225     FinishAsynchronousTest();
1226   }
1227 
CustomizeHeadlessBrowserContext(HeadlessBrowserContext::Builder & builder)1228   void CustomizeHeadlessBrowserContext(
1229       HeadlessBrowserContext::Builder& builder) override {
1230     builder.SetAcceptLanguage("en-UK, DE, FR");
1231   }
1232 };
1233 
1234 #if defined(OS_WIN)
1235 // TODO(crbug.com/1045980): Disabled due to flakiness.
1236 DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(NavigatorLanguages);
1237 #else
1238 HEADLESS_ASYNC_DEVTOOLED_TEST_F(NavigatorLanguages);
1239 #endif
1240 
1241 }  // namespace headless
1242