1 // Copyright 2019 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/browser/loader/prefetch_browsertest_base.h"
6 
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/task/post_task.h"
11 #include "content/browser/loader/prefetch_url_loader_service.h"
12 #include "content/browser/storage_partition_impl.h"
13 #include "content/browser/web_package/signed_exchange_handler.h"
14 #include "content/browser/web_package/signed_exchange_loader.h"
15 #include "content/public/browser/browser_context.h"
16 #include "content/public/browser/browser_task_traits.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/web_contents.h"
19 #include "content/public/test/browser_test_utils.h"
20 #include "content/shell/browser/shell.h"
21 #include "net/test/embedded_test_server/embedded_test_server.h"
22 #include "net/test/embedded_test_server/http_request.h"
23 
24 namespace content {
25 
26 PrefetchBrowserTestBase::ResponseEntry::ResponseEntry() = default;
27 
ResponseEntry(const std::string & content,const std::string & content_type,const std::vector<std::pair<std::string,std::string>> & headers,net::HttpStatusCode code)28 PrefetchBrowserTestBase::ResponseEntry::ResponseEntry(
29     const std::string& content,
30     const std::string& content_type,
31     const std::vector<std::pair<std::string, std::string>>& headers,
32     net::HttpStatusCode code)
33     : content(content),
34       content_type(content_type),
35       headers(headers),
36       code(code) {}
37 
38 PrefetchBrowserTestBase::ResponseEntry::ResponseEntry(ResponseEntry&& other) =
39     default;
40 
41 PrefetchBrowserTestBase::ResponseEntry::~ResponseEntry() = default;
42 
43 PrefetchBrowserTestBase::ResponseEntry& PrefetchBrowserTestBase::ResponseEntry::
44 operator=(ResponseEntry&& other) = default;
45 
46 PrefetchBrowserTestBase::ScopedSignedExchangeHandlerFactory::
ScopedSignedExchangeHandlerFactory(SignedExchangeHandlerFactory * factory)47     ScopedSignedExchangeHandlerFactory(SignedExchangeHandlerFactory* factory) {
48   SignedExchangeLoader::SetSignedExchangeHandlerFactoryForTest(factory);
49 }
50 
51 PrefetchBrowserTestBase::ScopedSignedExchangeHandlerFactory::
~ScopedSignedExchangeHandlerFactory()52     ~ScopedSignedExchangeHandlerFactory() {
53   SignedExchangeLoader::SetSignedExchangeHandlerFactoryForTest(nullptr);
54 }
55 
56 PrefetchBrowserTestBase::PrefetchBrowserTestBase() = default;
57 PrefetchBrowserTestBase::~PrefetchBrowserTestBase() = default;
58 
SetUpOnMainThread()59 void PrefetchBrowserTestBase::SetUpOnMainThread() {
60   ContentBrowserTest::SetUpOnMainThread();
61   StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>(
62       BrowserContext::GetDefaultStoragePartition(
63           shell()->web_contents()->GetBrowserContext()));
64   partition->GetPrefetchURLLoaderService()
65       ->RegisterPrefetchLoaderCallbackForTest(base::BindRepeating(
66           &PrefetchBrowserTestBase::OnPrefetchURLLoaderCalled,
67           base::Unretained(this)));
68 }
69 
RegisterResponse(const std::string & url,ResponseEntry && entry)70 void PrefetchBrowserTestBase::RegisterResponse(const std::string& url,
71                                                ResponseEntry&& entry) {
72   response_map_[url] = std::move(entry);
73 }
74 
75 std::unique_ptr<net::test_server::HttpResponse>
ServeResponses(const net::test_server::HttpRequest & request)76 PrefetchBrowserTestBase::ServeResponses(
77     const net::test_server::HttpRequest& request) {
78   auto found = response_map_.find(request.relative_url);
79   if (found != response_map_.end()) {
80     auto response = std::make_unique<net::test_server::BasicHttpResponse>();
81     response->set_code(found->second.code);
82     response->set_content(found->second.content);
83     response->set_content_type(found->second.content_type);
84     for (const auto& header : found->second.headers) {
85       response->AddCustomHeader(header.first, header.second);
86     }
87     return std::move(response);
88   }
89   return nullptr;
90 }
91 
OnPrefetchURLLoaderCalled()92 void PrefetchBrowserTestBase::OnPrefetchURLLoaderCalled() {
93   DCHECK_CURRENTLY_ON(BrowserThread::UI);
94   base::AutoLock lock(lock_);
95   prefetch_url_loader_called_++;
96 }
97 
GetPrefetchURLLoaderCallCount()98 int PrefetchBrowserTestBase::GetPrefetchURLLoaderCallCount() {
99   DCHECK_CURRENTLY_ON(BrowserThread::UI);
100   base::AutoLock lock(lock_);
101   return prefetch_url_loader_called_;
102 }
103 
RegisterRequestHandler(net::EmbeddedTestServer * test_server)104 void PrefetchBrowserTestBase::RegisterRequestHandler(
105     net::EmbeddedTestServer* test_server) {
106   test_server->RegisterRequestHandler(base::BindRepeating(
107       &PrefetchBrowserTestBase::ServeResponses, base::Unretained(this)));
108 }
109 
NavigateToURLAndWaitTitle(const GURL & url,const std::string & title)110 void PrefetchBrowserTestBase::NavigateToURLAndWaitTitle(
111     const GURL& url,
112     const std::string& title) {
113   base::string16 title16 = base::ASCIIToUTF16(title);
114   TitleWatcher title_watcher(shell()->web_contents(), title16);
115   // Execute the JavaScript code to triger the followup navigation from the
116   // current page.
117   EXPECT_TRUE(ExecuteScript(
118       shell()->web_contents(),
119       base::StringPrintf("location.href = '%s';", url.spec().c_str())));
120   EXPECT_EQ(title16, title_watcher.WaitAndGetTitle());
121 }
122 
WaitUntilLoaded(const GURL & url)123 void PrefetchBrowserTestBase::WaitUntilLoaded(const GURL& url) {
124   bool result = false;
125   ASSERT_TRUE(
126       ExecuteScriptAndExtractBool(shell()->web_contents(),
127                                   base::StringPrintf(R"(
128 new Promise((resolve) => {
129   const url = '%s';
130   if (performance.getEntriesByName(url).length > 0) {
131     resolve();
132     return;
133   }
134   new PerformanceObserver((list) => {
135     if (list.getEntriesByName(url).length > 0) {
136       resolve();
137     }
138   }).observe({ entryTypes: ['resource'] });
139 }).then(() => {
140   window.domAutomationController.send(true);
141 }))",
142                                                      url.spec().c_str()),
143                                   &result));
144   ASSERT_TRUE(result);
145 }
146 
147 // static
148 scoped_refptr<PrefetchBrowserTestBase::RequestCounter>
CreateAndMonitor(net::EmbeddedTestServer * test_server,const std::string & path,base::RunLoop * waiter)149 PrefetchBrowserTestBase::RequestCounter::CreateAndMonitor(
150     net::EmbeddedTestServer* test_server,
151     const std::string& path,
152     base::RunLoop* waiter) {
153   auto counter = base::MakeRefCounted<RequestCounter>(path, waiter);
154   test_server->RegisterRequestMonitor(
155       base::BindRepeating(&RequestCounter::OnRequest, counter));
156   return counter;
157 }
158 
RequestCounter(const std::string & path,base::RunLoop * waiter)159 PrefetchBrowserTestBase::RequestCounter::RequestCounter(const std::string& path,
160                                                         base::RunLoop* waiter)
161     : waiter_closure_(waiter ? waiter->QuitClosure() : base::OnceClosure()),
162       path_(path) {
163   DCHECK_CURRENTLY_ON(BrowserThread::UI);
164 }
165 
166 PrefetchBrowserTestBase::RequestCounter::~RequestCounter() = default;
167 
GetRequestCount()168 int PrefetchBrowserTestBase::RequestCounter::GetRequestCount() {
169   base::AutoLock lock(lock_);
170   return request_count_;
171 }
172 
OnRequest(const net::test_server::HttpRequest & request)173 void PrefetchBrowserTestBase::RequestCounter::OnRequest(
174     const net::test_server::HttpRequest& request) {
175   if (request.relative_url != path_)
176     return;
177   base::AutoLock lock(lock_);
178   ++request_count_;
179   if (waiter_closure_)
180     std::move(waiter_closure_).Run();
181 }
182 
183 }  // namespace content
184