root/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. AddCacheEntryOnIOThread
  2. AddDummyHttpPipelineFeedbackOnIOThread
  3. EnableHttpPipeliningOnIOThread
  4. browser
  5. incognito_browser_
  6. RegisterMessages
  7. RunJavascriptCallback
  8. GetTestServerURL
  9. AddCacheEntry
  10. LoadPage
  11. PrerenderPage
  12. NavigateToPrerender
  13. CreateIncognitoBrowser
  14. CloseIncognitoBrowser
  15. EnableHttpPipelining
  16. AddDummyHttpPipelineFeedback
  17. GetNetLogLoggerLog
  18. SetUpCommandLine
  19. SetUpOnMainThread
  20. GetMockMessageHandler
  21. CreatePrerenderLoaderUrl
  22. StartTestServer

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.h"

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/io_thread.h"
#include "chrome/browser/net/chrome_net_log.h"
#include "chrome/browser/prerender/prerender_manager.h"
#include "chrome/browser/prerender/prerender_manager_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/webui/net_internals/net_internals_ui.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui_message_handler.h"
#include "net/base/address_list.h"
#include "net/base/net_errors.h"
#include "net/base/net_log.h"
#include "net/base/net_log_logger.h"
#include "net/dns/host_cache.h"
#include "net/dns/host_resolver.h"
#include "net/dns/mock_host_resolver.h"
#include "net/http/http_network_session.h"
#include "net/http/http_pipelined_host_capability.h"
#include "net/http/http_transaction_factory.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"

using content::BrowserThread;
using content::WebUIMessageHandler;

namespace {

// Called on IO thread.  Adds an entry to the cache for the specified hostname.
// Either |net_error| must be net::OK, or |address| must be NULL.
void AddCacheEntryOnIOThread(net::URLRequestContextGetter* context_getter,
                             const std::string& hostname,
                             const std::string& ip_literal,
                             int net_error,
                             int expire_days_from_now) {
  ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
  net::URLRequestContext* context = context_getter->GetURLRequestContext();
  net::HostCache* cache = context->host_resolver()->GetHostCache();
  ASSERT_TRUE(cache);

  net::HostCache::Key key(hostname, net::ADDRESS_FAMILY_UNSPECIFIED, 0);
  base::TimeDelta ttl = base::TimeDelta::FromDays(expire_days_from_now);

  net::AddressList address_list;
  if (net_error == net::OK) {
    // If |net_error| does not indicate an error, convert |ip_literal| to a
    // net::AddressList, so it can be used with the cache.
    int rv = net::ParseAddressList(ip_literal, hostname, &address_list);
    ASSERT_EQ(net::OK, rv);
  } else {
    ASSERT_TRUE(ip_literal.empty());
  }

  // Add entry to the cache.
  cache->Set(net::HostCache::Key(hostname, net::ADDRESS_FAMILY_UNSPECIFIED, 0),
             net::HostCache::Entry(net_error, address_list),
             base::TimeTicks::Now(),
             ttl);
}

// Called on IO thread.  Adds an entry to the list of known HTTP pipelining
// hosts.
void AddDummyHttpPipelineFeedbackOnIOThread(
    net::URLRequestContextGetter* context_getter,
    const std::string& hostname,
    int port,
    net::HttpPipelinedHostCapability capability) {
  ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
  net::URLRequestContext* context = context_getter->GetURLRequestContext();
  net::HttpNetworkSession* http_network_session =
      context->http_transaction_factory()->GetSession();
  base::WeakPtr<net::HttpServerProperties> http_server_properties =
      http_network_session->http_server_properties();
  net::HostPortPair origin(hostname, port);
  http_server_properties->SetPipelineCapability(origin, capability);
}

// Called on IO thread.  Adds an entry to the list of known HTTP pipelining
// hosts.
void EnableHttpPipeliningOnIOThread(
    net::URLRequestContextGetter* context_getter, bool enable) {
  ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
  net::URLRequestContext* context = context_getter->GetURLRequestContext();
  net::HttpNetworkSession* http_network_session =
      context->http_transaction_factory()->GetSession();
  http_network_session->set_http_pipelining_enabled(enable);
}

}  // namespace

////////////////////////////////////////////////////////////////////////////////
// NetInternalsTest::MessageHandler
////////////////////////////////////////////////////////////////////////////////

// Class to handle messages from the renderer needed by certain tests.
class NetInternalsTest::MessageHandler : public content::WebUIMessageHandler {
 public:
  explicit MessageHandler(NetInternalsTest* net_internals_test);

