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
43using content::BrowserThread;
44using content::WebUIMessageHandler;
45
46namespace {
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.
50void 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.
87class 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
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
138NetInternalsTest::MessageHandler::MessageHandler(
139    NetInternalsTest* net_internals_test)
140    : net_internals_test_(net_internals_test),
141      incognito_browser_(NULL) {
142}
143
144void 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
172void NetInternalsTest::MessageHandler::RunJavascriptCallback(
173    base::Value* value) {
174  web_ui()->CallJavascriptFunction("NetInternalsTest.callback", *value);
175}
176
177void 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
187void 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
209void 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
221void 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
234void 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
244void 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
254void 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
263void 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
299NetInternalsTest::NetInternalsTest()
300    : test_server_started_(false) {
301  message_handler_.reset(new MessageHandler(this));
302}
303
304NetInternalsTest::~NetInternalsTest() {
305}
306
307void 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
314void 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
330content::WebUIMessageHandler* NetInternalsTest::GetMockMessageHandler() {
331  return message_handler_.get();
332}
333
334GURL 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
349bool NetInternalsTest::StartTestServer() {
350  if (test_server_started_)
351    return true;
352  test_server_started_ = test_server()->Start();
353  return test_server_started_;
354}
355