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