 private:
  virtual void RegisterMessages() OVERRIDE;

  // Runs NetInternalsTest.callback with the given value.
  void RunJavascriptCallback(base::Value* value);

  // Takes a string and provides the corresponding URL from the test server,
  // which must already have been started.
  void GetTestServerURL(const base::ListValue* list_value);

  // Called on UI thread.  Adds an entry to the cache for the specified
  // hostname by posting a task to the IO thread.  Takes the host name,
  // ip address, net error code, and expiration time in days from now
  // as parameters.  If the error code indicates failure, the ip address
  // must be an empty string.
  void AddCacheEntry(const base::ListValue* list_value);

  // Opens the given URL in a new tab.
  void LoadPage(const base::ListValue* list_value);

  // Opens a page in a new tab that prerenders the given URL.
  void PrerenderPage(const base::ListValue* list_value);

  // Navigates to the prerender in the background tab. This assumes that
  // there is a "Click()" function in the background tab which will navigate
  // there, and that the background tab exists at slot 1.
  void NavigateToPrerender(const base::ListValue* list_value);

  // Creates an incognito browser.  Once creation is complete, passes a
  // message to the Javascript test harness.
  void CreateIncognitoBrowser(const base::ListValue* list_value);

  // Closes an incognito browser created with CreateIncognitoBrowser.
  void CloseIncognitoBrowser(const base::ListValue* list_value);

  // Takes in a boolean and enables/disabled HTTP pipelining accordingly.
  void EnableHttpPipelining(const base::ListValue* list_value);

  // Called on UI thread. Adds an entry to the list of known HTTP pipelining
  // hosts.
  void AddDummyHttpPipelineFeedback(const base::ListValue* list_value);

  // Creates a simple log with a NetLogLogger, and returns it to the
  // Javascript callback.
  void GetNetLogLoggerLog(const base::ListValue* list_value);

  Browser* browser() { return net_internals_test_->browser(); }

  NetInternalsTest* net_internals_test_;
  Browser* incognito_browser_;

  DISALLOW_COPY_AND_ASSIGN(MessageHandler);
};

NetInternalsTest::MessageHandler::MessageHandler(
    NetInternalsTest* net_internals_test)
    : net_internals_test_(net_internals_test),
      incognito_browser_(NULL) {
}

void NetInternalsTest::MessageHandler::RegisterMessages() {
  web_ui()->RegisterMessageCallback("getTestServerURL",
      base::Bind(&NetInternalsTest::MessageHandler::GetTestServerURL,
                 base::Unretained(this)));
  web_ui()->RegisterMessageCallback("addCacheEntry",
      base::Bind(&NetInternalsTest::MessageHandler::AddCacheEntry,
                 base::Unretained(this)));
  web_ui()->RegisterMessageCallback("loadPage",
      base::Bind(&NetInternalsTest::MessageHandler::LoadPage,
                  base::Unretained(this)));
  web_ui()->RegisterMessageCallback("prerenderPage",
      base::Bind(&NetInternalsTest::MessageHandler::PrerenderPage,
                  base::Unretained(this)));
  web_ui()->RegisterMessageCallback("navigateToPrerender",
      base::Bind(&NetInternalsTest::MessageHandler::NavigateToPrerender,
                 base::Unretained(this)));
  web_ui()->RegisterMessageCallback("createIncognitoBrowser",
      base::Bind(&NetInternalsTest::MessageHandler::CreateIncognitoBrowser,
                 base::Unretained(this)));
  web_ui()->RegisterMessageCallback("closeIncognitoBrowser",
      base::Bind(&NetInternalsTest::MessageHandler::CloseIncognitoBrowser,
                 base::Unretained(this)));
  web_ui()->RegisterMessageCallback("enableHttpPipelining",
      base::Bind(&NetInternalsTest::MessageHandler::EnableHttpPipelining,
                 base::Unretained(this)));
  web_ui()->RegisterMessageCallback("addDummyHttpPipelineFeedback",
      base::Bind(
          &NetInternalsTest::MessageHandler::AddDummyHttpPipelineFeedback,
          base::Unretained(this)));
  web_ui()->RegisterMessageCallback("getNetLogLoggerLog",
      base::Bind(
          &NetInternalsTest::MessageHandler::GetNetLogLoggerLog,
          base::Unretained(this)));
}

