1 // Copyright (c) 2012 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 "content/public/test/test_utils.h"
6
7 #include <utility>
8
9 #include "base/base_switches.h"
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/location.h"
13 #include "base/macros.h"
14 #include "base/run_loop.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/task/current_thread.h"
19 #include "base/task/sequence_manager/sequence_manager.h"
20 #include "base/task/task_observer.h"
21 #include "base/task/thread_pool/thread_pool_instance.h"
22 #include "base/threading/thread_task_runner_handle.h"
23 #include "base/values.h"
24 #include "build/build_config.h"
25 #include "content/browser/renderer_host/render_frame_host_delegate.h"
26 #include "content/browser/renderer_host/render_frame_host_impl.h"
27 #include "content/common/content_navigation_policy.h"
28 #include "content/public/browser/browser_child_process_host_iterator.h"
29 #include "content/public/browser/browser_task_traits.h"
30 #include "content/public/browser/browser_thread.h"
31 #include "content/public/browser/notification_service.h"
32 #include "content/public/browser/render_frame_host.h"
33 #include "content/public/browser/render_process_host.h"
34 #include "content/public/browser/site_isolation_policy.h"
35 #include "content/public/browser/web_contents.h"
36 #include "content/public/common/content_features.h"
37 #include "content/public/common/content_switches.h"
38 #include "content/public/common/process_type.h"
39 #include "content/public/common/url_constants.h"
40 #include "content/public/test/test_launcher.h"
41 #include "testing/gtest/include/gtest/gtest.h"
42 #include "third_party/blink/public/common/fetch/fetch_api_request_headers_map.h"
43 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
44 #include "url/url_util.h"
45
46 namespace content {
47
48 namespace {
49
50 // Number of times to repost a Quit task so that the MessageLoop finishes up
51 // pending tasks and tasks posted by those pending tasks without risking the
52 // potential hang behavior of MessageLoop::QuitWhenIdle.
53 // The criteria for choosing this number: it should be high enough to make the
54 // quit act like QuitWhenIdle, while taking into account that any page which is
55 // animating may be rendering another frame for each quit deferral. For an
56 // animating page, the potential delay to quitting the RunLoop would be
57 // kNumQuitDeferrals * frame_render_time. Some perf tests run slow, such as
58 // 200ms/frame.
59 constexpr int kNumQuitDeferrals = 10;
60
DeferredQuitRunLoop(base::OnceClosure quit_task,int num_quit_deferrals)61 void DeferredQuitRunLoop(base::OnceClosure quit_task, int num_quit_deferrals) {
62 if (num_quit_deferrals <= 0) {
63 std::move(quit_task).Run();
64 } else {
65 base::ThreadTaskRunnerHandle::Get()->PostTask(
66 FROM_HERE, base::BindOnce(&DeferredQuitRunLoop, std::move(quit_task),
67 num_quit_deferrals - 1));
68 }
69 }
70
71 // Monitors if any task is processed by the message loop.
72 class TaskObserver : public base::TaskObserver {
73 public:
TaskObserver()74 TaskObserver() : processed_(false) {}
~TaskObserver()75 ~TaskObserver() override {}
76
77 // TaskObserver overrides.
WillProcessTask(const base::PendingTask & pending_task,bool was_blocked_or_low_priority)78 void WillProcessTask(const base::PendingTask& pending_task,
79 bool was_blocked_or_low_priority) override {}
DidProcessTask(const base::PendingTask & pending_task)80 void DidProcessTask(const base::PendingTask& pending_task) override {
81 if (base::EndsWith(pending_task.posted_from.file_name(), "base/run_loop.cc",
82 base::CompareCase::SENSITIVE)) {
83 // Don't consider RunLoop internal tasks (i.e. QuitClosure() reposted by
84 // ProxyToTaskRunner() or RunLoop timeouts) as actual work.
85 return;
86 }
87 processed_ = true;
88 }
89
90 // Returns true if any task was processed.
processed() const91 bool processed() const { return processed_; }
92
93 private:
94 bool processed_;
95 DISALLOW_COPY_AND_ASSIGN(TaskObserver);
96 };
97
98 // Adapter that makes a WindowedNotificationObserver::ConditionTestCallback from
99 // a WindowedNotificationObserver::ConditionTestCallbackWithoutSourceAndDetails
100 // by ignoring the notification source and details.
IgnoreSourceAndDetails(WindowedNotificationObserver::ConditionTestCallbackWithoutSourceAndDetails callback,const NotificationSource & source,const NotificationDetails & details)101 bool IgnoreSourceAndDetails(
102 WindowedNotificationObserver::ConditionTestCallbackWithoutSourceAndDetails
103 callback,
104 const NotificationSource& source,
105 const NotificationDetails& details) {
106 return std::move(callback).Run();
107 }
108
109 } // namespace
110
CreateFetchAPIRequest(const GURL & url,const std::string & method,const blink::FetchAPIRequestHeadersMap & headers,blink::mojom::ReferrerPtr referrer,bool is_reload)111 blink::mojom::FetchAPIRequestPtr CreateFetchAPIRequest(
112 const GURL& url,
113 const std::string& method,
114 const blink::FetchAPIRequestHeadersMap& headers,
115 blink::mojom::ReferrerPtr referrer,
116 bool is_reload) {
117 auto request = blink::mojom::FetchAPIRequest::New();
118 request->url = url;
119 request->method = method;
120 request->headers = headers;
121 request->referrer = std::move(referrer);
122 request->is_reload = is_reload;
123 return request;
124 }
125
RunMessageLoop()126 void RunMessageLoop() {
127 base::RunLoop run_loop;
128 RunThisRunLoop(&run_loop);
129 }
130
RunThisRunLoop(base::RunLoop * run_loop)131 void RunThisRunLoop(base::RunLoop* run_loop) {
132 base::CurrentThread::ScopedNestableTaskAllower allow;
133 run_loop->Run();
134 }
135
RunAllPendingInMessageLoop()136 void RunAllPendingInMessageLoop() {
137 DCHECK_CURRENTLY_ON(BrowserThread::UI);
138 RunAllPendingInMessageLoop(BrowserThread::UI);
139 }
140
RunAllPendingInMessageLoop(BrowserThread::ID thread_id)141 void RunAllPendingInMessageLoop(BrowserThread::ID thread_id) {
142 // See comment for |kNumQuitDeferrals| for why this is needed.
143 for (int i = 0; i <= kNumQuitDeferrals; ++i) {
144 BrowserThread::RunAllPendingTasksOnThreadForTesting(thread_id);
145 }
146 }
147
RunAllTasksUntilIdle()148 void RunAllTasksUntilIdle() {
149 while (true) {
150 // Setup a task observer to determine if MessageLoop tasks run in the
151 // current loop iteration and loop in case the MessageLoop posts tasks to
152 // the Task Scheduler after the initial flush.
153 TaskObserver task_observer;
154 base::CurrentThread::Get()->AddTaskObserver(&task_observer);
155
156 // This must use RunLoop::Type::kNestableTasksAllowed in case this
157 // RunAllTasksUntilIdle() call is nested inside an existing Run(). Without
158 // it, the QuitWhenIdleClosure() below would never run if it's posted from
159 // another thread (i.e.. by run_loop.cc's ProxyToTaskRunner).
160 base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
161
162 base::ThreadPoolInstance::Get()->FlushAsyncForTesting(
163 run_loop.QuitWhenIdleClosure());
164
165 run_loop.Run();
166
167 base::CurrentThread::Get()->RemoveTaskObserver(&task_observer);
168
169 if (!task_observer.processed())
170 break;
171 }
172 }
173
GetDeferredQuitTaskForRunLoop(base::RunLoop * run_loop)174 base::OnceClosure GetDeferredQuitTaskForRunLoop(base::RunLoop* run_loop) {
175 return base::BindOnce(&DeferredQuitRunLoop, run_loop->QuitClosure(),
176 kNumQuitDeferrals);
177 }
178
ExecuteScriptAndGetValue(RenderFrameHost * render_frame_host,const std::string & script)179 base::Value ExecuteScriptAndGetValue(RenderFrameHost* render_frame_host,
180 const std::string& script) {
181 base::RunLoop run_loop;
182 base::Value result;
183
184 render_frame_host->ExecuteJavaScriptForTests(
185 base::UTF8ToUTF16(script),
186 base::BindOnce(
187 [](base::OnceClosure quit_closure, base::Value* out_result,
188 base::Value value) {
189 *out_result = std::move(value);
190 std::move(quit_closure).Run();
191 },
192 run_loop.QuitWhenIdleClosure(), &result));
193 run_loop.Run();
194
195 return result;
196 }
197
AreAllSitesIsolatedForTesting()198 bool AreAllSitesIsolatedForTesting() {
199 return SiteIsolationPolicy::UseDedicatedProcessesForAllSites();
200 }
201
AreDefaultSiteInstancesEnabled()202 bool AreDefaultSiteInstancesEnabled() {
203 return !AreAllSitesIsolatedForTesting() &&
204 base::FeatureList::IsEnabled(
205 features::kProcessSharingWithDefaultSiteInstances);
206 }
207
IsolateAllSitesForTesting(base::CommandLine * command_line)208 void IsolateAllSitesForTesting(base::CommandLine* command_line) {
209 command_line->AppendSwitch(switches::kSitePerProcess);
210 }
211
CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()212 bool CanSameSiteMainFrameNavigationsChangeRenderFrameHosts() {
213 // TODO(crbug.com/936696): Also return true when RenderDocument for main frame
214 // is enabled.
215 return CanSameSiteMainFrameNavigationsChangeSiteInstances();
216 }
217
CanSameSiteMainFrameNavigationsChangeSiteInstances()218 bool CanSameSiteMainFrameNavigationsChangeSiteInstances() {
219 return IsProactivelySwapBrowsingInstanceOnSameSiteNavigationEnabled() ||
220 IsSameSiteBackForwardCacheEnabled();
221 }
222
DisableProactiveBrowsingInstanceSwapFor(RenderFrameHost * rfh)223 void DisableProactiveBrowsingInstanceSwapFor(RenderFrameHost* rfh) {
224 if (!CanSameSiteMainFrameNavigationsChangeSiteInstances())
225 return;
226 // If the RFH is not a main frame, navigations on it will never result in a
227 // proactive BrowsingInstance swap, so we shouldn't really call it on main
228 // frames.
229 DCHECK(!rfh->GetParent());
230 static_cast<RenderFrameHostImpl*>(rfh)
231 ->DisableProactiveBrowsingInstanceSwapForTesting();
232 }
233
GetWebUIURL(const std::string & host)234 GURL GetWebUIURL(const std::string& host) {
235 return GURL(GetWebUIURLString(host));
236 }
237
GetWebUIURLString(const std::string & host)238 std::string GetWebUIURLString(const std::string& host) {
239 return std::string(content::kChromeUIScheme) + url::kStandardSchemeSeparator +
240 host;
241 }
242
CreateAndAttachInnerContents(RenderFrameHost * rfh)243 WebContents* CreateAndAttachInnerContents(RenderFrameHost* rfh) {
244 WebContents* outer_contents =
245 static_cast<RenderFrameHostImpl*>(rfh)->delegate()->GetAsWebContents();
246 if (!outer_contents)
247 return nullptr;
248
249 WebContents::CreateParams inner_params(outer_contents->GetBrowserContext());
250
251 std::unique_ptr<WebContents> inner_contents_ptr =
252 WebContents::Create(inner_params);
253
254 // Attach. |inner_contents| becomes owned by |outer_contents|.
255 WebContents* inner_contents = inner_contents_ptr.get();
256 outer_contents->AttachInnerWebContents(std::move(inner_contents_ptr), rfh,
257 false /* is_full_page */);
258
259 return inner_contents;
260 }
261
AwaitDocumentOnLoadCompleted(WebContents * web_contents)262 void AwaitDocumentOnLoadCompleted(WebContents* web_contents) {
263 class Awaiter : public WebContentsObserver {
264 public:
265 explicit Awaiter(content::WebContents* web_contents)
266 : content::WebContentsObserver(web_contents),
267 observed_(web_contents->IsDocumentOnLoadCompletedInMainFrame()) {}
268
269 Awaiter(const Awaiter&) = delete;
270 Awaiter& operator=(const Awaiter&) = delete;
271
272 ~Awaiter() override = default;
273
274 void Await() {
275 if (!observed_)
276 run_loop_.Run();
277 DCHECK(web_contents()->IsDocumentOnLoadCompletedInMainFrame());
278 }
279
280 // WebContentsObserver:
281 void DocumentOnLoadCompletedInMainFrame() override {
282 observed_ = true;
283 if (run_loop_.running())
284 run_loop_.Quit();
285 }
286
287 private:
288 bool observed_ = false;
289 base::RunLoop run_loop_;
290 };
291
292 Awaiter(web_contents).Await();
293 }
294
MessageLoopRunner(QuitMode quit_mode)295 MessageLoopRunner::MessageLoopRunner(QuitMode quit_mode)
296 : quit_mode_(quit_mode) {}
297
298 MessageLoopRunner::~MessageLoopRunner() = default;
299
Run()300 void MessageLoopRunner::Run() {
301 DCHECK(thread_checker_.CalledOnValidThread());
302
303 // Do not run the message loop if our quit closure has already been called.
304 // This helps in scenarios where the closure has a chance to run before
305 // we Run explicitly.
306 if (quit_closure_called_)
307 return;
308
309 loop_running_ = true;
310 RunThisRunLoop(&run_loop_);
311 }
312
QuitClosure()313 base::OnceClosure MessageLoopRunner::QuitClosure() {
314 return base::BindOnce(&MessageLoopRunner::Quit, this);
315 }
316
Quit()317 void MessageLoopRunner::Quit() {
318 DCHECK(thread_checker_.CalledOnValidThread());
319
320 quit_closure_called_ = true;
321
322 // Only run the quit task if we are running the message loop.
323 if (loop_running_) {
324 switch (quit_mode_) {
325 case QuitMode::DEFERRED:
326 DeferredQuitRunLoop(run_loop_.QuitClosure(), kNumQuitDeferrals);
327 break;
328 case QuitMode::IMMEDIATE:
329 run_loop_.Quit();
330 break;
331 }
332 loop_running_ = false;
333 }
334 }
335
WindowedNotificationObserver(int notification_type,const NotificationSource & source)336 WindowedNotificationObserver::WindowedNotificationObserver(
337 int notification_type,
338 const NotificationSource& source)
339 : source_(NotificationService::AllSources()) {
340 AddNotificationType(notification_type, source);
341 }
342
WindowedNotificationObserver(int notification_type,ConditionTestCallback callback)343 WindowedNotificationObserver::WindowedNotificationObserver(
344 int notification_type,
345 ConditionTestCallback callback)
346 : callback_(std::move(callback)),
347 source_(NotificationService::AllSources()) {
348 AddNotificationType(notification_type, source_);
349 }
350
WindowedNotificationObserver(int notification_type,ConditionTestCallbackWithoutSourceAndDetails callback)351 WindowedNotificationObserver::WindowedNotificationObserver(
352 int notification_type,
353 ConditionTestCallbackWithoutSourceAndDetails callback)
354 : callback_(
355 base::BindRepeating(&IgnoreSourceAndDetails, std::move(callback))),
356 source_(NotificationService::AllSources()) {
357 registrar_.Add(this, notification_type, source_);
358 }
359
360 WindowedNotificationObserver::~WindowedNotificationObserver() = default;
361
AddNotificationType(int notification_type,const NotificationSource & source)362 void WindowedNotificationObserver::AddNotificationType(
363 int notification_type,
364 const NotificationSource& source) {
365 registrar_.Add(this, notification_type, source);
366 }
367
Wait()368 void WindowedNotificationObserver::Wait() {
369 if (!seen_)
370 run_loop_.Run();
371 EXPECT_TRUE(seen_);
372 }
373
Observe(int type,const NotificationSource & source,const NotificationDetails & details)374 void WindowedNotificationObserver::Observe(int type,
375 const NotificationSource& source,
376 const NotificationDetails& details) {
377 source_ = source;
378 details_ = details;
379 if (!callback_.is_null() && !callback_.Run(source, details))
380 return;
381
382 seen_ = true;
383 run_loop_.Quit();
384 }
385
InProcessUtilityThreadHelper()386 InProcessUtilityThreadHelper::InProcessUtilityThreadHelper() {
387 RenderProcessHost::SetRunRendererInProcess(true);
388 }
389
~InProcessUtilityThreadHelper()390 InProcessUtilityThreadHelper::~InProcessUtilityThreadHelper() {
391 JoinAllUtilityThreads();
392 RenderProcessHost::SetRunRendererInProcess(false);
393 }
394
JoinAllUtilityThreads()395 void InProcessUtilityThreadHelper::JoinAllUtilityThreads() {
396 ASSERT_FALSE(run_loop_);
397 run_loop_.emplace();
398 BrowserChildProcessObserver::Add(this);
399 CheckHasRunningChildProcess();
400 run_loop_->Run();
401 run_loop_.reset();
402 BrowserChildProcessObserver::Remove(this);
403 }
404
CheckHasRunningChildProcess()405 void InProcessUtilityThreadHelper::CheckHasRunningChildProcess() {
406 ASSERT_TRUE(run_loop_);
407
408 auto check_has_running_child_process_on_io =
409 [](base::OnceClosure quit_closure) {
410 BrowserChildProcessHostIterator it;
411 // If not Done(), we have some running child processes and need to wait.
412 if (it.Done())
413 std::move(quit_closure).Run();
414 };
415
416 GetIOThreadTaskRunner({})->PostTask(
417 FROM_HERE, base::BindOnce(check_has_running_child_process_on_io,
418 run_loop_->QuitClosure()));
419 }
420
BrowserChildProcessHostDisconnected(const ChildProcessData & data)421 void InProcessUtilityThreadHelper::BrowserChildProcessHostDisconnected(
422 const ChildProcessData& data) {
423 CheckHasRunningChildProcess();
424 }
425
RenderFrameDeletedObserver(RenderFrameHost * rfh)426 RenderFrameDeletedObserver::RenderFrameDeletedObserver(RenderFrameHost* rfh)
427 : WebContentsObserver(WebContents::FromRenderFrameHost(rfh)),
428 process_id_(rfh->GetProcess()->GetID()),
429 routing_id_(rfh->GetRoutingID()),
430 deleted_(false) {}
431
~RenderFrameDeletedObserver()432 RenderFrameDeletedObserver::~RenderFrameDeletedObserver() {}
433
RenderFrameDeleted(RenderFrameHost * render_frame_host)434 void RenderFrameDeletedObserver::RenderFrameDeleted(
435 RenderFrameHost* render_frame_host) {
436 if (render_frame_host->GetProcess()->GetID() == process_id_ &&
437 render_frame_host->GetRoutingID() == routing_id_) {
438 deleted_ = true;
439
440 if (runner_.get())
441 runner_->Quit();
442 }
443 }
444
deleted()445 bool RenderFrameDeletedObserver::deleted() {
446 return deleted_;
447 }
448
WaitUntilDeleted()449 void RenderFrameDeletedObserver::WaitUntilDeleted() {
450 if (deleted_)
451 return;
452
453 runner_.reset(new base::RunLoop());
454 runner_->Run();
455 runner_.reset();
456 }
457
WebContentsDestroyedWatcher(WebContents * web_contents)458 WebContentsDestroyedWatcher::WebContentsDestroyedWatcher(
459 WebContents* web_contents)
460 : WebContentsObserver(web_contents) {
461 EXPECT_TRUE(web_contents != nullptr);
462 }
463
~WebContentsDestroyedWatcher()464 WebContentsDestroyedWatcher::~WebContentsDestroyedWatcher() {
465 }
466
Wait()467 void WebContentsDestroyedWatcher::Wait() {
468 run_loop_.Run();
469 }
470
WebContentsDestroyed()471 void WebContentsDestroyedWatcher::WebContentsDestroyed() {
472 destroyed_ = true;
473 run_loop_.Quit();
474 }
475
TestPageScaleObserver(WebContents * web_contents)476 TestPageScaleObserver::TestPageScaleObserver(WebContents* web_contents)
477 : WebContentsObserver(web_contents) {}
478
~TestPageScaleObserver()479 TestPageScaleObserver::~TestPageScaleObserver() {}
480
OnPageScaleFactorChanged(float page_scale_factor)481 void TestPageScaleObserver::OnPageScaleFactorChanged(float page_scale_factor) {
482 last_scale_ = page_scale_factor;
483 seen_page_scale_change_ = true;
484 if (done_callback_)
485 std::move(done_callback_).Run();
486 }
487
WaitForPageScaleUpdate()488 float TestPageScaleObserver::WaitForPageScaleUpdate() {
489 if (!seen_page_scale_change_) {
490 base::RunLoop run_loop;
491 done_callback_ = run_loop.QuitClosure();
492 run_loop.Run();
493 }
494 seen_page_scale_change_ = false;
495 return last_scale_;
496 }
497
EffectiveURLContentBrowserClient(bool requires_dedicated_process)498 EffectiveURLContentBrowserClient::EffectiveURLContentBrowserClient(
499 bool requires_dedicated_process)
500 : requires_dedicated_process_(requires_dedicated_process) {}
501
EffectiveURLContentBrowserClient(const GURL & url_to_modify,const GURL & url_to_return,bool requires_dedicated_process)502 EffectiveURLContentBrowserClient::EffectiveURLContentBrowserClient(
503 const GURL& url_to_modify,
504 const GURL& url_to_return,
505 bool requires_dedicated_process)
506 : requires_dedicated_process_(requires_dedicated_process) {
507 AddTranslation(url_to_modify, url_to_return);
508 }
509
~EffectiveURLContentBrowserClient()510 EffectiveURLContentBrowserClient::~EffectiveURLContentBrowserClient() {}
511
AddTranslation(const GURL & url_to_modify,const GURL & url_to_return)512 void EffectiveURLContentBrowserClient::AddTranslation(
513 const GURL& url_to_modify,
514 const GURL& url_to_return) {
515 urls_to_modify_[url_to_modify] = url_to_return;
516 }
517
GetEffectiveURL(BrowserContext * browser_context,const GURL & url)518 GURL EffectiveURLContentBrowserClient::GetEffectiveURL(
519 BrowserContext* browser_context,
520 const GURL& url) {
521 auto it = urls_to_modify_.find(url);
522 if (it != urls_to_modify_.end())
523 return it->second;
524 return url;
525 }
526
DoesSiteRequireDedicatedProcess(BrowserContext * browser_context,const GURL & effective_site_url)527 bool EffectiveURLContentBrowserClient::DoesSiteRequireDedicatedProcess(
528 BrowserContext* browser_context,
529 const GURL& effective_site_url) {
530 if (!requires_dedicated_process_)
531 return false;
532
533 for (const auto& pair : urls_to_modify_) {
534 if (SiteInstance::GetSiteForURL(browser_context, pair.first) ==
535 effective_site_url)
536 return true;
537 }
538 return false;
539 }
540
541 } // namespace content
542