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 "headless/test/headless_browser_test.h"
6 
7 #include <memory>
8 
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/files/file_path.h"
12 #include "base/macros.h"
13 #include "base/path_service.h"
14 #include "base/run_loop.h"
15 #include "base/task/current_thread.h"
16 #include "build/build_config.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/render_process_host.h"
19 #include "content/public/common/url_constants.h"
20 #include "content/public/test/browser_test_utils.h"
21 #include "content/public/test/test_navigation_observer.h"
22 #include "headless/lib/browser/headless_browser_impl.h"
23 #include "headless/lib/browser/headless_web_contents_impl.h"
24 #include "headless/lib/headless_content_main_delegate.h"
25 #include "headless/public/devtools/domains/emulation.h"
26 #include "headless/public/devtools/domains/runtime.h"
27 #include "headless/public/headless_devtools_client.h"
28 #include "headless/public/headless_devtools_target.h"
29 #include "headless/public/headless_web_contents.h"
30 #include "testing/gtest/include/gtest/gtest.h"
31 #include "ui/gfx/geometry/size.h"
32 #include "ui/gl/gl_switches.h"
33 #include "url/gurl.h"
34 
35 namespace headless {
36 namespace {
37 
38 class SynchronousLoadObserver {
39  public:
SynchronousLoadObserver(HeadlessBrowserTest * browser_test,HeadlessWebContents * web_contents)40   SynchronousLoadObserver(HeadlessBrowserTest* browser_test,
41                           HeadlessWebContents* web_contents)
42       : web_contents_(web_contents),
43         devtools_client_(HeadlessDevToolsClient::Create()) {
44     web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get());
45     load_observer_.reset(new LoadObserver(
46         devtools_client_.get(),
47         base::BindOnce(&HeadlessBrowserTest::FinishAsynchronousTest,
48                        base::Unretained(browser_test))));
49   }
50 
~SynchronousLoadObserver()51   ~SynchronousLoadObserver() {
52     web_contents_->GetDevToolsTarget()->DetachClient(devtools_client_.get());
53   }
54 
navigation_succeeded() const55   bool navigation_succeeded() const {
56     return load_observer_->navigation_succeeded();
57   }
58 
59  private:
60   HeadlessWebContents* web_contents_;  // Not owned.
61   std::unique_ptr<HeadlessDevToolsClient> devtools_client_;
62   std::unique_ptr<LoadObserver> load_observer_;
63 };
64 
65 class EvaluateHelper {
66  public:
EvaluateHelper(HeadlessBrowserTest * browser_test,HeadlessWebContents * web_contents,const std::string & script_to_eval)67   EvaluateHelper(HeadlessBrowserTest* browser_test,
68                  HeadlessWebContents* web_contents,
69                  const std::string& script_to_eval)
70       : browser_test_(browser_test),
71         web_contents_(web_contents),
72         devtools_client_(HeadlessDevToolsClient::Create()) {
73     web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get());
74     devtools_client_->GetRuntime()->Evaluate(
75         script_to_eval, base::BindOnce(&EvaluateHelper::OnEvaluateResult,
76                                        base::Unretained(this)));
77   }
78 
~EvaluateHelper()79   ~EvaluateHelper() {
80     web_contents_->GetDevToolsTarget()->DetachClient(devtools_client_.get());
81   }
82 
OnEvaluateResult(std::unique_ptr<runtime::EvaluateResult> result)83   void OnEvaluateResult(std::unique_ptr<runtime::EvaluateResult> result) {
84     result_ = std::move(result);
85     browser_test_->FinishAsynchronousTest();
86   }
87 
TakeResult()88   std::unique_ptr<runtime::EvaluateResult> TakeResult() {
89     return std::move(result_);
90   }
91 
92  private:
93   HeadlessBrowserTest* browser_test_;  // Not owned.
94   HeadlessWebContents* web_contents_;  // Not owned.
95   std::unique_ptr<HeadlessDevToolsClient> devtools_client_;
96 
97   std::unique_ptr<runtime::EvaluateResult> result_;
98 
99   DISALLOW_COPY_AND_ASSIGN(EvaluateHelper);
100 };
101 
102 }  // namespace
103 
LoadObserver(HeadlessDevToolsClient * devtools_client,base::OnceClosure callback)104 LoadObserver::LoadObserver(HeadlessDevToolsClient* devtools_client,
105                            base::OnceClosure callback)
106     : callback_(std::move(callback)),
107       devtools_client_(devtools_client),
108       navigation_succeeded_(true) {
109   devtools_client_->GetNetwork()->AddObserver(this);
110   devtools_client_->GetNetwork()->Enable();
111   devtools_client_->GetPage()->AddObserver(this);
112   devtools_client_->GetPage()->Enable();
113 }
114 
~LoadObserver()115 LoadObserver::~LoadObserver() {
116   devtools_client_->GetNetwork()->RemoveObserver(this);
117   devtools_client_->GetPage()->RemoveObserver(this);
118 }
119 
OnLoadEventFired(const page::LoadEventFiredParams & params)120 void LoadObserver::OnLoadEventFired(const page::LoadEventFiredParams& params) {
121   std::move(callback_).Run();
122 }
123 
OnResponseReceived(const network::ResponseReceivedParams & params)124 void LoadObserver::OnResponseReceived(
125     const network::ResponseReceivedParams& params) {
126   if (params.GetResponse()->GetStatus() != 200 ||
127       params.GetResponse()->GetUrl() == content::kUnreachableWebDataURL) {
128     navigation_succeeded_ = false;
129   }
130 }
131 
HeadlessBrowserTest()132 HeadlessBrowserTest::HeadlessBrowserTest() {
133 #if defined(OS_MAC)
134   // On Mac the source root is not set properly. We override it by assuming
135   // that is two directories up from the execution test file.
136   base::FilePath dir_exe_path;
137   CHECK(base::PathService::Get(base::DIR_EXE, &dir_exe_path));
138   dir_exe_path = dir_exe_path.Append("../../");
139   CHECK(base::PathService::Override(base::DIR_SOURCE_ROOT, dir_exe_path));
140 #endif  // defined(OS_MAC)
141   base::FilePath headless_test_data(FILE_PATH_LITERAL("headless/test/data"));
142   CreateTestServer(headless_test_data);
143 }
144 
SetUp()145 void HeadlessBrowserTest::SetUp() {
146   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
147   // Enable GPU usage (i.e., SwiftShader, hardware GL on macOS) in all tests
148   // since that's the default configuration of --headless.
149   command_line->AppendSwitch(switches::kUseGpuInTests);
150   SetUpCommandLine(command_line);
151   BrowserTestBase::SetUp();
152 }
153 
SetUpWithoutGPU()154 void HeadlessBrowserTest::SetUpWithoutGPU() {
155   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
156   SetUpCommandLine(command_line);
157   BrowserTestBase::SetUp();
158 }
159 
160 HeadlessBrowserTest::~HeadlessBrowserTest() = default;
161 
PreRunTestOnMainThread()162 void HeadlessBrowserTest::PreRunTestOnMainThread() {
163   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
164   // Pump startup related events.
165   base::RunLoop().RunUntilIdle();
166 }
167 
PostRunTestOnMainThread()168 void HeadlessBrowserTest::PostRunTestOnMainThread() {
169   browser()->Shutdown();
170   for (content::RenderProcessHost::iterator i(
171            content::RenderProcessHost::AllHostsIterator());
172        !i.IsAtEnd(); i.Advance()) {
173     i.GetCurrentValue()->FastShutdownIfPossible();
174   }
175   // Pump tasks produced during shutdown.
176   base::RunLoop().RunUntilIdle();
177 }
178 
browser() const179 HeadlessBrowser* HeadlessBrowserTest::browser() const {
180   return HeadlessContentMainDelegate::GetInstance()->browser();
181 }
182 
options() const183 HeadlessBrowser::Options* HeadlessBrowserTest::options() const {
184   return HeadlessContentMainDelegate::GetInstance()->browser()->options();
185 }
186 
WaitForLoad(HeadlessWebContents * web_contents)187 bool HeadlessBrowserTest::WaitForLoad(HeadlessWebContents* web_contents) {
188   HeadlessWebContentsImpl* web_contents_impl =
189       HeadlessWebContentsImpl::From(web_contents);
190   content::TestNavigationObserver observer(web_contents_impl->web_contents(),
191                                            1);
192   observer.Wait();
193   return observer.last_navigation_succeeded();
194 }
195 
WaitForLoadAndGainFocus(HeadlessWebContents * web_contents)196 void HeadlessBrowserTest::WaitForLoadAndGainFocus(
197     HeadlessWebContents* web_contents) {
198   content::WebContents* content =
199       HeadlessWebContentsImpl::From(web_contents)->web_contents();
200 
201   // To finish loading and to gain focus are two independent events. Which one
202   // is issued first is undefined. The following code is waiting on both, in any
203   // order.
204   content::TestNavigationObserver load_observer(content, 1);
205   content::FrameFocusedObserver focus_observer(content->GetMainFrame());
206   load_observer.Wait();
207   focus_observer.Wait();
208 }
209 
EvaluateScript(HeadlessWebContents * web_contents,const std::string & script)210 std::unique_ptr<runtime::EvaluateResult> HeadlessBrowserTest::EvaluateScript(
211     HeadlessWebContents* web_contents,
212     const std::string& script) {
213   EvaluateHelper helper(this, web_contents, script);
214   RunAsynchronousTest();
215   return helper.TakeResult();
216 }
217 
RunAsynchronousTest()218 void HeadlessBrowserTest::RunAsynchronousTest() {
219   EXPECT_FALSE(run_loop_);
220   run_loop_ = std::make_unique<base::RunLoop>(
221       base::RunLoop::Type::kNestableTasksAllowed);
222   run_loop_->Run();
223   run_loop_ = nullptr;
224 }
225 
FinishAsynchronousTest()226 void HeadlessBrowserTest::FinishAsynchronousTest() {
227   run_loop_->Quit();
228 }
229 
HeadlessAsyncDevTooledBrowserTest()230 HeadlessAsyncDevTooledBrowserTest::HeadlessAsyncDevTooledBrowserTest()
231     : browser_context_(nullptr),
232       web_contents_(nullptr),
233       render_process_exited_(false) {}
234 
235 HeadlessAsyncDevTooledBrowserTest::~HeadlessAsyncDevTooledBrowserTest() =
236     default;
237 
DevToolsTargetReady()238 void HeadlessAsyncDevTooledBrowserTest::DevToolsTargetReady() {
239   EXPECT_TRUE(web_contents_->GetDevToolsTarget());
240   web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get());
241 #if defined(OS_MAC)
242   devtools_client_->GetEmulation()->SetDeviceMetricsOverride(
243       emulation::SetDeviceMetricsOverrideParams::Builder()
244           .SetWidth(0)
245           .SetHeight(0)
246           .SetDeviceScaleFactor(1)
247           .SetMobile(false)
248           .Build(),
249       base::BindOnce(
250           [](HeadlessAsyncDevTooledBrowserTest* self) {
251             self->RunDevTooledTest();
252           },
253           base::Unretained(this)));
254 #else
255   RunDevTooledTest();
256 #endif
257 }
258 
RenderProcessExited(base::TerminationStatus status,int exit_code)259 void HeadlessAsyncDevTooledBrowserTest::RenderProcessExited(
260     base::TerminationStatus status,
261     int exit_code) {
262   if (status == base::TERMINATION_STATUS_NORMAL_TERMINATION)
263     return;
264 
265   FinishAsynchronousTest();
266   render_process_exited_ = true;
267   FAIL() << "Abnormal renderer termination "
268          << "(status=" << status << ", exit_code=" << exit_code << ")";
269 }
270 
RunTest()271 void HeadlessAsyncDevTooledBrowserTest::RunTest() {
272   devtools_client_ = HeadlessDevToolsClient::Create();
273   browser_devtools_client_ = HeadlessDevToolsClient::Create();
274   interceptor_ = std::make_unique<TestNetworkInterceptor>();
275   HeadlessBrowserContext::Builder builder =
276       browser()->CreateBrowserContextBuilder();
277   CustomizeHeadlessBrowserContext(builder);
278   browser_context_ = builder.Build();
279 
280   browser()->SetDefaultBrowserContext(browser_context_);
281   browser()->GetDevToolsTarget()->AttachClient(browser_devtools_client_.get());
282 
283   HeadlessWebContents::Builder web_contents_builder =
284       browser_context_->CreateWebContentsBuilder();
285   web_contents_builder.SetEnableBeginFrameControl(GetEnableBeginFrameControl());
286   CustomizeHeadlessWebContents(web_contents_builder);
287   web_contents_ = web_contents_builder.Build();
288 
289   web_contents_->AddObserver(this);
290 
291   RunAsynchronousTest();
292   interceptor_.reset();
293   if (!render_process_exited_)
294     web_contents_->GetDevToolsTarget()->DetachClient(devtools_client_.get());
295   web_contents_->RemoveObserver(this);
296   web_contents_->Close();
297   web_contents_ = nullptr;
298   browser()->GetDevToolsTarget()->DetachClient(browser_devtools_client_.get());
299   browser_context_->Close();
300   browser_context_ = nullptr;
301   // Let the tasks that might have beein scheduled during web contents
302   // being closed run (see https://crbug.com/1036627 for details).
303   base::RunLoop().RunUntilIdle();
304 }
305 
GetEnableBeginFrameControl()306 bool HeadlessAsyncDevTooledBrowserTest::GetEnableBeginFrameControl() {
307   return false;
308 }
309 
CustomizeHeadlessBrowserContext(HeadlessBrowserContext::Builder & builder)310 void HeadlessAsyncDevTooledBrowserTest::CustomizeHeadlessBrowserContext(
311     HeadlessBrowserContext::Builder& builder) {}
312 
CustomizeHeadlessWebContents(HeadlessWebContents::Builder & builder)313 void HeadlessAsyncDevTooledBrowserTest::CustomizeHeadlessWebContents(
314     HeadlessWebContents::Builder& builder) {}
315 
316 }  // namespace headless
317