void NetInternalsTest::MessageHandler::RunJavascriptCallback(
    base::Value* value) {
  web_ui()->CallJavascriptFunction("NetInternalsTest.callback", *value);
}

void NetInternalsTest::MessageHandler::GetTestServerURL(
    const base::ListValue* list_value) {
  ASSERT_TRUE(net_internals_test_->StartTestServer());
  std::string path;
  ASSERT_TRUE(list_value->GetString(0, &path));
  GURL url = net_internals_test_->test_server()->GetURL(path);
  scoped_ptr<base::Value> url_value(base::Value::CreateStringValue(url.spec()));
  RunJavascriptCallback(url_value.get());
}

void NetInternalsTest::MessageHandler::AddCacheEntry(
    const base::ListValue* list_value) {
  std::string hostname;
  std::string ip_literal;
  double net_error;
  double expire_days_from_now;
  ASSERT_TRUE(list_value->GetString(0, &hostname));
  ASSERT_TRUE(list_value->GetString(1, &ip_literal));
  ASSERT_TRUE(list_value->GetDouble(2, &net_error));
  ASSERT_TRUE(list_value->GetDouble(3, &expire_days_from_now));
  ASSERT_TRUE(browser());

  BrowserThread::PostTask(
      BrowserThread::IO, FROM_HERE,
      base::Bind(&AddCacheEntryOnIOThread,
                 make_scoped_refptr(browser()->profile()->GetRequestContext()),
                 hostname,
                 ip_literal,
                 static_cast<int>(net_error),
                 static_cast<int>(expire_days_from_now)));
}

void NetInternalsTest::MessageHandler::LoadPage(
    const base::ListValue* list_value) {
  std::string url;
  ASSERT_TRUE(list_value->GetString(0, &url));
  LOG(WARNING) << "url: [" << url << "]";
  ui_test_utils::NavigateToURLWithDisposition(
      browser(),
      GURL(url),
      NEW_BACKGROUND_TAB,
      ui_test_utils::BROWSER_TEST_NONE);
}

void NetInternalsTest::MessageHandler::PrerenderPage(
    const base::ListValue* list_value) {
  std::string prerender_url;
  ASSERT_TRUE(list_value->GetString(0, &prerender_url));
  GURL loader_url =
      net_internals_test_->CreatePrerenderLoaderUrl(GURL(prerender_url));
  ui_test_utils::NavigateToURLWithDisposition(
      browser(),
      GURL(loader_url),
      NEW_BACKGROUND_TAB,
      ui_test_utils::BROWSER_TEST_NONE);
}

void NetInternalsTest::MessageHandler::NavigateToPrerender(
    const base::ListValue* list_value) {
  std::string url;
  ASSERT_TRUE(list_value->GetString(0, &url));
  content::RenderFrameHost* frame =
      browser()->tab_strip_model()->GetWebContentsAt(1)->GetMainFrame();
  frame->ExecuteJavaScript(
      base::ASCIIToUTF16(base::StringPrintf("Click('%s')", url.c_str())));
}

void NetInternalsTest::MessageHandler::CreateIncognitoBrowser(
    const base::ListValue* list_value) {
  ASSERT_FALSE(incognito_browser_);
  incognito_browser_ = net_internals_test_->CreateIncognitoBrowser();

  // Tell the test harness that creation is complete.
  base::StringValue command_value("onIncognitoBrowserCreatedForTest");
  web_ui()->CallJavascriptFunction("g_browser.receive", command_value);
}

void NetInternalsTest::MessageHandler::CloseIncognitoBrowser(
    const base::ListValue* list_value) {
  ASSERT_TRUE(incognito_browser_);
  incognito_browser_->tab_strip_model()->CloseAllTabs();
  // Closing all a Browser's tabs will ultimately result in its destruction,
  // thought it may not have been destroyed yet.
  incognito_browser_ = NULL;
}

void NetInternalsTest::MessageHandler::EnableHttpPipelining(
    const base::ListValue* list_value) {
  bool enable;
  ASSERT_TRUE(list_value->GetBoolean(0, &enable));
  BrowserThread::PostTask(
      BrowserThread::IO, FROM_HERE,
      base::Bind(&EnableHttpPipeliningOnIOThread,
                 make_scoped_refptr(browser()->profile()->GetRequestContext()),
                 enable));
}

