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 "chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.h"
6
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/command_line.h"
10 #include "base/file_util.h"
11 #include "base/files/file_path.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/values.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/io_thread.h"
17 #include "chrome/browser/net/chrome_net_log.h"
18 #include "chrome/browser/prerender/prerender_manager.h"
19 #include "chrome/browser/prerender/prerender_manager_factory.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/ui/browser.h"
22 #include "chrome/browser/ui/tabs/tab_strip_model.h"
23 #include "chrome/browser/ui/webui/net_internals/net_internals_ui.h"
24 #include "chrome/common/chrome_switches.h"
25 #include "chrome/test/base/ui_test_utils.h"
26 #include "content/public/browser/render_frame_host.h"
27 #include "content/public/browser/web_contents.h"
28 #include "content/public/browser/web_ui_message_handler.h"
29 #include "net/base/address_list.h"
30 #include "net/base/net_errors.h"
31 #include "net/base/net_log.h"
32 #include "net/base/net_log_logger.h"
33 #include "net/dns/host_cache.h"
34 #include "net/dns/host_resolver.h"
35 #include "net/dns/mock_host_resolver.h"
36 #include "net/http/http_network_session.h"
37 #include "net/http/http_transaction_factory.h"
38 #include "net/url_request/url_request_context.h"
39 #include "net/url_request/url_request_context_getter.h"
40 #include "testing/gtest/include/gtest/gtest.h"
41 #include "url/gurl.h"
42
43 using content::BrowserThread;
44 using content::WebUIMessageHandler;
45
46 namespace {
47
48 // Called on IO thread. Adds an entry to the cache for the specified hostname.
49 // Either |net_error| must be net::OK, or |address| must be NULL.
AddCacheEntryOnIOThread(net::URLRequestContextGetter * context_getter,const std::string & hostname,const std::string & ip_literal,int net_error,int expire_days_from_now)50 void AddCacheEntryOnIOThread(net::URLRequestContextGetter* context_getter,
51 const std::string& hostname,
52 const std::string& ip_literal,
53 int net_error,
54 int expire_days_from_now) {
55 ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
56 net::URLRequestContext* context = context_getter->GetURLRequestContext();
57 net::HostCache* cache = context->host_resolver()->GetHostCache();
58 ASSERT_TRUE(cache);
59
60 net::HostCache::Key key(hostname, net::ADDRESS_FAMILY_UNSPECIFIED, 0);
61 base::TimeDelta ttl = base::TimeDelta::FromDays(expire_days_from_now);
62
63 net::AddressList address_list;
64 if (net_error == net::OK) {
65 // If |net_error| does not indicate an error, convert |ip_literal| to a
66 // net::AddressList, so it can be used with the cache.
67 int rv = net::ParseAddressList(ip_literal, hostname, &address_list);
68 ASSERT_EQ(net::OK, rv);
69 } else {
70 ASSERT_TRUE(ip_literal.empty());
71 }
72
73 // Add entry to the cache.
74 cache->Set(net::HostCache::Key(hostname, net::ADDRESS_FAMILY_UNSPECIFIED, 0),
75 net::HostCache::Entry(net_error, address_list),
76 base::TimeTicks::Now(),
77 ttl);
78 }
79
80 } // namespace
81
82 ////////////////////////////////////////////////////////////////////////////////
83 // NetInternalsTest::MessageHandler
84 ////////////////////////////////////////////////////////////////////////////////
85
86 // Class to handle messages from the renderer needed by certain tests.
87 class NetInternalsTest::MessageHandler : public content::WebUIMessageHandler {
88 public:
89 explicit MessageHandler(NetInternalsTest* net_internals_test);
90
91 private:
92 virtual void RegisterMessages() OVERRIDE;
93
94 // Runs NetInternalsTest.callback with the given value.
95 void RunJavascriptCallback(base::Value* value);
96
97 // Takes a string and provides the corresponding URL from the test server,
98 // which must already have been started.
99 void GetTestServerURL(const base::ListValue* list_value);
100
101 // Called on UI thread. Adds an entry to the cache for the specified
102 // hostname by posting a task to the IO thread. Takes the host name,
103 // ip address, net error code, and expiration time in days from now
104 // as parameters. If the error code indicates failure, the ip address
105 // must be an empty string.
106 void AddCacheEntry(const base::ListValue* list_value);
107
108 // Opens the given URL in a new tab.
109 void LoadPage(const base::ListValue* list_value);
110
111 // Opens a page in a new tab that prerenders the given URL.
112 void PrerenderPage(const base::ListValue* list_value);
113
114 // Navigates to the prerender in the background tab. This assumes that
115 // there is a "Click()" function in the background tab which will navigate
116 // there, and that the background tab exists at slot 1.
117 void NavigateToPrerender(const base::ListValue* list_value);
118
119 // Creates an incognito browser. Once creation is complete, passes a
120 // message to the Javascript test harness.
121 void CreateIncognitoBrowser(const base::ListValue* list_value);
122
123 // Closes an incognito browser created with CreateIncognitoBrowser.
124 void CloseIncognitoBrowser(const base::ListValue* list_value);
125
126 // Creates a simple log with a NetLogLogger, and returns it to the
127 // Javascript callback.
128 void GetNetLogLoggerLog(const base::ListValue* list_value);
129
browser()130 Browser* browser() { return net_internals_test_->browser(); }
131
132 NetInternalsTest* net_internals_test_;
133 Browser* incognito_browser_;
134
135 DISALLOW_COPY_AND_ASSIGN(MessageHandler);
136 };
137
MessageHandler(NetInternalsTest * net_internals_test)138 NetInternalsTest::MessageHandler::MessageHandler(
139 NetInternalsTest* net_internals_test)
140 : net_internals_test_(net_internals_test),
141 incognito_browser_(NULL) {
142 }
143
RegisterMessages()144 void NetInternalsTest::MessageHandler::RegisterMessages() {
145 web_ui()->RegisterMessageCallback("getTestServerURL",
146 base::Bind(&NetInternalsTest::MessageHandler::GetTestServerURL,
147 base::Unretained(this)));
148 web_ui()->RegisterMessageCallback("addCacheEntry",
149 base::Bind(&NetInternalsTest::MessageHandler::AddCacheEntry,
150 base::Unretained(this)));
151 web_ui()->RegisterMessageCallback("loadPage",
152 base::Bind(&NetInternalsTest::MessageHandler::LoadPage,
153 base::Unretained(this)));
154 web_ui()->RegisterMessageCallback("prerenderPage",
155 base::Bind(&NetInternalsTest::MessageHandler::PrerenderPage,
156 base::Unretained(this)));
157 web_ui()->RegisterMessageCallback("navigateToPrerender",
158 base::Bind(&NetInternalsTest::MessageHandler::NavigateToPrerender,
159 base::Unretained(this)));
160 web_ui()->RegisterMessageCallback("createIncognitoBrowser",
161 base::Bind(&NetInternalsTest::MessageHandler::CreateIncognitoBrowser,
162 base::Unretained(this)));
163 web_ui()->RegisterMessageCallback("closeIncognitoBrowser",
164 base::Bind(&NetInternalsTest::MessageHandler::CloseIncognitoBrowser,
165 base::Unretained(this)));
166 web_ui()->RegisterMessageCallback("getNetLogLoggerLog",
167 base::Bind(
168 &NetInternalsTest::MessageHandler::GetNetLogLoggerLog,
169 base::Unretained(this)));
170 }
171
RunJavascriptCallback(base::Value * value)172 void NetInternalsTest::MessageHandler::RunJavascriptCallback(
173 base::Value* value) {
174 web_ui()->CallJavascriptFunction("NetInternalsTest.callback", *value);
175 }
176
GetTestServerURL(const base::ListValue * list_value)177 void NetInternalsTest::MessageHandler::GetTestServerURL(
178 const base::ListValue* list_value) {
179 ASSERT_TRUE(net_internals_test_->StartTestServer());
180 std::string path;
181 ASSERT_TRUE(list_value->GetString(0, &path));
182 GURL url = net_internals_test_->test_server()->GetURL(path);
183 scoped_ptr<base::Value> url_value(base::Value::CreateStringValue(url.spec()));
184 RunJavascriptCallback(url_value.get());
185 }
186
AddCacheEntry(const base::ListValue * list_value)187 void NetInternalsTest::MessageHandler::AddCacheEntry(
188 const base::ListValue* list_value) {
189 std::string hostname;
190 std::string ip_literal;
191 double net_error;
192 double expire_days_from_now;
193 ASSERT_TRUE(list_value->GetString(0, &hostname));
194 ASSERT_TRUE(list_value->GetString(1, &ip_literal));
195 ASSERT_TRUE(list_value->GetDouble(2, &net_error));
196 ASSERT_TRUE(list_value->GetDouble(3, &expire_days_from_now));
197 ASSERT_TRUE(browser());
198
199 BrowserThread::PostTask(
200 BrowserThread::IO, FROM_HERE,
201 base::Bind(&AddCacheEntryOnIOThread,
202 make_scoped_refptr(browser()->profile()->GetRequestContext()),
203 hostname,
204 ip_literal,
205 static_cast<int>(net_error),
206 static_cast<int>(expire_days_from_now)));
207 }
208
LoadPage(const base::ListValue * list_value)209 void NetInternalsTest::MessageHandler::LoadPage(
210 const base::ListValue* list_value) {
211 std::string url;
212 ASSERT_TRUE(list_value->GetString(0, &url));
213 LOG(WARNING) << "url: [" << url << "]";
214 ui_test_utils::NavigateToURLWithDisposition(
215 browser(),
216 GURL(url),
217 NEW_BACKGROUND_TAB,
218 ui_test_utils::BROWSER_TEST_NONE);
219 }
220
PrerenderPage(const base::ListValue * list_value)221 void NetInternalsTest::MessageHandler::PrerenderPage(
222 const base::ListValue* list_value) {
223 std::string prerender_url;
224 ASSERT_TRUE(list_value->GetString(0, &prerender_url));
225 GURL loader_url =
226 net_internals_test_->CreatePrerenderLoaderUrl(GURL(prerender_url));
227 ui_test_utils::NavigateToURLWithDisposition(
228 browser(),
229 GURL(loader_url),
230 NEW_BACKGROUND_TAB,
231 ui_test_utils::BROWSER_TEST_NONE);
232 }
233
NavigateToPrerender(const base::ListValue * list_value)234 void NetInternalsTest::MessageHandler::NavigateToPrerender(
235 const base::ListValue* list_value) {
236 std::string url;
237 ASSERT_TRUE(list_value->GetString(0, &url));
238 content::RenderFrameHost* frame =
239 browser()->tab_strip_model()->GetWebContentsAt(1)->GetMainFrame();
240 frame->ExecuteJavaScript(
241 base::ASCIIToUTF16(base::StringPrintf("Click('%s')", url.c_str())));
242 }
243
CreateIncognitoBrowser(const base::ListValue * list_value)244 void NetInternalsTest::MessageHandler::CreateIncognitoBrowser(
245 const base::ListValue* list_value) {
246 ASSERT_FALSE(incognito_browser_);
247 incognito_browser_ = net_internals_test_->CreateIncognitoBrowser();
248
249 // Tell the test harness that creation is complete.
250 base::StringValue command_value("onIncognitoBrowserCreatedForTest");
251 web_ui()->CallJavascriptFunction("g_browser.receive", command_value);
252 }
253
CloseIncognitoBrowser(const base::ListValue * list_value)254 void NetInternalsTest::MessageHandler::CloseIncognitoBrowser(
255 const base::ListValue* list_value) {
256 ASSERT_TRUE(incognito_browser_);
257 incognito_browser_->tab_strip_model()->CloseAllTabs();
258 // Closing all a Browser's tabs will ultimately result in its destruction,
259 // thought it may not have been destroyed yet.
260 incognito_browser_ = NULL;
261 }
262
GetNetLogLoggerLog(const base::ListValue * list_value)263 void NetInternalsTest::MessageHandler::GetNetLogLoggerLog(
264 const base::ListValue* list_value) {
265 base::ScopedTempDir temp_directory;
266 ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
267 base::FilePath temp_file;
268 ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_directory.path(),
269 &temp_file));
270 FILE* temp_file_handle = base::OpenFile(temp_file, "w");
271 ASSERT_TRUE(temp_file_handle);
272
273 scoped_ptr<base::Value> constants(NetInternalsUI::GetConstants());
274 scoped_ptr<net::NetLogLogger> net_log_logger(new net::NetLogLogger(
275 temp_file_handle, *constants));
276 net_log_logger->StartObserving(g_browser_process->net_log());
277 g_browser_process->net_log()->AddGlobalEntry(
278 net::NetLog::TYPE_NETWORK_IP_ADDRESSES_CHANGED);
279 net::BoundNetLog bound_net_log = net::BoundNetLog::Make(
280 g_browser_process->net_log(),
281 net::NetLog::SOURCE_URL_REQUEST);
282 bound_net_log.BeginEvent(net::NetLog::TYPE_REQUEST_ALIVE);
283 net_log_logger->StopObserving();
284 net_log_logger.reset();
285
286 std::string log_contents;
287 ASSERT_TRUE(base::ReadFileToString(temp_file, &log_contents));
288 ASSERT_GT(log_contents.length(), 0u);
289
290 scoped_ptr<base::Value> log_contents_value(
291 new base::StringValue(log_contents));
292 RunJavascriptCallback(log_contents_value.get());
293 }
294
295 ////////////////////////////////////////////////////////////////////////////////
296 // NetInternalsTest
297 ////////////////////////////////////////////////////////////////////////////////
298
NetInternalsTest()299 NetInternalsTest::NetInternalsTest()
300 : test_server_started_(false) {
301 message_handler_.reset(new MessageHandler(this));
302 }
303
~NetInternalsTest()304 NetInternalsTest::~NetInternalsTest() {
305 }
306
SetUpCommandLine(CommandLine * command_line)307 void NetInternalsTest::SetUpCommandLine(CommandLine* command_line) {
308 WebUIBrowserTest::SetUpCommandLine(command_line);
309 // Needed to test the prerender view.
310 command_line->AppendSwitchASCII(switches::kPrerenderMode,
311 switches::kPrerenderModeSwitchValueEnabled);
312 }
313
SetUpOnMainThread()314 void NetInternalsTest::SetUpOnMainThread() {
315 WebUIBrowserTest::SetUpOnMainThread();
316 // Increase the memory allowed in a prerendered page above normal settings,
317 // as debug builds use more memory and often go over the usual limit.
318 Profile* profile = browser()->profile();
319 prerender::PrerenderManager* prerender_manager =
320 prerender::PrerenderManagerFactory::GetForProfile(profile);
321 prerender_manager->mutable_config().max_bytes = 1000 * 1024 * 1024;
322 if (!prerender_manager->cookie_store_loaded()) {
323 base::RunLoop loop;
324 prerender_manager->set_on_cookie_store_loaded_cb_for_testing(
325 loop.QuitClosure());
326 loop.Run();
327 }
328 }
329
GetMockMessageHandler()330 content::WebUIMessageHandler* NetInternalsTest::GetMockMessageHandler() {
331 return message_handler_.get();
332 }
333
CreatePrerenderLoaderUrl(const GURL & prerender_url)334 GURL NetInternalsTest::CreatePrerenderLoaderUrl(
335 const GURL& prerender_url) {
336 EXPECT_TRUE(StartTestServer());
337 std::vector<net::SpawnedTestServer::StringPair> replacement_text;
338 replacement_text.push_back(
339 make_pair("REPLACE_WITH_PRERENDER_URL", prerender_url.spec()));
340 std::string replacement_path;
341 EXPECT_TRUE(net::SpawnedTestServer::GetFilePathWithReplacements(
342 "files/prerender/prerender_loader.html",
343 replacement_text,
344 &replacement_path));
345 GURL url_loader = test_server()->GetURL(replacement_path);
346 return url_loader;
347 }
348
StartTestServer()349 bool NetInternalsTest::StartTestServer() {
350 if (test_server_started_)
351 return true;
352 test_server_started_ = test_server()->Start();
353 return test_server_started_;
354 }
355