1// Copyright (c) 2011 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_ui.h"
6
7#include <algorithm>
8#include <string>
9#include <utility>
10#include <vector>
11
12#include "base/base64.h"
13#include "base/command_line.h"
14#include "base/file_util.h"
15#include "base/memory/singleton.h"
16#include "base/message_loop.h"
17#include "base/path_service.h"
18#include "base/string_number_conversions.h"
19#include "base/string_piece.h"
20#include "base/string_split.h"
21#include "base/string_util.h"
22#include "base/utf_string_conversions.h"
23#include "base/values.h"
24#include "chrome/browser/browser_process.h"
25#include "chrome/browser/io_thread.h"
26#include "chrome/browser/net/chrome_net_log.h"
27#include "chrome/browser/net/connection_tester.h"
28#include "chrome/browser/net/passive_log_collector.h"
29#include "chrome/browser/net/url_fixer_upper.h"
30#include "chrome/browser/platform_util.h"
31#include "chrome/browser/prefs/pref_member.h"
32#include "chrome/browser/profiles/profile.h"
33#include "chrome/browser/ui/shell_dialogs.h"
34#include "chrome/browser/ui/webui/chrome_url_data_manager.h"
35#include "chrome/common/chrome_paths.h"
36#include "chrome/common/chrome_version_info.h"
37#include "chrome/common/jstemplate_builder.h"
38#include "chrome/common/pref_names.h"
39#include "chrome/common/url_constants.h"
40#include "content/browser/browser_thread.h"
41#include "content/browser/tab_contents/tab_contents.h"
42#include "content/browser/tab_contents/tab_contents_view.h"
43#include "content/common/notification_details.h"
44#include "grit/generated_resources.h"
45#include "grit/net_internals_resources.h"
46#include "net/base/escape.h"
47#include "net/base/host_resolver_impl.h"
48#include "net/base/net_errors.h"
49#include "net/base/net_util.h"
50#include "net/base/sys_addrinfo.h"
51#include "net/base/x509_cert_types.h"
52#include "net/disk_cache/disk_cache.h"
53#include "net/http/http_alternate_protocols.h"
54#include "net/http/http_cache.h"
55#include "net/http/http_network_layer.h"
56#include "net/http/http_network_session.h"
57#include "net/http/http_stream_factory.h"
58#include "net/proxy/proxy_service.h"
59#include "net/url_request/url_request_context.h"
60#include "net/url_request/url_request_context_getter.h"
61#include "ui/base/l10n/l10n_util.h"
62#include "ui/base/resource/resource_bundle.h"
63
64#ifdef OS_WIN
65#include "chrome/browser/net/service_providers_win.h"
66#endif
67
68namespace {
69
70// Delay between when an event occurs and when it is passed to the Javascript
71// page.  All events that occur during this period are grouped together and
72// sent to the page at once, which reduces context switching and CPU usage.
73const int kNetLogEventDelayMilliseconds = 100;
74
75// Returns the HostCache for |context|'s primary HostResolver, or NULL if
76// there is none.
77net::HostCache* GetHostResolverCache(net::URLRequestContext* context) {
78  net::HostResolverImpl* host_resolver_impl =
79      context->host_resolver()->GetAsHostResolverImpl();
80
81  if (!host_resolver_impl)
82    return NULL;
83
84  return host_resolver_impl->cache();
85}
86
87// Returns the disk cache backend for |context| if there is one, or NULL.
88disk_cache::Backend* GetDiskCacheBackend(net::URLRequestContext* context) {
89  if (!context->http_transaction_factory())
90    return NULL;
91
92  net::HttpCache* http_cache = context->http_transaction_factory()->GetCache();
93  if (!http_cache)
94    return NULL;
95
96  return http_cache->GetCurrentBackend();
97}
98
99// Returns the http network session for |context| if there is one.
100// Otherwise, returns NULL.
101net::HttpNetworkSession* GetHttpNetworkSession(
102    net::URLRequestContext* context) {
103  if (!context->http_transaction_factory())
104    return NULL;
105
106  return context->http_transaction_factory()->GetSession();
107}
108
109Value* ExperimentToValue(const ConnectionTester::Experiment& experiment) {
110  DictionaryValue* dict = new DictionaryValue();
111
112  if (experiment.url.is_valid())
113    dict->SetString("url", experiment.url.spec());
114
115  dict->SetString("proxy_settings_experiment",
116                  ConnectionTester::ProxySettingsExperimentDescription(
117                      experiment.proxy_settings_experiment));
118  dict->SetString("host_resolver_experiment",
119                  ConnectionTester::HostResolverExperimentDescription(
120                      experiment.host_resolver_experiment));
121  return dict;
122}
123
124class NetInternalsHTMLSource : public ChromeURLDataManager::DataSource {
125 public:
126  NetInternalsHTMLSource();
127
128  // Called when the network layer has requested a resource underneath
129  // the path we registered.
130  virtual void StartDataRequest(const std::string& path,
131                                bool is_incognito,
132                                int request_id);
133  virtual std::string GetMimeType(const std::string&) const;
134
135 private:
136  ~NetInternalsHTMLSource() {}
137  DISALLOW_COPY_AND_ASSIGN(NetInternalsHTMLSource);
138};
139
140// This class receives javascript messages from the renderer.
141// Note that the WebUI infrastructure runs on the UI thread, therefore all of
142// this class's methods are expected to run on the UI thread.
143//
144// Since the network code we want to run lives on the IO thread, we proxy
145// almost everything over to NetInternalsMessageHandler::IOThreadImpl, which
146// runs on the IO thread.
147//
148// TODO(eroman): Can we start on the IO thread to begin with?
149class NetInternalsMessageHandler
150    : public WebUIMessageHandler,
151      public SelectFileDialog::Listener,
152      public base::SupportsWeakPtr<NetInternalsMessageHandler>,
153      public NotificationObserver {
154 public:
155  NetInternalsMessageHandler();
156  virtual ~NetInternalsMessageHandler();
157
158  // WebUIMessageHandler implementation.
159  virtual WebUIMessageHandler* Attach(WebUI* web_ui);
160  virtual void RegisterMessages();
161
162  // Executes the javascript function |function_name| in the renderer, passing
163  // it the argument |value|.
164  void CallJavascriptFunction(const std::wstring& function_name,
165                              const Value* value);
166
167  // NotificationObserver implementation.
168  virtual void Observe(NotificationType type,
169                       const NotificationSource& source,
170                       const NotificationDetails& details);
171
172  // Javascript message handlers.
173  void OnRendererReady(const ListValue* list);
174  void OnEnableHttpThrottling(const ListValue* list);
175
176  // SelectFileDialog::Listener implementation
177  virtual void FileSelected(const FilePath& path, int index, void* params);
178  virtual void FileSelectionCanceled(void* params);
179
180  // The only callback handled on the UI thread.  As it needs to access fields
181  // from |web_ui_|, it can't be called on the IO thread.
182  void OnLoadLogFile(const ListValue* list);
183
184 private:
185  class IOThreadImpl;
186
187  // Task run on the FILE thread to read the contents of a log file.  The result
188  // is then passed to IOThreadImpl's CallJavascriptFunction, which sends it
189  // back to the web page.  IOThreadImpl is used instead of the
190  // NetInternalsMessageHandler directly because it checks if the message
191  // handler has been destroyed in the meantime.
192  class ReadLogFileTask : public Task {
193   public:
194    ReadLogFileTask(IOThreadImpl* proxy, const FilePath& path);
195
196    virtual void Run();
197
198   private:
199    // IOThreadImpl implements existence checks already.  Simpler to reused them
200    // then to reimplement them.
201    scoped_refptr<IOThreadImpl> proxy_;
202
203    // Path of the file to open.
204    const FilePath path_;
205  };
206
207  // The pref member about whether HTTP throttling is enabled, which needs to
208  // be accessed on the UI thread.
209  BooleanPrefMember http_throttling_enabled_;
210
211  // OnRendererReady invokes this callback to do the part of message handling
212  // that needs to happen on the IO thread.
213  scoped_ptr<WebUI::MessageCallback> renderer_ready_io_callback_;
214
215  // This is the "real" message handler, which lives on the IO thread.
216  scoped_refptr<IOThreadImpl> proxy_;
217
218  // Used for loading log files.
219  scoped_refptr<SelectFileDialog> select_log_file_dialog_;
220
221  DISALLOW_COPY_AND_ASSIGN(NetInternalsMessageHandler);
222};
223
224// This class is the "real" message handler. It is allocated and destroyed on
225// the UI thread.  With the exception of OnAddEntry, OnWebUIDeleted, and
226// CallJavascriptFunction, its methods are all expected to be called from the IO
227// thread.  OnAddEntry and CallJavascriptFunction can be called from any thread,
228// and OnWebUIDeleted can only be called from the UI thread.
229class NetInternalsMessageHandler::IOThreadImpl
230    : public base::RefCountedThreadSafe<
231          NetInternalsMessageHandler::IOThreadImpl,
232          BrowserThread::DeleteOnUIThread>,
233      public ChromeNetLog::ThreadSafeObserver,
234      public ConnectionTester::Delegate {
235 public:
236  // Type for methods that can be used as MessageHandler callbacks.
237  typedef void (IOThreadImpl::*MessageHandler)(const ListValue*);
238
239  // Creates a proxy for |handler| that will live on the IO thread.
240  // |handler| is a weak pointer, since it is possible for the
241  // WebUIMessageHandler to be deleted on the UI thread while we were executing
242  // on the IO thread. |io_thread| is the global IOThread (it is passed in as
243  // an argument since we need to grab it from the UI thread).
244  IOThreadImpl(
245      const base::WeakPtr<NetInternalsMessageHandler>& handler,
246      IOThread* io_thread,
247      net::URLRequestContextGetter* context_getter);
248
249  // Creates a callback that will run |method| on the IO thread.
250  //
251  // This can be used with WebUI::RegisterMessageCallback() to bind to a method
252  // on the IO thread.
253  WebUI::MessageCallback* CreateCallback(MessageHandler method);
254
255  // Called once the WebUI has been deleted (i.e. renderer went away), on the
256  // IO thread.
257  void Detach();
258
259  // Sends all passive log entries in |passive_entries| to the Javascript
260  // handler, called on the IO thread.
261  void SendPassiveLogEntries(const ChromeNetLog::EntryList& passive_entries);
262
263  // Called when the WebUI is deleted.  Prevents calling Javascript functions
264  // afterwards.  Called on UI thread.
265  void OnWebUIDeleted();
266
267  //--------------------------------
268  // Javascript message handlers:
269  //--------------------------------
270
271  void OnRendererReady(const ListValue* list);
272
273  void OnGetProxySettings(const ListValue* list);
274  void OnReloadProxySettings(const ListValue* list);
275  void OnGetBadProxies(const ListValue* list);
276  void OnClearBadProxies(const ListValue* list);
277  void OnGetHostResolverInfo(const ListValue* list);
278  void OnClearHostResolverCache(const ListValue* list);
279  void OnEnableIPv6(const ListValue* list);
280  void OnStartConnectionTests(const ListValue* list);
281  void OnHSTSQuery(const ListValue* list);
282  void OnHSTSAdd(const ListValue* list);
283  void OnHSTSDelete(const ListValue* list);
284  void OnGetHttpCacheInfo(const ListValue* list);
285  void OnGetSocketPoolInfo(const ListValue* list);
286  void OnCloseIdleSockets(const ListValue* list);
287  void OnFlushSocketPools(const ListValue* list);
288  void OnGetSpdySessionInfo(const ListValue* list);
289  void OnGetSpdyStatus(const ListValue* list);
290  void OnGetSpdyAlternateProtocolMappings(const ListValue* list);
291#ifdef OS_WIN
292  void OnGetServiceProviders(const ListValue* list);
293#endif
294
295  void OnSetLogLevel(const ListValue* list);
296
297  // ChromeNetLog::ThreadSafeObserver implementation:
298  virtual void OnAddEntry(net::NetLog::EventType type,
299                          const base::TimeTicks& time,
300                          const net::NetLog::Source& source,
301                          net::NetLog::EventPhase phase,
302                          net::NetLog::EventParameters* params);
303
304  // ConnectionTester::Delegate implementation:
305  virtual void OnStartConnectionTestSuite();
306  virtual void OnStartConnectionTestExperiment(
307      const ConnectionTester::Experiment& experiment);
308  virtual void OnCompletedConnectionTestExperiment(
309      const ConnectionTester::Experiment& experiment,
310      int result);
311  virtual void OnCompletedConnectionTestSuite();
312
313  // Helper that executes |function_name| in the attached renderer.
314  // The function takes ownership of |arg|.  Note that this can be called from
315  // any thread.
316  void CallJavascriptFunction(const std::wstring& function_name, Value* arg);
317
318 private:
319  friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
320  friend class DeleteTask<IOThreadImpl>;
321
322  ~IOThreadImpl();
323
324  class CallbackHelper;
325
326  // Helper that runs |method| with |arg|, and deletes |arg| on completion.
327  void DispatchToMessageHandler(ListValue* arg, MessageHandler method);
328
329  // Adds |entry| to the queue of pending log entries to be sent to the page via
330  // Javascript.  Must be called on the IO Thread.  Also creates a delayed task
331  // that will call PostPendingEntries, if there isn't one already.
332  void AddEntryToQueue(Value* entry);
333
334  // Sends all pending entries to the page via Javascript, and clears the list
335  // of pending entries.  Sending multiple entries at once results in a
336  // significant reduction of CPU usage when a lot of events are happening.
337  // Must be called on the IO Thread.
338  void PostPendingEntries();
339
340  // Pointer to the UI-thread message handler. Only access this from
341  // the UI thread.
342  base::WeakPtr<NetInternalsMessageHandler> handler_;
343
344  // The global IOThread, which contains the global NetLog to observer.
345  IOThread* io_thread_;
346
347  scoped_refptr<net::URLRequestContextGetter> context_getter_;
348
349  // Helper that runs the suite of connection tests.
350  scoped_ptr<ConnectionTester> connection_tester_;
351
352  // True if the Web UI has been deleted.  This is used to prevent calling
353  // Javascript functions after the Web UI is destroyed.  On refresh, the
354  // messages can end up being sent to the refreshed page, causing duplicate
355  // or partial entries.
356  //
357  // This is only read and written to on the UI thread.
358  bool was_webui_deleted_;
359
360  // True if we have attached an observer to the NetLog already.
361  bool is_observing_log_;
362
363  // Log entries that have yet to be passed along to Javascript page.  Non-NULL
364  // when and only when there is a pending delayed task to call
365  // PostPendingEntries.  Read and written to exclusively on the IO Thread.
366  scoped_ptr<ListValue> pending_entries_;
367};
368
369// Helper class for a WebUI::MessageCallback which when executed calls
370// instance->*method(value) on the IO thread.
371class NetInternalsMessageHandler::IOThreadImpl::CallbackHelper
372    : public WebUI::MessageCallback {
373 public:
374  CallbackHelper(IOThreadImpl* instance, IOThreadImpl::MessageHandler method)
375      : instance_(instance),
376        method_(method) {
377    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
378  }
379
380  virtual void RunWithParams(const Tuple1<const ListValue*>& params) {
381    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
382
383    // We need to make a copy of the value in order to pass it over to the IO
384    // thread. We will delete this in IOThreadImpl::DispatchMessageHandler().
385    ListValue* list_copy = static_cast<ListValue*>(
386        params.a ? params.a->DeepCopy() : NULL);
387
388    if (!BrowserThread::PostTask(
389            BrowserThread::IO, FROM_HERE,
390            NewRunnableMethod(instance_.get(),
391                              &IOThreadImpl::DispatchToMessageHandler,
392                              list_copy, method_))) {
393      // Failed posting the task, avoid leaking |list_copy|.
394      delete list_copy;
395    }
396  }
397
398 private:
399  scoped_refptr<IOThreadImpl> instance_;
400  IOThreadImpl::MessageHandler method_;
401};
402
403////////////////////////////////////////////////////////////////////////////////
404//
405// NetInternalsHTMLSource
406//
407////////////////////////////////////////////////////////////////////////////////
408
409NetInternalsHTMLSource::NetInternalsHTMLSource()
410    : DataSource(chrome::kChromeUINetInternalsHost, MessageLoop::current()) {
411}
412
413void NetInternalsHTMLSource::StartDataRequest(const std::string& path,
414                                              bool is_incognito,
415                                              int request_id) {
416  DictionaryValue localized_strings;
417  SetFontAndTextDirection(&localized_strings);
418
419  // The provided "path" may contain a fragment, or query section. We only
420  // care about the path itself, and will disregard anything else.
421  std::string filename =
422      GURL(std::string("chrome://net/") + path).path().substr(1);
423
424  // The source for the net internals page is flattened during compilation, so
425  // the only resource that should legitimately be requested is the main file.
426  // Note that users can type anything into the address bar, though, so we must
427  // handle arbitrary input.
428  if (filename.empty() || filename == "index.html") {
429    base::StringPiece html(
430        ResourceBundle::GetSharedInstance().GetRawDataResource(
431            IDR_NET_INTERNALS_INDEX_HTML));
432    std::string full_html(html.data(), html.size());
433    jstemplate_builder::AppendJsonHtml(&localized_strings, &full_html);
434    jstemplate_builder::AppendI18nTemplateSourceHtml(&full_html);
435    jstemplate_builder::AppendI18nTemplateProcessHtml(&full_html);
436    jstemplate_builder::AppendJsTemplateSourceHtml(&full_html);
437
438    scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes);
439    html_bytes->data.resize(full_html.size());
440    std::copy(full_html.begin(), full_html.end(), html_bytes->data.begin());
441    SendResponse(request_id, html_bytes);
442    return;
443  }
444
445  const std::string data_string("<p style='color:red'>Failed to read resource" +
446      EscapeForHTML(filename) + "</p>");
447  scoped_refptr<RefCountedBytes> bytes(new RefCountedBytes);
448  bytes->data.resize(data_string.size());
449  std::copy(data_string.begin(), data_string.end(), bytes->data.begin());
450  SendResponse(request_id, bytes);
451}
452
453std::string NetInternalsHTMLSource::GetMimeType(const std::string&) const {
454  return "text/html";
455}
456
457////////////////////////////////////////////////////////////////////////////////
458//
459// NetInternalsMessageHandler
460//
461////////////////////////////////////////////////////////////////////////////////
462
463NetInternalsMessageHandler::NetInternalsMessageHandler() {}
464
465NetInternalsMessageHandler::~NetInternalsMessageHandler() {
466  if (proxy_) {
467    proxy_.get()->OnWebUIDeleted();
468    // Notify the handler on the IO thread that the renderer is gone.
469    BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
470        NewRunnableMethod(proxy_.get(), &IOThreadImpl::Detach));
471  }
472  if (select_log_file_dialog_)
473    select_log_file_dialog_->ListenerDestroyed();
474}
475
476WebUIMessageHandler* NetInternalsMessageHandler::Attach(WebUI* web_ui) {
477  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
478
479  PrefService* pref_service = web_ui->GetProfile()->GetPrefs();
480  http_throttling_enabled_.Init(prefs::kHttpThrottlingEnabled, pref_service,
481                                this);
482
483  proxy_ = new IOThreadImpl(this->AsWeakPtr(), g_browser_process->io_thread(),
484                            web_ui->GetProfile()->GetRequestContext());
485  renderer_ready_io_callback_.reset(
486      proxy_->CreateCallback(&IOThreadImpl::OnRendererReady));
487
488  WebUIMessageHandler* result = WebUIMessageHandler::Attach(web_ui);
489  return result;
490}
491
492void NetInternalsMessageHandler::FileSelected(
493    const FilePath& path, int index, void* params) {
494  select_log_file_dialog_.release();
495  BrowserThread::PostTask(
496      BrowserThread::FILE, FROM_HERE,
497      new ReadLogFileTask(proxy_.get(), path));
498}
499
500void NetInternalsMessageHandler::FileSelectionCanceled(void* params) {
501  select_log_file_dialog_.release();
502}
503
504void NetInternalsMessageHandler::OnLoadLogFile(const ListValue* list) {
505  // Only allow a single dialog at a time.
506  if (select_log_file_dialog_.get())
507    return;
508  select_log_file_dialog_ = SelectFileDialog::Create(this);
509  select_log_file_dialog_->SelectFile(
510      SelectFileDialog::SELECT_OPEN_FILE, string16(), FilePath(), NULL, 0,
511      FILE_PATH_LITERAL(""), web_ui_->tab_contents(),
512      web_ui_->tab_contents()->view()->GetTopLevelNativeWindow(), NULL);
513}
514
515void NetInternalsMessageHandler::RegisterMessages() {
516  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
517  // Only callback handled on UI thread.
518  web_ui_->RegisterMessageCallback(
519      "loadLogFile",
520      NewCallback(this, &NetInternalsMessageHandler::OnLoadLogFile));
521
522  web_ui_->RegisterMessageCallback(
523      "notifyReady",
524      NewCallback(this, &NetInternalsMessageHandler::OnRendererReady));
525  web_ui_->RegisterMessageCallback(
526      "getProxySettings",
527      proxy_->CreateCallback(&IOThreadImpl::OnGetProxySettings));
528  web_ui_->RegisterMessageCallback(
529      "reloadProxySettings",
530      proxy_->CreateCallback(&IOThreadImpl::OnReloadProxySettings));
531  web_ui_->RegisterMessageCallback(
532      "getBadProxies",
533      proxy_->CreateCallback(&IOThreadImpl::OnGetBadProxies));
534  web_ui_->RegisterMessageCallback(
535      "clearBadProxies",
536      proxy_->CreateCallback(&IOThreadImpl::OnClearBadProxies));
537  web_ui_->RegisterMessageCallback(
538      "getHostResolverInfo",
539      proxy_->CreateCallback(&IOThreadImpl::OnGetHostResolverInfo));
540  web_ui_->RegisterMessageCallback(
541      "clearHostResolverCache",
542      proxy_->CreateCallback(&IOThreadImpl::OnClearHostResolverCache));
543  web_ui_->RegisterMessageCallback(
544      "enableIPv6",
545      proxy_->CreateCallback(&IOThreadImpl::OnEnableIPv6));
546  web_ui_->RegisterMessageCallback(
547      "startConnectionTests",
548      proxy_->CreateCallback(&IOThreadImpl::OnStartConnectionTests));
549  web_ui_->RegisterMessageCallback(
550      "hstsQuery",
551      proxy_->CreateCallback(&IOThreadImpl::OnHSTSQuery));
552  web_ui_->RegisterMessageCallback(
553      "hstsAdd",
554      proxy_->CreateCallback(&IOThreadImpl::OnHSTSAdd));
555  web_ui_->RegisterMessageCallback(
556      "hstsDelete",
557      proxy_->CreateCallback(&IOThreadImpl::OnHSTSDelete));
558  web_ui_->RegisterMessageCallback(
559      "getHttpCacheInfo",
560      proxy_->CreateCallback(&IOThreadImpl::OnGetHttpCacheInfo));
561  web_ui_->RegisterMessageCallback(
562      "getSocketPoolInfo",
563      proxy_->CreateCallback(&IOThreadImpl::OnGetSocketPoolInfo));
564  web_ui_->RegisterMessageCallback(
565      "closeIdleSockets",
566      proxy_->CreateCallback(&IOThreadImpl::OnCloseIdleSockets));
567  web_ui_->RegisterMessageCallback(
568      "flushSocketPools",
569      proxy_->CreateCallback(&IOThreadImpl::OnFlushSocketPools));
570  web_ui_->RegisterMessageCallback(
571      "getSpdySessionInfo",
572      proxy_->CreateCallback(&IOThreadImpl::OnGetSpdySessionInfo));
573  web_ui_->RegisterMessageCallback(
574      "getSpdyStatus",
575      proxy_->CreateCallback(&IOThreadImpl::OnGetSpdyStatus));
576  web_ui_->RegisterMessageCallback(
577      "getSpdyAlternateProtocolMappings",
578      proxy_->CreateCallback(
579          &IOThreadImpl::OnGetSpdyAlternateProtocolMappings));
580#ifdef OS_WIN
581  web_ui_->RegisterMessageCallback(
582      "getServiceProviders",
583      proxy_->CreateCallback(&IOThreadImpl::OnGetServiceProviders));
584#endif
585
586  web_ui_->RegisterMessageCallback(
587      "setLogLevel",
588      proxy_->CreateCallback(&IOThreadImpl::OnSetLogLevel));
589
590  web_ui_->RegisterMessageCallback(
591      "enableHttpThrottling",
592      NewCallback(this, &NetInternalsMessageHandler::OnEnableHttpThrottling));
593}
594
595void NetInternalsMessageHandler::CallJavascriptFunction(
596    const std::wstring& function_name,
597    const Value* value) {
598  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
599  if (value) {
600    web_ui_->CallJavascriptFunction(WideToASCII(function_name), *value);
601  } else {
602    web_ui_->CallJavascriptFunction(WideToASCII(function_name));
603  }
604}
605
606void NetInternalsMessageHandler::Observe(NotificationType type,
607                                         const NotificationSource& source,
608                                         const NotificationDetails& details) {
609  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
610  DCHECK_EQ(type.value, NotificationType::PREF_CHANGED);
611
612  std::string* pref_name = Details<std::string>(details).ptr();
613  if (*pref_name == prefs::kHttpThrottlingEnabled) {
614    scoped_ptr<Value> enabled(
615        Value::CreateBooleanValue(*http_throttling_enabled_));
616
617    CallJavascriptFunction(
618        L"g_browser.receivedHttpThrottlingEnabledPrefChanged", enabled.get());
619  }
620}
621
622void NetInternalsMessageHandler::OnRendererReady(const ListValue* list) {
623  CHECK(renderer_ready_io_callback_.get());
624  renderer_ready_io_callback_->Run(list);
625
626  scoped_ptr<Value> enabled(
627      Value::CreateBooleanValue(*http_throttling_enabled_));
628  CallJavascriptFunction(
629      L"g_browser.receivedHttpThrottlingEnabledPrefChanged", enabled.get());
630}
631
632void NetInternalsMessageHandler::OnEnableHttpThrottling(const ListValue* list) {
633  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
634
635  bool enable = false;
636  if (!list->GetBoolean(0, &enable)) {
637    NOTREACHED();
638    return;
639  }
640
641  http_throttling_enabled_.SetValue(enable);
642}
643
644////////////////////////////////////////////////////////////////////////////////
645//
646// NetInternalsMessageHandler::ReadLogFileTask
647//
648////////////////////////////////////////////////////////////////////////////////
649
650NetInternalsMessageHandler::ReadLogFileTask::ReadLogFileTask(
651    IOThreadImpl* proxy, const FilePath& path)
652    : proxy_(proxy), path_(path) {
653}
654
655void NetInternalsMessageHandler::ReadLogFileTask::Run() {
656  std::string file_contents;
657  if (!file_util::ReadFileToString(path_, &file_contents))
658    return;
659  proxy_->CallJavascriptFunction(L"g_browser.loadedLogFile",
660                                 new StringValue(file_contents));
661}
662
663////////////////////////////////////////////////////////////////////////////////
664//
665// NetInternalsMessageHandler::IOThreadImpl
666//
667////////////////////////////////////////////////////////////////////////////////
668
669NetInternalsMessageHandler::IOThreadImpl::IOThreadImpl(
670    const base::WeakPtr<NetInternalsMessageHandler>& handler,
671    IOThread* io_thread,
672    net::URLRequestContextGetter* context_getter)
673    : ThreadSafeObserver(net::NetLog::LOG_ALL_BUT_BYTES),
674      handler_(handler),
675      io_thread_(io_thread),
676      context_getter_(context_getter),
677      was_webui_deleted_(false),
678      is_observing_log_(false) {
679  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
680}
681
682NetInternalsMessageHandler::IOThreadImpl::~IOThreadImpl() {
683  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
684}
685
686WebUI::MessageCallback*
687NetInternalsMessageHandler::IOThreadImpl::CreateCallback(
688    MessageHandler method) {
689  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
690  return new CallbackHelper(this, method);
691}
692
693void NetInternalsMessageHandler::IOThreadImpl::Detach() {
694  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
695  // Unregister with network stack to observe events.
696  if (is_observing_log_)
697    io_thread_->net_log()->RemoveObserver(this);
698
699  // Cancel any in-progress connection tests.
700  connection_tester_.reset();
701}
702
703void NetInternalsMessageHandler::IOThreadImpl::SendPassiveLogEntries(
704    const ChromeNetLog::EntryList& passive_entries) {
705  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
706  ListValue* dict_list = new ListValue();
707  for (size_t i = 0; i < passive_entries.size(); ++i) {
708    const ChromeNetLog::Entry& e = passive_entries[i];
709    dict_list->Append(net::NetLog::EntryToDictionaryValue(e.type,
710                                                          e.time,
711                                                          e.source,
712                                                          e.phase,
713                                                          e.params,
714                                                          false));
715  }
716
717  CallJavascriptFunction(L"g_browser.receivedPassiveLogEntries", dict_list);
718}
719
720void NetInternalsMessageHandler::IOThreadImpl::OnWebUIDeleted() {
721  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
722  was_webui_deleted_ = true;
723}
724
725void NetInternalsMessageHandler::IOThreadImpl::OnRendererReady(
726    const ListValue* list) {
727  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
728  DCHECK(!is_observing_log_) << "notifyReady called twice";
729
730  // Tell the javascript about the relationship between event type enums and
731  // their symbolic name.
732  {
733    std::vector<net::NetLog::EventType> event_types =
734        net::NetLog::GetAllEventTypes();
735
736    DictionaryValue* dict = new DictionaryValue();
737
738    for (size_t i = 0; i < event_types.size(); ++i) {
739      const char* name = net::NetLog::EventTypeToString(event_types[i]);
740      dict->SetInteger(name, static_cast<int>(event_types[i]));
741    }
742
743    CallJavascriptFunction(L"g_browser.receivedLogEventTypeConstants", dict);
744  }
745
746  // Tell the javascript about the version of the client and its
747  // command line arguments.
748  {
749    DictionaryValue* dict = new DictionaryValue();
750
751    chrome::VersionInfo version_info;
752
753    if (!version_info.is_valid()) {
754      DLOG(ERROR) << "Unable to create chrome::VersionInfo";
755    } else {
756      // We have everything we need to send the right values.
757      dict->SetString("version", version_info.Version());
758      dict->SetString("cl", version_info.LastChange());
759      dict->SetString("version_mod",
760                      platform_util::GetVersionStringModifier());
761      dict->SetString("official",
762          l10n_util::GetStringUTF16(
763              version_info.IsOfficialBuild() ?
764                IDS_ABOUT_VERSION_OFFICIAL
765              : IDS_ABOUT_VERSION_UNOFFICIAL));
766
767      dict->SetString("command_line",
768          CommandLine::ForCurrentProcess()->command_line_string());
769    }
770
771    CallJavascriptFunction(L"g_browser.receivedClientInfo",
772                           dict);
773  }
774
775  // Tell the javascript about the relationship between load flag enums and
776  // their symbolic name.
777  {
778    DictionaryValue* dict = new DictionaryValue();
779
780#define LOAD_FLAG(label, value) \
781    dict->SetInteger(# label, static_cast<int>(value));
782#include "net/base/load_flags_list.h"
783#undef LOAD_FLAG
784
785    CallJavascriptFunction(L"g_browser.receivedLoadFlagConstants", dict);
786  }
787
788  // Tell the javascript about the relationship between net error codes and
789  // their symbolic name.
790  {
791    DictionaryValue* dict = new DictionaryValue();
792
793#define NET_ERROR(label, value) \
794    dict->SetInteger(# label, static_cast<int>(value));
795#include "net/base/net_error_list.h"
796#undef NET_ERROR
797
798    CallJavascriptFunction(L"g_browser.receivedNetErrorConstants", dict);
799  }
800
801  // Tell the javascript about the relationship between event phase enums and
802  // their symbolic name.
803  {
804    DictionaryValue* dict = new DictionaryValue();
805
806    dict->SetInteger("PHASE_BEGIN", net::NetLog::PHASE_BEGIN);
807    dict->SetInteger("PHASE_END", net::NetLog::PHASE_END);
808    dict->SetInteger("PHASE_NONE", net::NetLog::PHASE_NONE);
809
810    CallJavascriptFunction(L"g_browser.receivedLogEventPhaseConstants", dict);
811  }
812
813  // Tell the javascript about the relationship between source type enums and
814  // their symbolic names.
815  {
816    DictionaryValue* dict = new DictionaryValue();
817
818#define SOURCE_TYPE(label, value) dict->SetInteger(# label, value);
819#include "net/base/net_log_source_type_list.h"
820#undef SOURCE_TYPE
821
822    CallJavascriptFunction(L"g_browser.receivedLogSourceTypeConstants", dict);
823  }
824
825  // Tell the javascript about the relationship between LogLevel enums and their
826  // symbolic names.
827  {
828    DictionaryValue* dict = new DictionaryValue();
829
830    dict->SetInteger("LOG_ALL", net::NetLog::LOG_ALL);
831    dict->SetInteger("LOG_ALL_BUT_BYTES", net::NetLog::LOG_ALL_BUT_BYTES);
832    dict->SetInteger("LOG_BASIC", net::NetLog::LOG_BASIC);
833
834    CallJavascriptFunction(L"g_browser.receivedLogLevelConstants", dict);
835  }
836
837  // Tell the javascript about the relationship between address family enums and
838  // their symbolic names.
839  {
840    DictionaryValue* dict = new DictionaryValue();
841
842    dict->SetInteger("ADDRESS_FAMILY_UNSPECIFIED",
843                     net::ADDRESS_FAMILY_UNSPECIFIED);
844    dict->SetInteger("ADDRESS_FAMILY_IPV4",
845                     net::ADDRESS_FAMILY_IPV4);
846    dict->SetInteger("ADDRESS_FAMILY_IPV6",
847                     net::ADDRESS_FAMILY_IPV6);
848
849    CallJavascriptFunction(L"g_browser.receivedAddressFamilyConstants", dict);
850  }
851
852  // Tell the javascript how the "time ticks" values we have given it relate to
853  // actual system times. (We used time ticks throughout since they are stable
854  // across system clock changes).
855  {
856    int64 cur_time_ms = (base::Time::Now() - base::Time()).InMilliseconds();
857
858    int64 cur_time_ticks_ms =
859        (base::TimeTicks::Now() - base::TimeTicks()).InMilliseconds();
860
861    // If we add this number to a time tick value, it gives the timestamp.
862    int64 tick_to_time_ms = cur_time_ms - cur_time_ticks_ms;
863
864    // Chrome on all platforms stores times using the Windows epoch
865    // (Jan 1 1601), but the javascript wants a unix epoch.
866    // TODO(eroman): Getting the timestamp relative the to unix epoch should
867    //               be part of the time library.
868    const int64 kUnixEpochMs = 11644473600000LL;
869    int64 tick_to_unix_time_ms = tick_to_time_ms - kUnixEpochMs;
870
871    // Pass it as a string, since it may be too large to fit in an integer.
872    CallJavascriptFunction(L"g_browser.receivedTimeTickOffset",
873                           Value::CreateStringValue(
874                               base::Int64ToString(tick_to_unix_time_ms)));
875  }
876
877  // Register with network stack to observe events.
878  is_observing_log_ = true;
879  ChromeNetLog::EntryList entries;
880  io_thread_->net_log()->AddObserverAndGetAllPassivelyCapturedEvents(this,
881                                                                     &entries);
882  SendPassiveLogEntries(entries);
883}
884
885void NetInternalsMessageHandler::IOThreadImpl::OnGetProxySettings(
886    const ListValue* list) {
887  net::URLRequestContext* context = context_getter_->GetURLRequestContext();
888  net::ProxyService* proxy_service = context->proxy_service();
889
890  DictionaryValue* dict = new DictionaryValue();
891  if (proxy_service->fetched_config().is_valid())
892    dict->Set("original", proxy_service->fetched_config().ToValue());
893  if (proxy_service->config().is_valid())
894    dict->Set("effective", proxy_service->config().ToValue());
895
896  CallJavascriptFunction(L"g_browser.receivedProxySettings", dict);
897}
898
899void NetInternalsMessageHandler::IOThreadImpl::OnReloadProxySettings(
900    const ListValue* list) {
901  net::URLRequestContext* context = context_getter_->GetURLRequestContext();
902  context->proxy_service()->ForceReloadProxyConfig();
903
904  // Cause the renderer to be notified of the new values.
905  OnGetProxySettings(NULL);
906}
907
908void NetInternalsMessageHandler::IOThreadImpl::OnGetBadProxies(
909    const ListValue* list) {
910  net::URLRequestContext* context = context_getter_->GetURLRequestContext();
911
912  const net::ProxyRetryInfoMap& bad_proxies_map =
913      context->proxy_service()->proxy_retry_info();
914
915  ListValue* dict_list = new ListValue();
916
917  for (net::ProxyRetryInfoMap::const_iterator it = bad_proxies_map.begin();
918       it != bad_proxies_map.end(); ++it) {
919    const std::string& proxy_uri = it->first;
920    const net::ProxyRetryInfo& retry_info = it->second;
921
922    DictionaryValue* dict = new DictionaryValue();
923    dict->SetString("proxy_uri", proxy_uri);
924    dict->SetString("bad_until",
925                    net::NetLog::TickCountToString(retry_info.bad_until));
926
927    dict_list->Append(dict);
928  }
929
930  CallJavascriptFunction(L"g_browser.receivedBadProxies", dict_list);
931}
932
933void NetInternalsMessageHandler::IOThreadImpl::OnClearBadProxies(
934    const ListValue* list) {
935  net::URLRequestContext* context = context_getter_->GetURLRequestContext();
936  context->proxy_service()->ClearBadProxiesCache();
937
938  // Cause the renderer to be notified of the new values.
939  OnGetBadProxies(NULL);
940}
941
942void NetInternalsMessageHandler::IOThreadImpl::OnGetHostResolverInfo(
943    const ListValue* list) {
944  net::URLRequestContext* context = context_getter_->GetURLRequestContext();
945  net::HostResolverImpl* host_resolver_impl =
946      context->host_resolver()->GetAsHostResolverImpl();
947  net::HostCache* cache = GetHostResolverCache(context);
948
949  if (!host_resolver_impl || !cache) {
950    CallJavascriptFunction(L"g_browser.receivedHostResolverInfo", NULL);
951    return;
952  }
953
954  DictionaryValue* dict = new DictionaryValue();
955
956  dict->SetInteger(
957      "default_address_family",
958      static_cast<int>(host_resolver_impl->GetDefaultAddressFamily()));
959
960  DictionaryValue* cache_info_dict = new DictionaryValue();
961
962  cache_info_dict->SetInteger(
963      "capacity",
964      static_cast<int>(cache->max_entries()));
965  cache_info_dict->SetInteger(
966      "ttl_success_ms",
967      static_cast<int>(cache->success_entry_ttl().InMilliseconds()));
968  cache_info_dict->SetInteger(
969      "ttl_failure_ms",
970      static_cast<int>(cache->failure_entry_ttl().InMilliseconds()));
971
972  ListValue* entry_list = new ListValue();
973
974  for (net::HostCache::EntryMap::const_iterator it =
975       cache->entries().begin();
976       it != cache->entries().end();
977       ++it) {
978    const net::HostCache::Key& key = it->first;
979    const net::HostCache::Entry* entry = it->second.get();
980
981    DictionaryValue* entry_dict = new DictionaryValue();
982
983    entry_dict->SetString("hostname", key.hostname);
984    entry_dict->SetInteger("address_family",
985        static_cast<int>(key.address_family));
986    entry_dict->SetString("expiration",
987                          net::NetLog::TickCountToString(entry->expiration));
988
989    if (entry->error != net::OK) {
990      entry_dict->SetInteger("error", entry->error);
991    } else {
992      // Append all of the resolved addresses.
993      ListValue* address_list = new ListValue();
994      const struct addrinfo* current_address = entry->addrlist.head();
995      while (current_address) {
996        address_list->Append(Value::CreateStringValue(
997            net::NetAddressToStringWithPort(current_address)));
998        current_address = current_address->ai_next;
999      }
1000      entry_dict->Set("addresses", address_list);
1001    }
1002
1003    entry_list->Append(entry_dict);
1004  }
1005
1006  cache_info_dict->Set("entries", entry_list);
1007  dict->Set("cache", cache_info_dict);
1008
1009  CallJavascriptFunction(L"g_browser.receivedHostResolverInfo", dict);
1010}
1011
1012void NetInternalsMessageHandler::IOThreadImpl::OnClearHostResolverCache(
1013    const ListValue* list) {
1014  net::HostCache* cache =
1015      GetHostResolverCache(context_getter_->GetURLRequestContext());
1016
1017  if (cache)
1018    cache->clear();
1019
1020  // Cause the renderer to be notified of the new values.
1021  OnGetHostResolverInfo(NULL);
1022}
1023
1024void NetInternalsMessageHandler::IOThreadImpl::OnEnableIPv6(
1025    const ListValue* list) {
1026  net::URLRequestContext* context = context_getter_->GetURLRequestContext();
1027  net::HostResolverImpl* host_resolver_impl =
1028      context->host_resolver()->GetAsHostResolverImpl();
1029
1030  if (host_resolver_impl) {
1031    host_resolver_impl->SetDefaultAddressFamily(
1032        net::ADDRESS_FAMILY_UNSPECIFIED);
1033  }
1034
1035  // Cause the renderer to be notified of the new value.
1036  OnGetHostResolverInfo(NULL);
1037}
1038
1039void NetInternalsMessageHandler::IOThreadImpl::OnStartConnectionTests(
1040    const ListValue* list) {
1041  // |value| should be: [<URL to test>].
1042  string16 url_str;
1043  CHECK(list->GetString(0, &url_str));
1044
1045  // Try to fix-up the user provided URL into something valid.
1046  // For example, turn "www.google.com" into "http://www.google.com".
1047  GURL url(URLFixerUpper::FixupURL(UTF16ToUTF8(url_str), std::string()));
1048
1049  connection_tester_.reset(new ConnectionTester(
1050      this, io_thread_->globals()->proxy_script_fetcher_context.get()));
1051  connection_tester_->RunAllTests(url);
1052}
1053
1054void NetInternalsMessageHandler::IOThreadImpl::OnHSTSQuery(
1055    const ListValue* list) {
1056  // |list| should be: [<domain to query>].
1057  std::string domain;
1058  CHECK(list->GetString(0, &domain));
1059  DictionaryValue* result = new(DictionaryValue);
1060
1061  if (!IsStringASCII(domain)) {
1062    result->SetString("error", "non-ASCII domain name");
1063  } else {
1064    net::TransportSecurityState* transport_security_state =
1065        context_getter_->GetURLRequestContext()->transport_security_state();
1066    if (!transport_security_state) {
1067      result->SetString("error", "no TransportSecurityState active");
1068    } else {
1069      net::TransportSecurityState::DomainState state;
1070      const bool found = transport_security_state->IsEnabledForHost(
1071          &state, domain, true);
1072
1073      result->SetBoolean("result", found);
1074      if (found) {
1075        result->SetInteger("mode", static_cast<int>(state.mode));
1076        result->SetBoolean("subdomains", state.include_subdomains);
1077        result->SetBoolean("preloaded", state.preloaded);
1078        result->SetString("domain", state.domain);
1079
1080        std::vector<std::string> parts;
1081        for (std::vector<net::SHA1Fingerprint>::const_iterator
1082             i = state.public_key_hashes.begin();
1083             i != state.public_key_hashes.end(); i++) {
1084          std::string part = "sha1/";
1085          std::string hash_str(reinterpret_cast<const char*>(i->data),
1086                               sizeof(i->data));
1087          std::string b64;
1088          base::Base64Encode(hash_str, &b64);
1089          part += b64;
1090          parts.push_back(part);
1091        }
1092        result->SetString("public_key_hashes", JoinString(parts, ','));
1093      }
1094    }
1095  }
1096
1097  CallJavascriptFunction(L"g_browser.receivedHSTSResult", result);
1098}
1099
1100void NetInternalsMessageHandler::IOThreadImpl::OnHSTSAdd(
1101    const ListValue* list) {
1102  // |list| should be: [<domain to query>, <include subdomains>, <cert pins>].
1103  std::string domain;
1104  CHECK(list->GetString(0, &domain));
1105  if (!IsStringASCII(domain)) {
1106    // Silently fail. The user will get a helpful error if they query for the
1107    // name.
1108    return;
1109  }
1110  bool include_subdomains;
1111  CHECK(list->GetBoolean(1, &include_subdomains));
1112  std::string hashes_str;
1113  CHECK(list->GetString(2, &hashes_str));
1114
1115  net::TransportSecurityState* transport_security_state =
1116      context_getter_->GetURLRequestContext()->transport_security_state();
1117  if (!transport_security_state)
1118    return;
1119
1120  net::TransportSecurityState::DomainState state;
1121  state.expiry = state.created + base::TimeDelta::FromDays(1000);
1122  state.include_subdomains = include_subdomains;
1123  state.public_key_hashes.clear();
1124  if (!hashes_str.empty()) {
1125    std::vector<std::string> type_and_b64s;
1126    base::SplitString(hashes_str, ',', &type_and_b64s);
1127    for (std::vector<std::string>::const_iterator
1128         i = type_and_b64s.begin(); i != type_and_b64s.end(); i++) {
1129      std::string type_and_b64;
1130      RemoveChars(*i, " \t\r\n", &type_and_b64);
1131      if (type_and_b64.find("sha1/") != 0)
1132        continue;
1133      std::string b64 = type_and_b64.substr(5, type_and_b64.size() - 5);
1134      std::string hash_str;
1135      if (!base::Base64Decode(b64, &hash_str))
1136        continue;
1137      net::SHA1Fingerprint hash;
1138      if (hash_str.size() != sizeof(hash.data))
1139        continue;
1140      memcpy(hash.data, hash_str.data(), sizeof(hash.data));
1141      state.public_key_hashes.push_back(hash);
1142    }
1143  }
1144
1145  transport_security_state->EnableHost(domain, state);
1146}
1147
1148void NetInternalsMessageHandler::IOThreadImpl::OnHSTSDelete(
1149    const ListValue* list) {
1150  // |list| should be: [<domain to query>].
1151  std::string domain;
1152  CHECK(list->GetString(0, &domain));
1153  if (!IsStringASCII(domain)) {
1154    // There cannot be a unicode entry in the HSTS set.
1155    return;
1156  }
1157  net::TransportSecurityState* transport_security_state =
1158      context_getter_->GetURLRequestContext()->transport_security_state();
1159  if (!transport_security_state)
1160    return;
1161
1162  transport_security_state->DeleteHost(domain);
1163}
1164
1165void NetInternalsMessageHandler::IOThreadImpl::OnGetHttpCacheInfo(
1166    const ListValue* list) {
1167  DictionaryValue* info_dict = new DictionaryValue();
1168  DictionaryValue* stats_dict = new DictionaryValue();
1169
1170  disk_cache::Backend* disk_cache = GetDiskCacheBackend(
1171      context_getter_->GetURLRequestContext());
1172
1173  if (disk_cache) {
1174    // Extract the statistics key/value pairs from the backend.
1175    std::vector<std::pair<std::string, std::string> > stats;
1176    disk_cache->GetStats(&stats);
1177    for (size_t i = 0; i < stats.size(); ++i) {
1178      stats_dict->Set(stats[i].first,
1179                      Value::CreateStringValue(stats[i].second));
1180    }
1181  }
1182
1183  info_dict->Set("stats", stats_dict);
1184
1185  CallJavascriptFunction(L"g_browser.receivedHttpCacheInfo", info_dict);
1186}
1187
1188void NetInternalsMessageHandler::IOThreadImpl::OnGetSocketPoolInfo(
1189    const ListValue* list) {
1190  net::HttpNetworkSession* http_network_session =
1191      GetHttpNetworkSession(context_getter_->GetURLRequestContext());
1192
1193  Value* socket_pool_info = NULL;
1194  if (http_network_session)
1195    socket_pool_info = http_network_session->SocketPoolInfoToValue();
1196
1197  CallJavascriptFunction(L"g_browser.receivedSocketPoolInfo", socket_pool_info);
1198}
1199
1200
1201void NetInternalsMessageHandler::IOThreadImpl::OnFlushSocketPools(
1202    const ListValue* list) {
1203  net::HttpNetworkSession* http_network_session =
1204      GetHttpNetworkSession(context_getter_->GetURLRequestContext());
1205
1206  if (http_network_session)
1207    http_network_session->CloseAllConnections();
1208}
1209
1210void NetInternalsMessageHandler::IOThreadImpl::OnCloseIdleSockets(
1211    const ListValue* list) {
1212  net::HttpNetworkSession* http_network_session =
1213      GetHttpNetworkSession(context_getter_->GetURLRequestContext());
1214
1215  if (http_network_session)
1216    http_network_session->CloseIdleConnections();
1217}
1218
1219void NetInternalsMessageHandler::IOThreadImpl::OnGetSpdySessionInfo(
1220    const ListValue* list) {
1221  net::HttpNetworkSession* http_network_session =
1222      GetHttpNetworkSession(context_getter_->GetURLRequestContext());
1223
1224  Value* spdy_info = NULL;
1225  if (http_network_session) {
1226    spdy_info = http_network_session->SpdySessionPoolInfoToValue();
1227  }
1228
1229  CallJavascriptFunction(L"g_browser.receivedSpdySessionInfo", spdy_info);
1230}
1231
1232void NetInternalsMessageHandler::IOThreadImpl::OnGetSpdyStatus(
1233    const ListValue* list) {
1234  DictionaryValue* status_dict = new DictionaryValue();
1235
1236  status_dict->Set("spdy_enabled",
1237                   Value::CreateBooleanValue(
1238                       net::HttpStreamFactory::spdy_enabled()));
1239  status_dict->Set("use_alternate_protocols",
1240                   Value::CreateBooleanValue(
1241                       net::HttpStreamFactory::use_alternate_protocols()));
1242  status_dict->Set("force_spdy_over_ssl",
1243                   Value::CreateBooleanValue(
1244                       net::HttpStreamFactory::force_spdy_over_ssl()));
1245  status_dict->Set("force_spdy_always",
1246                   Value::CreateBooleanValue(
1247                       net::HttpStreamFactory::force_spdy_always()));
1248  status_dict->Set("next_protos",
1249                   Value::CreateStringValue(
1250                       *net::HttpStreamFactory::next_protos()));
1251
1252  CallJavascriptFunction(L"g_browser.receivedSpdyStatus", status_dict);
1253}
1254
1255void
1256NetInternalsMessageHandler::IOThreadImpl::OnGetSpdyAlternateProtocolMappings(
1257    const ListValue* list) {
1258  net::HttpNetworkSession* http_network_session =
1259      GetHttpNetworkSession(context_getter_->GetURLRequestContext());
1260
1261  ListValue* dict_list = new ListValue();
1262
1263  if (http_network_session) {
1264    const net::HttpAlternateProtocols& http_alternate_protocols =
1265        http_network_session->alternate_protocols();
1266    const net::HttpAlternateProtocols::ProtocolMap& map =
1267        http_alternate_protocols.protocol_map();
1268
1269    for (net::HttpAlternateProtocols::ProtocolMap::const_iterator it =
1270             map.begin();
1271         it != map.end(); ++it) {
1272      DictionaryValue* dict = new DictionaryValue();
1273      dict->SetString("host_port_pair", it->first.ToString());
1274      dict->SetString("alternate_protocol", it->second.ToString());
1275      dict_list->Append(dict);
1276    }
1277  }
1278
1279  CallJavascriptFunction(L"g_browser.receivedSpdyAlternateProtocolMappings",
1280                         dict_list);
1281}
1282
1283#ifdef OS_WIN
1284void NetInternalsMessageHandler::IOThreadImpl::OnGetServiceProviders(
1285    const ListValue* list) {
1286
1287  DictionaryValue* service_providers = new DictionaryValue();
1288
1289  WinsockLayeredServiceProviderList layered_providers;
1290  GetWinsockLayeredServiceProviders(&layered_providers);
1291  ListValue* layered_provider_list = new ListValue();
1292  for (size_t i = 0; i < layered_providers.size(); ++i) {
1293    DictionaryValue* service_dict = new DictionaryValue();
1294    service_dict->SetString("name", layered_providers[i].name);
1295    service_dict->SetInteger("version", layered_providers[i].version);
1296    service_dict->SetInteger("chain_length", layered_providers[i].chain_length);
1297    service_dict->SetInteger("socket_type", layered_providers[i].socket_type);
1298    service_dict->SetInteger("socket_protocol",
1299        layered_providers[i].socket_protocol);
1300    service_dict->SetString("path", layered_providers[i].path);
1301
1302    layered_provider_list->Append(service_dict);
1303  }
1304  service_providers->Set("service_providers", layered_provider_list);
1305
1306  WinsockNamespaceProviderList namespace_providers;
1307  GetWinsockNamespaceProviders(&namespace_providers);
1308  ListValue* namespace_list = new ListValue;
1309  for (size_t i = 0; i < namespace_providers.size(); ++i) {
1310    DictionaryValue* namespace_dict = new DictionaryValue();
1311    namespace_dict->SetString("name", namespace_providers[i].name);
1312    namespace_dict->SetBoolean("active", namespace_providers[i].active);
1313    namespace_dict->SetInteger("version", namespace_providers[i].version);
1314    namespace_dict->SetInteger("type", namespace_providers[i].type);
1315
1316    namespace_list->Append(namespace_dict);
1317  }
1318  service_providers->Set("namespace_providers", namespace_list);
1319
1320  CallJavascriptFunction(L"g_browser.receivedServiceProviders",
1321                         service_providers);
1322}
1323#endif
1324
1325void NetInternalsMessageHandler::IOThreadImpl::OnSetLogLevel(
1326    const ListValue* list) {
1327  int log_level;
1328  std::string log_level_string;
1329  if (!list->GetString(0, &log_level_string) ||
1330      !base::StringToInt(log_level_string, &log_level)) {
1331    NOTREACHED();
1332    return;
1333  }
1334
1335  DCHECK_GE(log_level, net::NetLog::LOG_ALL);
1336  DCHECK_LE(log_level, net::NetLog::LOG_BASIC);
1337  SetLogLevel(static_cast<net::NetLog::LogLevel>(log_level));
1338}
1339
1340// Note that unlike other methods of IOThreadImpl, this function
1341// can be called from ANY THREAD.
1342void NetInternalsMessageHandler::IOThreadImpl::OnAddEntry(
1343    net::NetLog::EventType type,
1344    const base::TimeTicks& time,
1345    const net::NetLog::Source& source,
1346    net::NetLog::EventPhase phase,
1347    net::NetLog::EventParameters* params) {
1348  BrowserThread::PostTask(
1349      BrowserThread::IO, FROM_HERE,
1350      NewRunnableMethod(
1351          this, &IOThreadImpl::AddEntryToQueue,
1352          net::NetLog::EntryToDictionaryValue(type, time, source, phase,
1353                                              params, false)));
1354}
1355
1356void NetInternalsMessageHandler::IOThreadImpl::AddEntryToQueue(Value* entry) {
1357  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1358  if (!pending_entries_.get()) {
1359    pending_entries_.reset(new ListValue());
1360    BrowserThread::PostDelayedTask(
1361        BrowserThread::IO, FROM_HERE,
1362        NewRunnableMethod(this, &IOThreadImpl::PostPendingEntries),
1363        kNetLogEventDelayMilliseconds);
1364  }
1365  pending_entries_->Append(entry);
1366}
1367
1368void NetInternalsMessageHandler::IOThreadImpl::PostPendingEntries() {
1369  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1370  CallJavascriptFunction(
1371      L"g_browser.receivedLogEntries",
1372      pending_entries_.release());
1373}
1374
1375void NetInternalsMessageHandler::IOThreadImpl::OnStartConnectionTestSuite() {
1376  CallJavascriptFunction(L"g_browser.receivedStartConnectionTestSuite", NULL);
1377}
1378
1379void NetInternalsMessageHandler::IOThreadImpl::OnStartConnectionTestExperiment(
1380    const ConnectionTester::Experiment& experiment) {
1381  CallJavascriptFunction(
1382      L"g_browser.receivedStartConnectionTestExperiment",
1383      ExperimentToValue(experiment));
1384}
1385
1386void
1387NetInternalsMessageHandler::IOThreadImpl::OnCompletedConnectionTestExperiment(
1388    const ConnectionTester::Experiment& experiment,
1389    int result) {
1390  DictionaryValue* dict = new DictionaryValue();
1391
1392  dict->Set("experiment", ExperimentToValue(experiment));
1393  dict->SetInteger("result", result);
1394
1395  CallJavascriptFunction(
1396      L"g_browser.receivedCompletedConnectionTestExperiment",
1397      dict);
1398}
1399
1400void
1401NetInternalsMessageHandler::IOThreadImpl::OnCompletedConnectionTestSuite() {
1402  CallJavascriptFunction(
1403      L"g_browser.receivedCompletedConnectionTestSuite",
1404      NULL);
1405}
1406
1407void NetInternalsMessageHandler::IOThreadImpl::DispatchToMessageHandler(
1408    ListValue* arg, MessageHandler method) {
1409  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1410  (this->*method)(arg);
1411  delete arg;
1412}
1413
1414// Note that this can be called from ANY THREAD.
1415void NetInternalsMessageHandler::IOThreadImpl::CallJavascriptFunction(
1416    const std::wstring& function_name,
1417    Value* arg) {
1418  if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
1419    if (handler_ && !was_webui_deleted_) {
1420      // We check |handler_| in case it was deleted on the UI thread earlier
1421      // while we were running on the IO thread.
1422      handler_->CallJavascriptFunction(function_name, arg);
1423    }
1424    delete arg;
1425    return;
1426  }
1427
1428  if (!BrowserThread::PostTask(
1429           BrowserThread::UI, FROM_HERE,
1430           NewRunnableMethod(
1431               this,
1432               &IOThreadImpl::CallJavascriptFunction,
1433               function_name, arg))) {
1434    // Failed posting the task, avoid leaking.
1435    delete arg;
1436  }
1437}
1438
1439}  // namespace
1440
1441
1442////////////////////////////////////////////////////////////////////////////////
1443//
1444// NetInternalsUI
1445//
1446////////////////////////////////////////////////////////////////////////////////
1447
1448NetInternalsUI::NetInternalsUI(TabContents* contents) : WebUI(contents) {
1449  AddMessageHandler((new NetInternalsMessageHandler())->Attach(this));
1450
1451  NetInternalsHTMLSource* html_source = new NetInternalsHTMLSource();
1452
1453  // Set up the chrome://net-internals/ source.
1454  contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source);
1455}
1456