void NetInternalsTest::MessageHandler::AddDummyHttpPipelineFeedback(
    const base::ListValue* list_value) {
  std::string hostname;
  double port;
  std::string raw_capability;
  net::HttpPipelinedHostCapability capability;
  ASSERT_TRUE(list_value->GetString(0, &hostname));
  ASSERT_TRUE(list_value->GetDouble(1, &port));
  ASSERT_TRUE(list_value->GetString(2, &raw_capability));
  if (raw_capability == "capable") {
    capability = net::PIPELINE_CAPABLE;
  } else if (raw_capability == "incapable") {
    capability = net::PIPELINE_INCAPABLE;
  } else {
    FAIL() << "Unexpected capability string: " << raw_capability;
  }
  BrowserThread::PostTask(
      BrowserThread::IO, FROM_HERE,
      base::Bind(&AddDummyHttpPipelineFeedbackOnIOThread,
                 make_scoped_refptr(browser()->profile()->GetRequestContext()),
                 hostname,
                 static_cast<int>(port),
                 capability));
}

void NetInternalsTest::MessageHandler::GetNetLogLoggerLog(
    const base::ListValue* list_value) {
  base::ScopedTempDir temp_directory;
  ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
  base::FilePath temp_file;
  ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_directory.path(),
                                             &temp_file));
  FILE* temp_file_handle = base::OpenFile(temp_file, "w");
  ASSERT_TRUE(temp_file_handle);

  scoped_ptr<base::Value> constants(NetInternalsUI::GetConstants());
  scoped_ptr<net::NetLogLogger> net_log_logger(new net::NetLogLogger(
      temp_file_handle, *constants));
  net_log_logger->StartObserving(g_browser_process->net_log());
  g_browser_process->net_log()->AddGlobalEntry(
      net::NetLog::TYPE_NETWORK_IP_ADDRESSES_CHANGED);
  net::BoundNetLog bound_net_log = net::BoundNetLog::Make(
      g_browser_process->net_log(),
      net::NetLog::SOURCE_URL_REQUEST);
  bound_net_log.BeginEvent(net::NetLog::TYPE_REQUEST_ALIVE);
  net_log_logger->StopObserving();
  net_log_logger.reset();

  std::string log_contents;
  ASSERT_TRUE(base::ReadFileToString(temp_file, &log_contents));
  ASSERT_GT(log_contents.length(), 0u);

  scoped_ptr<base::Value> log_contents_value(
      new base::StringValue(log_contents));
  RunJavascriptCallback(log_contents_value.get());
}

////////////////////////////////////////////////////////////////////////////////
// NetInternalsTest
////////////////////////////////////////////////////////////////////////////////

NetInternalsTest::NetInternalsTest()
    : test_server_started_(false) {
  message_handler_.reset(new MessageHandler(this));
}

NetInternalsTest::~NetInternalsTest() {
}

void NetInternalsTest::SetUpCommandLine(CommandLine* command_line) {
  WebUIBrowserTest::SetUpCommandLine(command_line);
  // Needed to test the prerender view.
  command_line->AppendSwitchASCII(switches::kPrerenderMode,
                                  switches::kPrerenderModeSwitchValueEnabled);
}

void NetInternalsTest::SetUpOnMainThread() {
  WebUIBrowserTest::SetUpOnMainThread();
  // Increase the memory allowed in a prerendered page above normal settings,
  // as debug builds use more memory and often go over the usual limit.
  Profile* profile = browser()->profile();
  prerender::PrerenderManager* prerender_manager =
      prerender::PrerenderManagerFactory::GetForProfile(profile);
  prerender_manager->mutable_config().max_bytes = 1000 * 1024 * 1024;
}

content::WebUIMessageHandler* NetInternalsTest::GetMockMessageHandler() {
  return message_handler_.get();
}

GURL NetInternalsTest::CreatePrerenderLoaderUrl(
    const GURL& prerender_url) {
  EXPECT_TRUE(StartTestServer());
  std::vector<net::SpawnedTestServer::StringPair> replacement_text;
  replacement_text.push_back(
      make_pair("REPLACE_WITH_PRERENDER_URL", prerender_url.spec()));
  std::string replacement_path;
  EXPECT_TRUE(net::SpawnedTestServer::GetFilePathWithReplacements(
      "files/prerender/prerender_loader.html",
      replacement_text,
      &replacement_path));
  GURL url_loader = test_server()->GetURL(replacement_path);
  return url_loader;
}

bool NetInternalsTest::StartTestServer() {
  if (test_server_started_)
    return true;
  test_server_started_ = test_server()->Start();
  return test_server_started_;
}

/* [<][>][^][v][top][bottom][index][help] */