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.
5 #include "chrome/browser/ui/webui/net_internals/net_internals_ui.h"
13 #include "base/base64.h"
14 #include "base/bind.h"
15 #include "base/bind_helpers.h"
16 #include "base/command_line.h"
17 #include "base/file_util.h"
18 #include "base/files/file_path.h"
19 #include "base/memory/weak_ptr.h"
20 #include "base/message_loop/message_loop.h"
21 #include "base/platform_file.h"
22 #include "base/prefs/pref_member.h"
23 #include "base/sequenced_task_runner_helpers.h"
24 #include "base/strings/string_number_conversions.h"
25 #include "base/strings/string_piece.h"
26 #include "base/strings/string_split.h"
27 #include "base/strings/string_util.h"
28 #include "base/strings/utf_string_conversions.h"
29 #include "base/threading/worker_pool.h"
30 #include "base/values.h"
31 #include "chrome/browser/browser_process.h"
32 #include "chrome/browser/browsing_data/browsing_data_helper.h"
33 #include "chrome/browser/browsing_data/browsing_data_remover.h"
34 #include "chrome/browser/chrome_notification_types.h"
35 #include "chrome/browser/download/download_prefs.h"
36 #include "chrome/browser/extensions/extension_service.h"
37 #include "chrome/browser/extensions/extension_system.h"
38 #include "chrome/browser/io_thread.h"
39 #include "chrome/browser/net/chrome_net_log.h"
40 #include "chrome/browser/net/chrome_network_delegate.h"
41 #include "chrome/browser/net/connection_tester.h"
42 #include "chrome/browser/prerender/prerender_manager.h"
43 #include "chrome/browser/prerender/prerender_manager_factory.h"
44 #include "chrome/browser/profiles/profile.h"
45 #include "chrome/browser/ui/webui/extensions/extension_basic_info.h"
46 #include "chrome/common/cancelable_task_tracker.h"
47 #include "chrome/common/chrome_paths.h"
48 #include "chrome/common/chrome_version_info.h"
49 #include "chrome/common/logging_chrome.h"
50 #include "chrome/common/net/url_fixer_upper.h"
51 #include "chrome/common/pref_names.h"
52 #include "chrome/common/url_constants.h"
53 #include "components/onc/onc_constants.h"
54 #include "content/public/browser/browser_thread.h"
55 #include "content/public/browser/notification_details.h"
56 #include "content/public/browser/resource_dispatcher_host.h"
57 #include "content/public/browser/web_contents.h"
58 #include "content/public/browser/web_ui.h"
59 #include "content/public/browser/web_ui_data_source.h"
60 #include "content/public/browser/web_ui_message_handler.h"
61 #include "extensions/common/extension_set.h"
62 #include "grit/generated_resources.h"
63 #include "grit/net_internals_resources.h"
64 #include "net/base/net_errors.h"
65 #include "net/base/net_log_logger.h"
66 #include "net/base/net_util.h"
67 #include "net/disk_cache/disk_cache.h"
68 #include "net/dns/host_cache.h"
69 #include "net/dns/host_resolver.h"
70 #include "net/http/http_cache.h"
71 #include "net/http/http_network_layer.h"
72 #include "net/http/http_network_session.h"
73 #include "net/http/http_server_properties.h"
74 #include "net/http/http_stream_factory.h"
75 #include "net/http/transport_security_state.h"
76 #include "net/proxy/proxy_service.h"
77 #include "net/url_request/url_request_context.h"
78 #include "net/url_request/url_request_context_getter.h"
79 #include "ui/base/resource/resource_bundle.h"
81 #if defined(OS_CHROMEOS)
82 #include "chrome/browser/chromeos/login/user.h"
83 #include "chrome/browser/chromeos/login/user_manager.h"
84 #include "chrome/browser/chromeos/net/onc_utils.h"
85 #include "chrome/browser/chromeos/system/syslogs_provider.h"
86 #include "chromeos/dbus/dbus_thread_manager.h"
87 #include "chromeos/dbus/debug_daemon_client.h"
88 #include "chromeos/network/onc/onc_certificate_importer_impl.h"
89 #include "chromeos/network/onc/onc_utils.h"
92 #include "chrome/browser/net/service_providers_win.h"
95 using base::PassPlatformFile
;
96 using base::PlatformFile
;
97 using base::PlatformFileError
;
98 using base::StringValue
;
99 using content::BrowserThread
;
100 using content::WebContents
;
101 using content::WebUIMessageHandler
;
105 // Delay between when an event occurs and when it is passed to the Javascript
106 // page. All events that occur during this period are grouped together and
107 // sent to the page at once, which reduces context switching and CPU usage.
108 const int kNetLogEventDelayMilliseconds
= 100;
110 // Returns the HostCache for |context|'s primary HostResolver, or NULL if
112 net::HostCache
* GetHostResolverCache(net::URLRequestContext
* context
) {
113 return context
->host_resolver()->GetHostCache();
116 std::string
HashesToBase64String(const net::HashValueVector
& hashes
) {
118 for (size_t i
= 0; i
!= hashes
.size(); ++i
) {
121 str
+= hashes
[i
].ToString();
126 bool Base64StringToHashes(const std::string
& hashes_str
,
127 net::HashValueVector
* hashes
) {
129 std::vector
<std::string
> vector_hash_str
;
130 base::SplitString(hashes_str
, ',', &vector_hash_str
);
132 for (size_t i
= 0; i
!= vector_hash_str
.size(); ++i
) {
133 std::string hash_str
;
134 base::RemoveChars(vector_hash_str
[i
], " \t\r\n", &hash_str
);
136 // Skip past unrecognized hash algos
137 // But return false on malformatted input
138 if (hash_str
.empty())
140 if (hash_str
.compare(0, 5, "sha1/") != 0 &&
141 hash_str
.compare(0, 7, "sha256/") != 0) {
144 if (!hash
.FromString(hash_str
))
146 hashes
->push_back(hash
);
151 // Returns a Value representing the state of a pre-existing URLRequest when
152 // net-internals was opened.
153 base::Value
* GetRequestStateAsValue(const net::URLRequest
* request
,
154 net::NetLog::LogLevel log_level
) {
155 return request
->GetStateAsValue();
158 // Returns true if |request1| was created before |request2|.
159 bool RequestCreatedBefore(const net::URLRequest
* request1
,
160 const net::URLRequest
* request2
) {
161 return request1
->creation_time() < request2
->creation_time();
164 // Returns the disk cache backend for |context| if there is one, or NULL.
165 disk_cache::Backend
* GetDiskCacheBackend(net::URLRequestContext
* context
) {
166 if (!context
->http_transaction_factory())
169 net::HttpCache
* http_cache
= context
->http_transaction_factory()->GetCache();
173 return http_cache
->GetCurrentBackend();
176 // Returns the http network session for |context| if there is one.
177 // Otherwise, returns NULL.
178 net::HttpNetworkSession
* GetHttpNetworkSession(
179 net::URLRequestContext
* context
) {
180 if (!context
->http_transaction_factory())
183 return context
->http_transaction_factory()->GetSession();
186 base::Value
* ExperimentToValue(const ConnectionTester::Experiment
& experiment
) {
187 base::DictionaryValue
* dict
= new base::DictionaryValue();
189 if (experiment
.url
.is_valid())
190 dict
->SetString("url", experiment
.url
.spec());
192 dict
->SetString("proxy_settings_experiment",
193 ConnectionTester::ProxySettingsExperimentDescription(
194 experiment
.proxy_settings_experiment
));
195 dict
->SetString("host_resolver_experiment",
196 ConnectionTester::HostResolverExperimentDescription(
197 experiment
.host_resolver_experiment
));
201 content::WebUIDataSource
* CreateNetInternalsHTMLSource() {
202 content::WebUIDataSource
* source
=
203 content::WebUIDataSource::Create(chrome::kChromeUINetInternalsHost
);
205 source
->SetDefaultResource(IDR_NET_INTERNALS_INDEX_HTML
);
206 source
->AddResourcePath("index.js", IDR_NET_INTERNALS_INDEX_JS
);
207 source
->SetJsonPath("strings.js");
211 #if defined(OS_CHROMEOS)
212 // Small helper class used to create temporary log file and pass its
213 // handle and error status to callback.
215 // DebugLogFileHelper* helper = new DebugLogFileHelper();
216 // base::WorkerPool::PostTaskAndReply(FROM_HERE,
217 // base::Bind(&DebugLogFileHelper::DoWork, base::Unretained(helper), ...),
218 // base::Bind(&DebugLogFileHelper::Reply, base::Owned(helper), ...),
220 class DebugLogFileHelper
{
222 typedef base::Callback
<void(PassPlatformFile pass_platform_file
,
224 PlatformFileError error
,
225 const base::FilePath
& file_path
)>
226 DebugLogFileCallback
;
229 : file_handle_(base::kInvalidPlatformFileValue
),
231 error_(base::PLATFORM_FILE_OK
) {
234 ~DebugLogFileHelper() {
237 void DoWork(const base::FilePath
& fileshelf
) {
238 const base::FilePath::CharType kLogFileName
[] =
239 FILE_PATH_LITERAL("debug-log.tgz");
241 file_path_
= fileshelf
.Append(kLogFileName
);
242 file_path_
= logging::GenerateTimestampedName(file_path_
,
246 base::PLATFORM_FILE_CREATE_ALWAYS
|
247 base::PLATFORM_FILE_WRITE
;
248 file_handle_
= base::CreatePlatformFile(file_path_
, flags
,
252 void Reply(const DebugLogFileCallback
& callback
) {
253 DCHECK(!callback
.is_null());
254 callback
.Run(PassPlatformFile(&file_handle_
), created_
, error_
, file_path_
);
258 PlatformFile file_handle_
;
260 PlatformFileError error_
;
261 base::FilePath file_path_
;
263 DISALLOW_COPY_AND_ASSIGN(DebugLogFileHelper
);
266 // Following functions are used for getting debug logs. Logs are
267 // fetched from /var/log/* and put on the fileshelf.
269 // Called once StoreDebugLogs is complete. Takes two parameters:
270 // - log_path: where the log file was saved in the case of success;
271 // - succeeded: was the log file saved successfully.
272 typedef base::Callback
<void(const base::FilePath
& log_path
,
273 bool succeded
)> StoreDebugLogsCallback
;
275 // Closes file handle, so, should be called on the WorkerPool thread.
276 void CloseDebugLogFile(PassPlatformFile pass_platform_file
) {
277 base::ClosePlatformFile(pass_platform_file
.ReleaseValue());
280 // Closes file handle and deletes debug log file, so, should be called
281 // on the WorkerPool thread.
282 void CloseAndDeleteDebugLogFile(PassPlatformFile pass_platform_file
,
283 const base::FilePath
& file_path
) {
284 CloseDebugLogFile(pass_platform_file
);
285 base::DeleteFile(file_path
, false);
288 // Called upon completion of |WriteDebugLogToFile|. Closes file
289 // descriptor, deletes log file in the case of failure and calls
291 void WriteDebugLogToFileCompleted(const StoreDebugLogsCallback
& callback
,
292 PassPlatformFile pass_platform_file
,
293 const base::FilePath
& file_path
,
295 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
297 bool posted
= base::WorkerPool::PostTaskAndReply(FROM_HERE
,
298 base::Bind(&CloseAndDeleteDebugLogFile
, pass_platform_file
, file_path
),
299 base::Bind(callback
, file_path
, false), false);
303 bool posted
= base::WorkerPool::PostTaskAndReply(FROM_HERE
,
304 base::Bind(&CloseDebugLogFile
, pass_platform_file
),
305 base::Bind(callback
, file_path
, true), false);
309 // Stores into |file_path| debug logs in the .tgz format. Calls
310 // |callback| upon completion.
311 void WriteDebugLogToFile(const StoreDebugLogsCallback
& callback
,
312 PassPlatformFile pass_platform_file
,
314 PlatformFileError error
,
315 const base::FilePath
& file_path
) {
316 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
319 "Can't create debug log file: " << file_path
.AsUTF8Unsafe() << ", " <<
321 bool posted
= base::WorkerPool::PostTaskAndReply(FROM_HERE
,
322 base::Bind(&CloseDebugLogFile
, pass_platform_file
),
323 base::Bind(callback
, file_path
, false), false);
327 PlatformFile platform_file
= pass_platform_file
.ReleaseValue();
328 chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->GetDebugLogs(
330 base::Bind(&WriteDebugLogToFileCompleted
,
331 callback
, PassPlatformFile(&platform_file
), file_path
));
334 // Stores debug logs in the .tgz archive on the |fileshelf|. The file
335 // is created on the worker pool, then writing to it is triggered from
336 // the UI thread, and finally it is closed (on success) or deleted (on
337 // failure) on the worker pool, prior to calling |callback|.
338 void StoreDebugLogs(const base::FilePath
& fileshelf
,
339 const StoreDebugLogsCallback
& callback
) {
340 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
341 DCHECK(!callback
.is_null());
342 DebugLogFileHelper
* helper
= new DebugLogFileHelper();
343 bool posted
= base::WorkerPool::PostTaskAndReply(FROM_HERE
,
344 base::Bind(&DebugLogFileHelper::DoWork
,
345 base::Unretained(helper
), fileshelf
),
346 base::Bind(&DebugLogFileHelper::Reply
, base::Owned(helper
),
347 base::Bind(&WriteDebugLogToFile
, callback
)), false);
350 #endif // defined(OS_CHROMEOS)
352 // This class receives javascript messages from the renderer.
353 // Note that the WebUI infrastructure runs on the UI thread, therefore all of
354 // this class's methods are expected to run on the UI thread.
356 // Since the network code we want to run lives on the IO thread, we proxy
357 // almost everything over to NetInternalsMessageHandler::IOThreadImpl, which
358 // runs on the IO thread.
360 // TODO(eroman): Can we start on the IO thread to begin with?
361 class NetInternalsMessageHandler
362 : public WebUIMessageHandler
,
363 public base::SupportsWeakPtr
<NetInternalsMessageHandler
> {
365 NetInternalsMessageHandler();
366 virtual ~NetInternalsMessageHandler();
368 // WebUIMessageHandler implementation.
369 virtual void RegisterMessages() OVERRIDE
;
371 // Calls g_browser.receive in the renderer, passing in |command| and |arg|.
372 // Takes ownership of |arg|. If the renderer is displaying a log file, the
373 // message will be ignored.
374 void SendJavascriptCommand(const std::string
& command
, base::Value
* arg
);
376 // Javascript message handlers.
377 void OnRendererReady(const base::ListValue
* list
);
378 void OnClearBrowserCache(const base::ListValue
* list
);
379 void OnGetPrerenderInfo(const base::ListValue
* list
);
380 void OnGetHistoricNetworkStats(const base::ListValue
* list
);
381 void OnGetExtensionInfo(const base::ListValue
* list
);
382 #if defined(OS_CHROMEOS)
383 void OnRefreshSystemLogs(const base::ListValue
* list
);
384 void OnGetSystemLog(const base::ListValue
* list
);
385 void OnImportONCFile(const base::ListValue
* list
);
386 void OnStoreDebugLogs(const base::ListValue
* list
);
387 void OnStoreDebugLogsCompleted(const base::FilePath
& log_path
,
389 void OnSetNetworkDebugMode(const base::ListValue
* list
);
390 void OnSetNetworkDebugModeCompleted(const std::string
& subsystem
,
397 #if defined(OS_CHROMEOS)
398 // Class that is used for getting network related ChromeOS logs.
399 // Logs are fetched from ChromeOS libcros on user request, and only when we
400 // don't yet have a copy of logs. If a copy is present, we send back data from
401 // it, else we save request and answer to it when we get logs from libcros.
402 // If needed, we also send request for system logs to libcros.
403 // Logs refresh has to be done explicitly, by deleting old logs and then
404 // loading them again.
405 class SystemLogsGetter
{
407 SystemLogsGetter(NetInternalsMessageHandler
* handler
,
408 chromeos::system::SyslogsProvider
* syslogs_provider
);
411 // Deletes logs copy we currently have, and resets logs_requested and
412 // logs_received flags.
413 void DeleteSystemLogs();
414 // Starts log fetching. If logs copy is present, requested logs are sent
416 // If syslogs load request hasn't been sent to libcros yet, we do that now,
417 // and postpone sending response.
418 // Request data is specified by args:
419 // $1 : key of the log we are interested in.
420 // $2 : string used to identify request.
421 void RequestSystemLog(const base::ListValue
* args
);
422 // Requests logs from libcros, but only if we don't have a copy.
423 void LoadSystemLogs();
424 // Processes callback from libcros containing system logs. Postponed
425 // request responses are sent.
426 void OnSystemLogsLoaded(chromeos::system::LogDictionaryType
* sys_info
,
427 std::string
* ignored_content
);
430 // Struct we save postponed log request in.
431 struct SystemLogRequest
{
436 // Processes request.
437 void SendLogs(const SystemLogRequest
& request
);
439 NetInternalsMessageHandler
* handler_
;
440 chromeos::system::SyslogsProvider
* syslogs_provider_
;
441 // List of postponed requests.
442 std::list
<SystemLogRequest
> requests_
;
443 scoped_ptr
<chromeos::system::LogDictionaryType
> logs_
;
445 bool logs_requested_
;
446 CancelableTaskTracker tracker_
;
447 // Libcros request task ID.
448 CancelableTaskTracker::TaskId syslogs_task_id_
;
450 #endif // defined(OS_CHROMEOS)
452 // This is the "real" message handler, which lives on the IO thread.
453 scoped_refptr
<IOThreadImpl
> proxy_
;
455 base::WeakPtr
<prerender::PrerenderManager
> prerender_manager_
;
457 #if defined(OS_CHROMEOS)
458 // Class that handles getting and filtering system logs.
459 scoped_ptr
<SystemLogsGetter
> syslogs_getter_
;
462 DISALLOW_COPY_AND_ASSIGN(NetInternalsMessageHandler
);
465 // This class is the "real" message handler. It is allocated and destroyed on
466 // the UI thread. With the exception of OnAddEntry, OnWebUIDeleted, and
467 // SendJavascriptCommand, its methods are all expected to be called from the IO
468 // thread. OnAddEntry and SendJavascriptCommand can be called from any thread,
469 // and OnWebUIDeleted can only be called from the UI thread.
470 class NetInternalsMessageHandler::IOThreadImpl
471 : public base::RefCountedThreadSafe
<
472 NetInternalsMessageHandler::IOThreadImpl
,
473 BrowserThread::DeleteOnUIThread
>,
474 public net::NetLog::ThreadSafeObserver
,
475 public ConnectionTester::Delegate
{
477 // Type for methods that can be used as MessageHandler callbacks.
478 typedef void (IOThreadImpl::*MessageHandler
)(const base::ListValue
*);
480 // Creates a proxy for |handler| that will live on the IO thread.
481 // |handler| is a weak pointer, since it is possible for the
482 // WebUIMessageHandler to be deleted on the UI thread while we were executing
483 // on the IO thread. |io_thread| is the global IOThread (it is passed in as
484 // an argument since we need to grab it from the UI thread).
486 const base::WeakPtr
<NetInternalsMessageHandler
>& handler
,
488 net::URLRequestContextGetter
* main_context_getter
);
490 // Called on UI thread just after creation, to add a ContextGetter to
491 // |context_getters_|.
492 void AddRequestContextGetter(net::URLRequestContextGetter
* context_getter
);
494 // Helper method to enable a callback that will be executed on the IO thread.
495 static void CallbackHelper(MessageHandler method
,
496 scoped_refptr
<IOThreadImpl
> io_thread
,
497 const base::ListValue
* list
);
499 // Called once the WebUI has been deleted (i.e. renderer went away), on the
503 // Called when the WebUI is deleted. Prevents calling Javascript functions
504 // afterwards. Called on UI thread.
505 void OnWebUIDeleted();
507 //--------------------------------
508 // Javascript message handlers:
509 //--------------------------------
511 void OnRendererReady(const base::ListValue
* list
);
513 void OnGetProxySettings(const base::ListValue
* list
);
514 void OnReloadProxySettings(const base::ListValue
* list
);
515 void OnGetBadProxies(const base::ListValue
* list
);
516 void OnClearBadProxies(const base::ListValue
* list
);
517 void OnGetHostResolverInfo(const base::ListValue
* list
);
518 void OnClearHostResolverCache(const base::ListValue
* list
);
519 void OnEnableIPv6(const base::ListValue
* list
);
520 void OnStartConnectionTests(const base::ListValue
* list
);
521 void OnHSTSQuery(const base::ListValue
* list
);
522 void OnHSTSAdd(const base::ListValue
* list
);
523 void OnHSTSDelete(const base::ListValue
* list
);
524 void OnGetHttpCacheInfo(const base::ListValue
* list
);
525 void OnGetSocketPoolInfo(const base::ListValue
* list
);
526 void OnGetSessionNetworkStats(const base::ListValue
* list
);
527 void OnCloseIdleSockets(const base::ListValue
* list
);
528 void OnFlushSocketPools(const base::ListValue
* list
);
529 void OnGetSpdySessionInfo(const base::ListValue
* list
);
530 void OnGetSpdyStatus(const base::ListValue
* list
);
531 void OnGetSpdyAlternateProtocolMappings(const base::ListValue
* list
);
532 void OnGetQuicInfo(const base::ListValue
* list
);
534 void OnGetServiceProviders(const base::ListValue
* list
);
536 void OnGetHttpPipeliningStatus(const base::ListValue
* list
);
537 void OnSetLogLevel(const base::ListValue
* list
);
539 // ChromeNetLog::ThreadSafeObserver implementation:
540 virtual void OnAddEntry(const net::NetLog::Entry
& entry
) OVERRIDE
;
542 // ConnectionTester::Delegate implementation:
543 virtual void OnStartConnectionTestSuite() OVERRIDE
;
544 virtual void OnStartConnectionTestExperiment(
545 const ConnectionTester::Experiment
& experiment
) OVERRIDE
;
546 virtual void OnCompletedConnectionTestExperiment(
547 const ConnectionTester::Experiment
& experiment
,
548 int result
) OVERRIDE
;
549 virtual void OnCompletedConnectionTestSuite() OVERRIDE
;
551 // Helper that calls g_browser.receive in the renderer, passing in |command|
552 // and |arg|. Takes ownership of |arg|. If the renderer is displaying a log
553 // file, the message will be ignored. Note that this can be called from any
555 void SendJavascriptCommand(const std::string
& command
, base::Value
* arg
);
558 friend struct BrowserThread::DeleteOnThread
<BrowserThread::UI
>;
559 friend class base::DeleteHelper
<IOThreadImpl
>;
561 typedef std::list
<scoped_refptr
<net::URLRequestContextGetter
> >
564 virtual ~IOThreadImpl();
566 // Adds |entry| to the queue of pending log entries to be sent to the page via
567 // Javascript. Must be called on the IO Thread. Also creates a delayed task
568 // that will call PostPendingEntries, if there isn't one already.
569 void AddEntryToQueue(base::Value
* entry
);
571 // Sends all pending entries to the page via Javascript, and clears the list
572 // of pending entries. Sending multiple entries at once results in a
573 // significant reduction of CPU usage when a lot of events are happening.
574 // Must be called on the IO Thread.
575 void PostPendingEntries();
577 // Adds entries with the states of ongoing URL requests.
578 void PrePopulateEventList();
580 net::URLRequestContext
* GetMainContext() {
581 return main_context_getter_
->GetURLRequestContext();
584 // Pointer to the UI-thread message handler. Only access this from
586 base::WeakPtr
<NetInternalsMessageHandler
> handler_
;
588 // The global IOThread, which contains the global NetLog to observer.
589 IOThread
* io_thread_
;
591 // The main URLRequestContextGetter for the tab's profile.
592 scoped_refptr
<net::URLRequestContextGetter
> main_context_getter_
;
594 // Helper that runs the suite of connection tests.
595 scoped_ptr
<ConnectionTester
> connection_tester_
;
597 // True if the Web UI has been deleted. This is used to prevent calling
598 // Javascript functions after the Web UI is destroyed. On refresh, the
599 // messages can end up being sent to the refreshed page, causing duplicate
600 // or partial entries.
602 // This is only read and written to on the UI thread.
603 bool was_webui_deleted_
;
605 // Log entries that have yet to be passed along to Javascript page. Non-NULL
606 // when and only when there is a pending delayed task to call
607 // PostPendingEntries. Read and written to exclusively on the IO Thread.
608 scoped_ptr
<base::ListValue
> pending_entries_
;
610 // Used for getting current status of URLRequests when net-internals is
611 // opened. |main_context_getter_| is automatically added on construction.
612 // Duplicates are allowed.
613 ContextGetterList context_getters_
;
615 DISALLOW_COPY_AND_ASSIGN(IOThreadImpl
);
618 ////////////////////////////////////////////////////////////////////////////////
620 // NetInternalsMessageHandler
622 ////////////////////////////////////////////////////////////////////////////////
624 NetInternalsMessageHandler::NetInternalsMessageHandler() {}
626 NetInternalsMessageHandler::~NetInternalsMessageHandler() {
628 proxy_
.get()->OnWebUIDeleted();
629 // Notify the handler on the IO thread that the renderer is gone.
630 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
,
631 base::Bind(&IOThreadImpl::Detach
, proxy_
.get()));
635 void NetInternalsMessageHandler::RegisterMessages() {
636 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
638 Profile
* profile
= Profile::FromWebUI(web_ui());
640 proxy_
= new IOThreadImpl(this->AsWeakPtr(), g_browser_process
->io_thread(),
641 profile
->GetRequestContext());
642 proxy_
->AddRequestContextGetter(profile
->GetMediaRequestContext());
643 proxy_
->AddRequestContextGetter(profile
->GetRequestContextForExtensions());
644 #if defined(OS_CHROMEOS)
645 syslogs_getter_
.reset(new SystemLogsGetter(this,
646 chromeos::system::SyslogsProvider::GetInstance()));
649 prerender::PrerenderManager
* prerender_manager
=
650 prerender::PrerenderManagerFactory::GetForProfile(profile
);
651 if (prerender_manager
) {
652 prerender_manager_
= prerender_manager
->AsWeakPtr();
654 prerender_manager_
= base::WeakPtr
<prerender::PrerenderManager
>();
657 web_ui()->RegisterMessageCallback(
659 base::Bind(&NetInternalsMessageHandler::OnRendererReady
,
660 base::Unretained(this)));
661 web_ui()->RegisterMessageCallback(
663 base::Bind(&IOThreadImpl::CallbackHelper
,
664 &IOThreadImpl::OnGetProxySettings
, proxy_
));
665 web_ui()->RegisterMessageCallback(
666 "reloadProxySettings",
667 base::Bind(&IOThreadImpl::CallbackHelper
,
668 &IOThreadImpl::OnReloadProxySettings
, proxy_
));
669 web_ui()->RegisterMessageCallback(
671 base::Bind(&IOThreadImpl::CallbackHelper
,
672 &IOThreadImpl::OnGetBadProxies
, proxy_
));
673 web_ui()->RegisterMessageCallback(
675 base::Bind(&IOThreadImpl::CallbackHelper
,
676 &IOThreadImpl::OnClearBadProxies
, proxy_
));
677 web_ui()->RegisterMessageCallback(
678 "getHostResolverInfo",
679 base::Bind(&IOThreadImpl::CallbackHelper
,
680 &IOThreadImpl::OnGetHostResolverInfo
, proxy_
));
681 web_ui()->RegisterMessageCallback(
682 "clearHostResolverCache",
683 base::Bind(&IOThreadImpl::CallbackHelper
,
684 &IOThreadImpl::OnClearHostResolverCache
, proxy_
));
685 web_ui()->RegisterMessageCallback(
687 base::Bind(&IOThreadImpl::CallbackHelper
,
688 &IOThreadImpl::OnEnableIPv6
, proxy_
));
689 web_ui()->RegisterMessageCallback(
690 "startConnectionTests",
691 base::Bind(&IOThreadImpl::CallbackHelper
,
692 &IOThreadImpl::OnStartConnectionTests
, proxy_
));
693 web_ui()->RegisterMessageCallback(
695 base::Bind(&IOThreadImpl::CallbackHelper
,
696 &IOThreadImpl::OnHSTSQuery
, proxy_
));
697 web_ui()->RegisterMessageCallback(
699 base::Bind(&IOThreadImpl::CallbackHelper
,
700 &IOThreadImpl::OnHSTSAdd
, proxy_
));
701 web_ui()->RegisterMessageCallback(
703 base::Bind(&IOThreadImpl::CallbackHelper
,
704 &IOThreadImpl::OnHSTSDelete
, proxy_
));
705 web_ui()->RegisterMessageCallback(
707 base::Bind(&IOThreadImpl::CallbackHelper
,
708 &IOThreadImpl::OnGetHttpCacheInfo
, proxy_
));
709 web_ui()->RegisterMessageCallback(
711 base::Bind(&IOThreadImpl::CallbackHelper
,
712 &IOThreadImpl::OnGetSocketPoolInfo
, proxy_
));
713 web_ui()->RegisterMessageCallback(
714 "getSessionNetworkStats",
715 base::Bind(&IOThreadImpl::CallbackHelper
,
716 &IOThreadImpl::OnGetSessionNetworkStats
, proxy_
));
717 web_ui()->RegisterMessageCallback(
719 base::Bind(&IOThreadImpl::CallbackHelper
,
720 &IOThreadImpl::OnCloseIdleSockets
, proxy_
));
721 web_ui()->RegisterMessageCallback(
723 base::Bind(&IOThreadImpl::CallbackHelper
,
724 &IOThreadImpl::OnFlushSocketPools
, proxy_
));
725 web_ui()->RegisterMessageCallback(
726 "getSpdySessionInfo",
727 base::Bind(&IOThreadImpl::CallbackHelper
,
728 &IOThreadImpl::OnGetSpdySessionInfo
, proxy_
));
729 web_ui()->RegisterMessageCallback(
731 base::Bind(&IOThreadImpl::CallbackHelper
,
732 &IOThreadImpl::OnGetSpdyStatus
, proxy_
));
733 web_ui()->RegisterMessageCallback(
734 "getSpdyAlternateProtocolMappings",
735 base::Bind(&IOThreadImpl::CallbackHelper
,
736 &IOThreadImpl::OnGetSpdyAlternateProtocolMappings
, proxy_
));
737 web_ui()->RegisterMessageCallback(
739 base::Bind(&IOThreadImpl::CallbackHelper
,
740 &IOThreadImpl::OnGetQuicInfo
, proxy_
));
742 web_ui()->RegisterMessageCallback(
743 "getServiceProviders",
744 base::Bind(&IOThreadImpl::CallbackHelper
,
745 &IOThreadImpl::OnGetServiceProviders
, proxy_
));
748 web_ui()->RegisterMessageCallback(
749 "getHttpPipeliningStatus",
750 base::Bind(&IOThreadImpl::CallbackHelper
,
751 &IOThreadImpl::OnGetHttpPipeliningStatus
, proxy_
));
752 web_ui()->RegisterMessageCallback(
754 base::Bind(&IOThreadImpl::CallbackHelper
,
755 &IOThreadImpl::OnSetLogLevel
, proxy_
));
756 web_ui()->RegisterMessageCallback(
758 base::Bind(&NetInternalsMessageHandler::OnClearBrowserCache
,
759 base::Unretained(this)));
760 web_ui()->RegisterMessageCallback(
762 base::Bind(&NetInternalsMessageHandler::OnGetPrerenderInfo
,
763 base::Unretained(this)));
764 web_ui()->RegisterMessageCallback(
765 "getHistoricNetworkStats",
766 base::Bind(&NetInternalsMessageHandler::OnGetHistoricNetworkStats
,
767 base::Unretained(this)));
768 web_ui()->RegisterMessageCallback(
770 base::Bind(&NetInternalsMessageHandler::OnGetExtensionInfo
,
771 base::Unretained(this)));
772 #if defined(OS_CHROMEOS)
773 web_ui()->RegisterMessageCallback(
775 base::Bind(&NetInternalsMessageHandler::OnRefreshSystemLogs
,
776 base::Unretained(this)));
777 web_ui()->RegisterMessageCallback(
779 base::Bind(&NetInternalsMessageHandler::OnGetSystemLog
,
780 base::Unretained(this)));
781 web_ui()->RegisterMessageCallback(
783 base::Bind(&NetInternalsMessageHandler::OnImportONCFile
,
784 base::Unretained(this)));
785 web_ui()->RegisterMessageCallback(
787 base::Bind(&NetInternalsMessageHandler::OnStoreDebugLogs
,
788 base::Unretained(this)));
789 web_ui()->RegisterMessageCallback(
790 "setNetworkDebugMode",
791 base::Bind(&NetInternalsMessageHandler::OnSetNetworkDebugMode
,
792 base::Unretained(this)));
796 void NetInternalsMessageHandler::SendJavascriptCommand(
797 const std::string
& command
,
799 scoped_ptr
<base::Value
> command_value(new base::StringValue(command
));
800 scoped_ptr
<base::Value
> value(arg
);
801 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
803 web_ui()->CallJavascriptFunction("g_browser.receive",
804 *command_value
.get(),
807 web_ui()->CallJavascriptFunction("g_browser.receive",
808 *command_value
.get());
812 void NetInternalsMessageHandler::OnRendererReady(const base::ListValue
* list
) {
813 IOThreadImpl::CallbackHelper(&IOThreadImpl::OnRendererReady
, proxy_
, list
);
816 void NetInternalsMessageHandler::OnClearBrowserCache(
817 const base::ListValue
* list
) {
818 BrowsingDataRemover
* remover
= BrowsingDataRemover::CreateForUnboundedRange(
819 Profile::FromWebUI(web_ui()));
820 remover
->Remove(BrowsingDataRemover::REMOVE_CACHE
,
821 BrowsingDataHelper::UNPROTECTED_WEB
);
822 // BrowsingDataRemover deletes itself.
825 void NetInternalsMessageHandler::OnGetPrerenderInfo(
826 const base::ListValue
* list
) {
827 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
829 base::DictionaryValue
* value
= NULL
;
830 prerender::PrerenderManager
* prerender_manager
= prerender_manager_
.get();
831 if (!prerender_manager
) {
832 value
= new base::DictionaryValue();
833 value
->SetBoolean("enabled", false);
834 value
->SetBoolean("omnibox_enabled", false);
836 value
= prerender_manager
->GetAsValue();
838 SendJavascriptCommand("receivedPrerenderInfo", value
);
841 void NetInternalsMessageHandler::OnGetHistoricNetworkStats(
842 const base::ListValue
* list
) {
843 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
844 base::Value
* historic_network_info
=
845 ChromeNetworkDelegate::HistoricNetworkStatsInfoToValue();
846 SendJavascriptCommand("receivedHistoricNetworkStats", historic_network_info
);
849 void NetInternalsMessageHandler::OnGetExtensionInfo(
850 const base::ListValue
* list
) {
851 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
852 base::ListValue
* extension_list
= new base::ListValue();
853 Profile
* profile
= Profile::FromWebUI(web_ui());
854 extensions::ExtensionSystem
* extension_system
=
855 extensions::ExtensionSystem::Get(profile
);
856 if (extension_system
) {
857 ExtensionService
* extension_service
= extension_system
->extension_service();
858 if (extension_service
) {
859 scoped_ptr
<const extensions::ExtensionSet
> extensions(
860 extension_service
->GenerateInstalledExtensionsSet());
861 for (extensions::ExtensionSet::const_iterator it
= extensions
->begin();
862 it
!= extensions
->end(); ++it
) {
863 base::DictionaryValue
* extension_info
= new base::DictionaryValue();
864 bool enabled
= extension_service
->IsExtensionEnabled((*it
)->id());
865 extensions::GetExtensionBasicInfo(it
->get(), enabled
, extension_info
);
866 extension_list
->Append(extension_info
);
870 SendJavascriptCommand("receivedExtensionInfo", extension_list
);
873 #if defined(OS_CHROMEOS)
874 ////////////////////////////////////////////////////////////////////////////////
876 // NetInternalsMessageHandler::SystemLogsGetter
878 ////////////////////////////////////////////////////////////////////////////////
880 NetInternalsMessageHandler::SystemLogsGetter::SystemLogsGetter(
881 NetInternalsMessageHandler
* handler
,
882 chromeos::system::SyslogsProvider
* syslogs_provider
)
884 syslogs_provider_(syslogs_provider
),
885 logs_received_(false),
886 logs_requested_(false) {
887 if (!syslogs_provider_
)
888 LOG(ERROR
) << "System access library not loaded";
891 NetInternalsMessageHandler::SystemLogsGetter::~SystemLogsGetter() {
895 void NetInternalsMessageHandler::SystemLogsGetter::DeleteSystemLogs() {
896 if (syslogs_provider_
&& logs_requested_
&& !logs_received_
) {
897 tracker_
.TryCancel(syslogs_task_id_
);
899 logs_requested_
= false;
900 logs_received_
= false;
904 void NetInternalsMessageHandler::SystemLogsGetter::RequestSystemLog(
905 const base::ListValue
* args
) {
906 if (!logs_requested_
) {
907 DCHECK(!logs_received_
);
910 SystemLogRequest log_request
;
911 args
->GetString(0, &log_request
.log_key
);
912 args
->GetString(1, &log_request
.cell_id
);
914 if (logs_received_
) {
915 SendLogs(log_request
);
917 requests_
.push_back(log_request
);
921 void NetInternalsMessageHandler::SystemLogsGetter::LoadSystemLogs() {
922 if (logs_requested_
|| !syslogs_provider_
)
924 logs_requested_
= true;
925 syslogs_task_id_
= syslogs_provider_
->RequestSyslogs(
926 false, // compress logs.
927 chromeos::system::SyslogsProvider::SYSLOGS_NETWORK
,
929 &NetInternalsMessageHandler::SystemLogsGetter::OnSystemLogsLoaded
,
930 base::Unretained(this)),
934 void NetInternalsMessageHandler::SystemLogsGetter::OnSystemLogsLoaded(
935 chromeos::system::LogDictionaryType
* sys_info
,
936 std::string
* ignored_content
) {
937 DCHECK(!ignored_content
);
938 logs_
.reset(sys_info
);
939 logs_received_
= true;
940 for (std::list
<SystemLogRequest
>::iterator request_it
= requests_
.begin();
941 request_it
!= requests_
.end();
943 SendLogs(*request_it
);
948 void NetInternalsMessageHandler::SystemLogsGetter::SendLogs(
949 const SystemLogRequest
& request
) {
950 base::DictionaryValue
* result
= new base::DictionaryValue();
951 chromeos::system::LogDictionaryType::iterator log_it
=
952 logs_
->find(request
.log_key
);
953 if (log_it
!= logs_
->end()) {
954 if (!log_it
->second
.empty()) {
955 result
->SetString("log", log_it
->second
);
957 result
->SetString("log", "<no relevant lines found>");
960 result
->SetString("log", "<invalid log name>");
962 result
->SetString("cellId", request
.cell_id
);
964 handler_
->SendJavascriptCommand("getSystemLogCallback", result
);
966 #endif // defined(OS_CHROMEOS)
968 ////////////////////////////////////////////////////////////////////////////////
970 // NetInternalsMessageHandler::IOThreadImpl
972 ////////////////////////////////////////////////////////////////////////////////
974 NetInternalsMessageHandler::IOThreadImpl::IOThreadImpl(
975 const base::WeakPtr
<NetInternalsMessageHandler
>& handler
,
977 net::URLRequestContextGetter
* main_context_getter
)
979 io_thread_(io_thread
),
980 main_context_getter_(main_context_getter
),
981 was_webui_deleted_(false) {
982 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
983 AddRequestContextGetter(main_context_getter
);
986 NetInternalsMessageHandler::IOThreadImpl::~IOThreadImpl() {
987 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
990 void NetInternalsMessageHandler::IOThreadImpl::AddRequestContextGetter(
991 net::URLRequestContextGetter
* context_getter
) {
992 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
993 context_getters_
.push_back(context_getter
);
996 void NetInternalsMessageHandler::IOThreadImpl::CallbackHelper(
997 MessageHandler method
,
998 scoped_refptr
<IOThreadImpl
> io_thread
,
999 const base::ListValue
* list
) {
1000 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1002 // We need to make a copy of the value in order to pass it over to the IO
1003 // thread. |list_copy| will be deleted when the task is destroyed. The called
1004 // |method| cannot take ownership of |list_copy|.
1005 base::ListValue
* list_copy
=
1006 (list
&& list
->GetSize()) ? list
->DeepCopy() : NULL
;
1008 BrowserThread::PostTask(
1009 BrowserThread::IO
, FROM_HERE
,
1010 base::Bind(method
, io_thread
, base::Owned(list_copy
)));
1013 void NetInternalsMessageHandler::IOThreadImpl::Detach() {
1014 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
1015 // Unregister with network stack to observe events.
1017 net_log()->RemoveThreadSafeObserver(this);
1019 // Cancel any in-progress connection tests.
1020 connection_tester_
.reset();
1023 void NetInternalsMessageHandler::IOThreadImpl::OnWebUIDeleted() {
1024 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1025 was_webui_deleted_
= true;
1028 void NetInternalsMessageHandler::IOThreadImpl::OnRendererReady(
1029 const base::ListValue
* list
) {
1030 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
1032 // If we have any pending entries, go ahead and get rid of them, so they won't
1033 // appear before the REQUEST_ALIVE events we add for currently active
1035 PostPendingEntries();
1037 SendJavascriptCommand("receivedConstants", NetInternalsUI::GetConstants());
1039 // Add entries for ongoing URL requests.
1040 PrePopulateEventList();
1043 // Register with network stack to observe events.
1044 io_thread_
->net_log()->AddThreadSafeObserver(this,
1045 net::NetLog::LOG_ALL_BUT_BYTES
);
1049 void NetInternalsMessageHandler::IOThreadImpl::OnGetProxySettings(
1050 const base::ListValue
* list
) {
1052 net::ProxyService
* proxy_service
= GetMainContext()->proxy_service();
1054 base::DictionaryValue
* dict
= new base::DictionaryValue();
1055 if (proxy_service
->fetched_config().is_valid())
1056 dict
->Set("original", proxy_service
->fetched_config().ToValue());
1057 if (proxy_service
->config().is_valid())
1058 dict
->Set("effective", proxy_service
->config().ToValue());
1060 SendJavascriptCommand("receivedProxySettings", dict
);
1063 void NetInternalsMessageHandler::IOThreadImpl::OnReloadProxySettings(
1064 const base::ListValue
* list
) {
1066 GetMainContext()->proxy_service()->ForceReloadProxyConfig();
1068 // Cause the renderer to be notified of the new values.
1069 OnGetProxySettings(NULL
);
1072 void NetInternalsMessageHandler::IOThreadImpl::OnGetBadProxies(
1073 const base::ListValue
* list
) {
1076 const net::ProxyRetryInfoMap
& bad_proxies_map
=
1077 GetMainContext()->proxy_service()->proxy_retry_info();
1079 base::ListValue
* dict_list
= new base::ListValue();
1081 for (net::ProxyRetryInfoMap::const_iterator it
= bad_proxies_map
.begin();
1082 it
!= bad_proxies_map
.end(); ++it
) {
1083 const std::string
& proxy_uri
= it
->first
;
1084 const net::ProxyRetryInfo
& retry_info
= it
->second
;
1086 base::DictionaryValue
* dict
= new base::DictionaryValue();
1087 dict
->SetString("proxy_uri", proxy_uri
);
1088 dict
->SetString("bad_until",
1089 net::NetLog::TickCountToString(retry_info
.bad_until
));
1091 dict_list
->Append(dict
);
1094 SendJavascriptCommand("receivedBadProxies", dict_list
);
1097 void NetInternalsMessageHandler::IOThreadImpl::OnClearBadProxies(
1098 const base::ListValue
* list
) {
1100 GetMainContext()->proxy_service()->ClearBadProxiesCache();
1102 // Cause the renderer to be notified of the new values.
1103 OnGetBadProxies(NULL
);
1106 void NetInternalsMessageHandler::IOThreadImpl::OnGetHostResolverInfo(
1107 const base::ListValue
* list
) {
1109 net::URLRequestContext
* context
= GetMainContext();
1110 net::HostCache
* cache
= GetHostResolverCache(context
);
1113 SendJavascriptCommand("receivedHostResolverInfo", NULL
);
1117 base::DictionaryValue
* dict
= new base::DictionaryValue();
1119 base::Value
* dns_config
= context
->host_resolver()->GetDnsConfigAsValue();
1121 dict
->Set("dns_config", dns_config
);
1124 "default_address_family",
1125 static_cast<int>(context
->host_resolver()->GetDefaultAddressFamily()));
1127 base::DictionaryValue
* cache_info_dict
= new base::DictionaryValue();
1129 cache_info_dict
->SetInteger(
1131 static_cast<int>(cache
->max_entries()));
1133 base::ListValue
* entry_list
= new base::ListValue();
1135 net::HostCache::EntryMap::Iterator
it(cache
->entries());
1136 for (; it
.HasNext(); it
.Advance()) {
1137 const net::HostCache::Key
& key
= it
.key();
1138 const net::HostCache::Entry
& entry
= it
.value();
1140 base::DictionaryValue
* entry_dict
= new base::DictionaryValue();
1142 entry_dict
->SetString("hostname", key
.hostname
);
1143 entry_dict
->SetInteger("address_family",
1144 static_cast<int>(key
.address_family
));
1145 entry_dict
->SetString("expiration",
1146 net::NetLog::TickCountToString(it
.expiration()));
1148 if (entry
.error
!= net::OK
) {
1149 entry_dict
->SetInteger("error", entry
.error
);
1151 // Append all of the resolved addresses.
1152 base::ListValue
* address_list
= new base::ListValue();
1153 for (size_t i
= 0; i
< entry
.addrlist
.size(); ++i
) {
1154 address_list
->AppendString(entry
.addrlist
[i
].ToStringWithoutPort());
1156 entry_dict
->Set("addresses", address_list
);
1159 entry_list
->Append(entry_dict
);
1162 cache_info_dict
->Set("entries", entry_list
);
1163 dict
->Set("cache", cache_info_dict
);
1165 SendJavascriptCommand("receivedHostResolverInfo", dict
);
1168 void NetInternalsMessageHandler::IOThreadImpl::OnClearHostResolverCache(
1169 const base::ListValue
* list
) {
1171 net::HostCache
* cache
= GetHostResolverCache(GetMainContext());
1176 // Cause the renderer to be notified of the new values.
1177 OnGetHostResolverInfo(NULL
);
1180 void NetInternalsMessageHandler::IOThreadImpl::OnEnableIPv6(
1181 const base::ListValue
* list
) {
1183 net::HostResolver
* host_resolver
= GetMainContext()->host_resolver();
1185 host_resolver
->SetDefaultAddressFamily(net::ADDRESS_FAMILY_UNSPECIFIED
);
1187 // Cause the renderer to be notified of the new value.
1188 OnGetHostResolverInfo(NULL
);
1191 void NetInternalsMessageHandler::IOThreadImpl::OnStartConnectionTests(
1192 const base::ListValue
* list
) {
1193 // |value| should be: [<URL to test>].
1194 base::string16 url_str
;
1195 CHECK(list
->GetString(0, &url_str
));
1197 // Try to fix-up the user provided URL into something valid.
1198 // For example, turn "www.google.com" into "http://www.google.com".
1199 GURL
url(URLFixerUpper::FixupURL(base::UTF16ToUTF8(url_str
), std::string()));
1201 connection_tester_
.reset(new ConnectionTester(
1203 io_thread_
->globals()->proxy_script_fetcher_context
.get(),
1205 connection_tester_
->RunAllTests(url
);
1208 void NetInternalsMessageHandler::IOThreadImpl::OnHSTSQuery(
1209 const base::ListValue
* list
) {
1210 // |list| should be: [<domain to query>].
1212 CHECK(list
->GetString(0, &domain
));
1213 base::DictionaryValue
* result
= new base::DictionaryValue();
1215 if (!IsStringASCII(domain
)) {
1216 result
->SetString("error", "non-ASCII domain name");
1218 net::TransportSecurityState
* transport_security_state
=
1219 GetMainContext()->transport_security_state();
1220 if (!transport_security_state
) {
1221 result
->SetString("error", "no TransportSecurityState active");
1223 net::TransportSecurityState::DomainState state
;
1224 const bool found
= transport_security_state
->GetDomainState(
1225 domain
, true, &state
);
1227 result
->SetBoolean("result", found
);
1229 result
->SetInteger("mode", static_cast<int>(state
.upgrade_mode
));
1230 result
->SetBoolean("sts_subdomains", state
.sts_include_subdomains
);
1231 result
->SetBoolean("pkp_subdomains", state
.pkp_include_subdomains
);
1232 result
->SetDouble("sts_observed", state
.sts_observed
.ToDoubleT());
1233 result
->SetDouble("pkp_observed", state
.pkp_observed
.ToDoubleT());
1234 result
->SetString("domain", state
.domain
);
1235 result
->SetDouble("expiry", state
.upgrade_expiry
.ToDoubleT());
1236 result
->SetDouble("dynamic_spki_hashes_expiry",
1237 state
.dynamic_spki_hashes_expiry
.ToDoubleT());
1239 result
->SetString("static_spki_hashes",
1240 HashesToBase64String(state
.static_spki_hashes
));
1241 result
->SetString("dynamic_spki_hashes",
1242 HashesToBase64String(state
.dynamic_spki_hashes
));
1247 SendJavascriptCommand("receivedHSTSResult", result
);
1250 void NetInternalsMessageHandler::IOThreadImpl::OnHSTSAdd(
1251 const base::ListValue
* list
) {
1252 // |list| should be: [<domain to query>, <STS include subdomains>, <PKP
1253 // include subdomains>, <key pins>].
1255 CHECK(list
->GetString(0, &domain
));
1256 if (!IsStringASCII(domain
)) {
1257 // Silently fail. The user will get a helpful error if they query for the
1261 bool sts_include_subdomains
;
1262 CHECK(list
->GetBoolean(1, &sts_include_subdomains
));
1263 bool pkp_include_subdomains
;
1264 CHECK(list
->GetBoolean(2, &pkp_include_subdomains
));
1265 std::string hashes_str
;
1266 CHECK(list
->GetString(3, &hashes_str
));
1268 net::TransportSecurityState
* transport_security_state
=
1269 GetMainContext()->transport_security_state();
1270 if (!transport_security_state
)
1273 base::Time expiry
= base::Time::Now() + base::TimeDelta::FromDays(1000);
1274 net::HashValueVector hashes
;
1275 if (!hashes_str
.empty()) {
1276 if (!Base64StringToHashes(hashes_str
, &hashes
))
1280 transport_security_state
->AddHSTS(domain
, expiry
, sts_include_subdomains
);
1281 transport_security_state
->AddHPKP(domain
, expiry
, pkp_include_subdomains
,
1285 void NetInternalsMessageHandler::IOThreadImpl::OnHSTSDelete(
1286 const base::ListValue
* list
) {
1287 // |list| should be: [<domain to query>].
1289 CHECK(list
->GetString(0, &domain
));
1290 if (!IsStringASCII(domain
)) {
1291 // There cannot be a unicode entry in the HSTS set.
1294 net::TransportSecurityState
* transport_security_state
=
1295 GetMainContext()->transport_security_state();
1296 if (!transport_security_state
)
1299 transport_security_state
->DeleteDynamicDataForHost(domain
);
1302 void NetInternalsMessageHandler::IOThreadImpl::OnGetHttpCacheInfo(
1303 const base::ListValue
* list
) {
1305 base::DictionaryValue
* info_dict
= new base::DictionaryValue();
1306 base::DictionaryValue
* stats_dict
= new base::DictionaryValue();
1308 disk_cache::Backend
* disk_cache
= GetDiskCacheBackend(GetMainContext());
1311 // Extract the statistics key/value pairs from the backend.
1312 std::vector
<std::pair
<std::string
, std::string
> > stats
;
1313 disk_cache
->GetStats(&stats
);
1314 for (size_t i
= 0; i
< stats
.size(); ++i
) {
1315 stats_dict
->SetStringWithoutPathExpansion(
1316 stats
[i
].first
, stats
[i
].second
);
1320 info_dict
->Set("stats", stats_dict
);
1322 SendJavascriptCommand("receivedHttpCacheInfo", info_dict
);
1325 void NetInternalsMessageHandler::IOThreadImpl::OnGetSocketPoolInfo(
1326 const base::ListValue
* list
) {
1328 net::HttpNetworkSession
* http_network_session
=
1329 GetHttpNetworkSession(GetMainContext());
1331 base::Value
* socket_pool_info
= NULL
;
1332 if (http_network_session
)
1333 socket_pool_info
= http_network_session
->SocketPoolInfoToValue();
1335 SendJavascriptCommand("receivedSocketPoolInfo", socket_pool_info
);
1338 void NetInternalsMessageHandler::IOThreadImpl::OnGetSessionNetworkStats(
1339 const base::ListValue
* list
) {
1341 net::HttpNetworkSession
* http_network_session
=
1342 GetHttpNetworkSession(main_context_getter_
->GetURLRequestContext());
1344 base::Value
* network_info
= NULL
;
1345 if (http_network_session
) {
1346 ChromeNetworkDelegate
* net_delegate
=
1347 static_cast<ChromeNetworkDelegate
*>(
1348 http_network_session
->network_delegate());
1350 network_info
= net_delegate
->SessionNetworkStatsInfoToValue();
1353 SendJavascriptCommand("receivedSessionNetworkStats", network_info
);
1356 void NetInternalsMessageHandler::IOThreadImpl::OnFlushSocketPools(
1357 const base::ListValue
* list
) {
1359 net::HttpNetworkSession
* http_network_session
=
1360 GetHttpNetworkSession(GetMainContext());
1362 if (http_network_session
)
1363 http_network_session
->CloseAllConnections();
1366 void NetInternalsMessageHandler::IOThreadImpl::OnCloseIdleSockets(
1367 const base::ListValue
* list
) {
1369 net::HttpNetworkSession
* http_network_session
=
1370 GetHttpNetworkSession(GetMainContext());
1372 if (http_network_session
)
1373 http_network_session
->CloseIdleConnections();
1376 void NetInternalsMessageHandler::IOThreadImpl::OnGetSpdySessionInfo(
1377 const base::ListValue
* list
) {
1379 net::HttpNetworkSession
* http_network_session
=
1380 GetHttpNetworkSession(GetMainContext());
1382 base::Value
* spdy_info
= http_network_session
?
1383 http_network_session
->SpdySessionPoolInfoToValue() : NULL
;
1384 SendJavascriptCommand("receivedSpdySessionInfo", spdy_info
);
1387 void NetInternalsMessageHandler::IOThreadImpl::OnGetSpdyStatus(
1388 const base::ListValue
* list
) {
1390 base::DictionaryValue
* status_dict
= new base::DictionaryValue();
1392 status_dict
->Set("spdy_enabled",
1393 base::Value::CreateBooleanValue(
1394 net::HttpStreamFactory::spdy_enabled()));
1395 status_dict
->Set("use_alternate_protocols",
1396 base::Value::CreateBooleanValue(
1397 net::HttpStreamFactory::use_alternate_protocols()));
1398 status_dict
->Set("force_spdy_over_ssl",
1399 base::Value::CreateBooleanValue(
1400 net::HttpStreamFactory::force_spdy_over_ssl()));
1401 status_dict
->Set("force_spdy_always",
1402 base::Value::CreateBooleanValue(
1403 net::HttpStreamFactory::force_spdy_always()));
1405 // The next_protos may not be specified for certain configurations of SPDY.
1406 std::string next_protos_string
;
1407 if (net::HttpStreamFactory::has_next_protos()) {
1408 next_protos_string
= JoinString(net::HttpStreamFactory::next_protos(), ',');
1410 status_dict
->SetString("next_protos", next_protos_string
);
1412 SendJavascriptCommand("receivedSpdyStatus", status_dict
);
1416 NetInternalsMessageHandler::IOThreadImpl::OnGetSpdyAlternateProtocolMappings(
1417 const base::ListValue
* list
) {
1419 base::ListValue
* dict_list
= new base::ListValue();
1421 const net::HttpServerProperties
& http_server_properties
=
1422 *GetMainContext()->http_server_properties();
1424 const net::AlternateProtocolMap
& map
=
1425 http_server_properties
.alternate_protocol_map();
1427 for (net::AlternateProtocolMap::const_iterator it
= map
.begin();
1428 it
!= map
.end(); ++it
) {
1429 base::DictionaryValue
* dict
= new base::DictionaryValue();
1430 dict
->SetString("host_port_pair", it
->first
.ToString());
1431 dict
->SetString("alternate_protocol", it
->second
.ToString());
1432 dict_list
->Append(dict
);
1435 SendJavascriptCommand("receivedSpdyAlternateProtocolMappings", dict_list
);
1438 void NetInternalsMessageHandler::IOThreadImpl::OnGetQuicInfo(
1439 const base::ListValue
* list
) {
1441 net::HttpNetworkSession
* http_network_session
=
1442 GetHttpNetworkSession(GetMainContext());
1444 base::Value
* quic_info
= http_network_session
?
1445 http_network_session
->QuicInfoToValue() : NULL
;
1446 SendJavascriptCommand("receivedQuicInfo", quic_info
);
1450 void NetInternalsMessageHandler::IOThreadImpl::OnGetServiceProviders(
1451 const base::ListValue
* list
) {
1454 base::DictionaryValue
* service_providers
= new base::DictionaryValue();
1456 WinsockLayeredServiceProviderList layered_providers
;
1457 GetWinsockLayeredServiceProviders(&layered_providers
);
1458 base::ListValue
* layered_provider_list
= new base::ListValue();
1459 for (size_t i
= 0; i
< layered_providers
.size(); ++i
) {
1460 base::DictionaryValue
* service_dict
= new base::DictionaryValue();
1461 service_dict
->SetString("name", layered_providers
[i
].name
);
1462 service_dict
->SetInteger("version", layered_providers
[i
].version
);
1463 service_dict
->SetInteger("chain_length", layered_providers
[i
].chain_length
);
1464 service_dict
->SetInteger("socket_type", layered_providers
[i
].socket_type
);
1465 service_dict
->SetInteger("socket_protocol",
1466 layered_providers
[i
].socket_protocol
);
1467 service_dict
->SetString("path", layered_providers
[i
].path
);
1469 layered_provider_list
->Append(service_dict
);
1471 service_providers
->Set("service_providers", layered_provider_list
);
1473 WinsockNamespaceProviderList namespace_providers
;
1474 GetWinsockNamespaceProviders(&namespace_providers
);
1475 base::ListValue
* namespace_list
= new base::ListValue
;
1476 for (size_t i
= 0; i
< namespace_providers
.size(); ++i
) {
1477 base::DictionaryValue
* namespace_dict
= new base::DictionaryValue();
1478 namespace_dict
->SetString("name", namespace_providers
[i
].name
);
1479 namespace_dict
->SetBoolean("active", namespace_providers
[i
].active
);
1480 namespace_dict
->SetInteger("version", namespace_providers
[i
].version
);
1481 namespace_dict
->SetInteger("type", namespace_providers
[i
].type
);
1483 namespace_list
->Append(namespace_dict
);
1485 service_providers
->Set("namespace_providers", namespace_list
);
1487 SendJavascriptCommand("receivedServiceProviders", service_providers
);
1491 #if defined(OS_CHROMEOS)
1492 void NetInternalsMessageHandler::OnRefreshSystemLogs(
1493 const base::ListValue
* list
) {
1495 DCHECK(syslogs_getter_
.get());
1496 syslogs_getter_
->DeleteSystemLogs();
1497 syslogs_getter_
->LoadSystemLogs();
1500 void NetInternalsMessageHandler::OnGetSystemLog(
1501 const base::ListValue
* list
) {
1502 DCHECK(syslogs_getter_
.get());
1503 syslogs_getter_
->RequestSystemLog(list
);
1506 void NetInternalsMessageHandler::OnImportONCFile(
1507 const base::ListValue
* list
) {
1508 std::string onc_blob
;
1509 std::string passcode
;
1510 if (list
->GetSize() != 2 ||
1511 !list
->GetString(0, &onc_blob
) ||
1512 !list
->GetString(1, &passcode
)) {
1517 const chromeos::User
* user
= chromeos::UserManager::Get()->GetActiveUser();
1519 onc::ONCSource onc_source
= onc::ONC_SOURCE_USER_IMPORT
;
1521 base::ListValue network_configs
;
1522 base::DictionaryValue global_network_config
;
1523 base::ListValue certificates
;
1524 if (!chromeos::onc::ParseAndValidateOncForImport(onc_blob
,
1528 &global_network_config
,
1530 error
= "Errors occurred during the ONC parsing. ";
1533 chromeos::onc::CertificateImporterImpl cert_importer
;
1534 if (!cert_importer
.ImportCertificates(certificates
, onc_source
, NULL
))
1535 error
+= "Some certificates couldn't be imported. ";
1537 std::string network_error
;
1538 chromeos::onc::ImportNetworksForUser(user
, network_configs
, &network_error
);
1539 if (!network_error
.empty())
1540 error
+= network_error
;
1542 error
= "No active user.";
1545 LOG_IF(ERROR
, !error
.empty()) << error
;
1546 SendJavascriptCommand("receivedONCFileParse", new base::StringValue(error
));
1549 void NetInternalsMessageHandler::OnStoreDebugLogs(const base::ListValue
* list
) {
1552 SendJavascriptCommand("receivedStoreDebugLogs",
1553 new base::StringValue("Creating log file..."));
1554 const DownloadPrefs
* const prefs
=
1555 DownloadPrefs::FromBrowserContext(Profile::FromWebUI(web_ui()));
1556 StoreDebugLogs(prefs
->DownloadPath(),
1557 base::Bind(&NetInternalsMessageHandler::OnStoreDebugLogsCompleted
,
1561 void NetInternalsMessageHandler::OnStoreDebugLogsCompleted(
1562 const base::FilePath
& log_path
, bool succeeded
) {
1565 status
= "Created log file: " + log_path
.BaseName().AsUTF8Unsafe();
1567 status
= "Failed to create log file";
1568 SendJavascriptCommand("receivedStoreDebugLogs",
1569 new base::StringValue(status
));
1572 void NetInternalsMessageHandler::OnSetNetworkDebugMode(
1573 const base::ListValue
* list
) {
1574 std::string subsystem
;
1575 if (list
->GetSize() != 1 || !list
->GetString(0, &subsystem
))
1577 chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->
1581 &NetInternalsMessageHandler::OnSetNetworkDebugModeCompleted
,
1586 void NetInternalsMessageHandler::OnSetNetworkDebugModeCompleted(
1587 const std::string
& subsystem
,
1591 status
= "Debug mode is changed to " + subsystem
;
1593 status
= "Failed to change debug mode to " + subsystem
;
1594 SendJavascriptCommand("receivedSetNetworkDebugMode",
1595 new base::StringValue(status
));
1597 #endif // defined(OS_CHROMEOS)
1599 void NetInternalsMessageHandler::IOThreadImpl::OnGetHttpPipeliningStatus(
1600 const base::ListValue
* list
) {
1602 base::DictionaryValue
* status_dict
= new base::DictionaryValue();
1604 base::Value
* pipelined_connection_info
= NULL
;
1605 net::HttpNetworkSession
* http_network_session
=
1606 GetHttpNetworkSession(GetMainContext());
1607 if (http_network_session
) {
1608 status_dict
->Set("pipelining_enabled", base::Value::CreateBooleanValue(
1609 http_network_session
->params().http_pipelining_enabled
));
1611 pipelined_connection_info
=
1612 http_network_session
->http_stream_factory()->PipelineInfoToValue();
1614 status_dict
->Set("pipelined_connection_info", pipelined_connection_info
);
1616 const net::HttpServerProperties
& http_server_properties
=
1617 *GetMainContext()->http_server_properties();
1619 // TODO(simonjam): This call is slow.
1620 const net::PipelineCapabilityMap pipeline_capability_map
=
1621 http_server_properties
.GetPipelineCapabilityMap();
1623 base::ListValue
* known_hosts_list
= new base::ListValue();
1624 net::PipelineCapabilityMap::const_iterator it
;
1625 for (it
= pipeline_capability_map
.begin();
1626 it
!= pipeline_capability_map
.end(); ++it
) {
1627 base::DictionaryValue
* host_dict
= new base::DictionaryValue();
1628 host_dict
->SetString("host", it
->first
.ToString());
1629 std::string capability
;
1630 switch (it
->second
) {
1631 case net::PIPELINE_CAPABLE
:
1632 capability
= "capable";
1635 case net::PIPELINE_PROBABLY_CAPABLE
:
1636 capability
= "probably capable";
1639 case net::PIPELINE_INCAPABLE
:
1640 capability
= "incapable";
1643 case net::PIPELINE_UNKNOWN
:
1645 capability
= "unknown";
1648 host_dict
->SetString("capability", capability
);
1649 known_hosts_list
->Append(host_dict
);
1651 status_dict
->Set("pipelined_host_info", known_hosts_list
);
1653 SendJavascriptCommand("receivedHttpPipeliningStatus", status_dict
);
1656 void NetInternalsMessageHandler::IOThreadImpl::OnSetLogLevel(
1657 const base::ListValue
* list
) {
1659 std::string log_level_string
;
1660 if (!list
->GetString(0, &log_level_string
) ||
1661 !base::StringToInt(log_level_string
, &log_level
)) {
1666 DCHECK_GE(log_level
, net::NetLog::LOG_ALL
);
1667 DCHECK_LE(log_level
, net::NetLog::LOG_BASIC
);
1668 net_log()->SetObserverLogLevel(
1669 this, static_cast<net::NetLog::LogLevel
>(log_level
));
1672 // Note that unlike other methods of IOThreadImpl, this function
1673 // can be called from ANY THREAD.
1674 void NetInternalsMessageHandler::IOThreadImpl::OnAddEntry(
1675 const net::NetLog::Entry
& entry
) {
1676 BrowserThread::PostTask(
1677 BrowserThread::IO
, FROM_HERE
,
1678 base::Bind(&IOThreadImpl::AddEntryToQueue
, this, entry
.ToValue()));
1681 void NetInternalsMessageHandler::IOThreadImpl::OnStartConnectionTestSuite() {
1682 SendJavascriptCommand("receivedStartConnectionTestSuite", NULL
);
1685 void NetInternalsMessageHandler::IOThreadImpl::OnStartConnectionTestExperiment(
1686 const ConnectionTester::Experiment
& experiment
) {
1687 SendJavascriptCommand(
1688 "receivedStartConnectionTestExperiment",
1689 ExperimentToValue(experiment
));
1693 NetInternalsMessageHandler::IOThreadImpl::OnCompletedConnectionTestExperiment(
1694 const ConnectionTester::Experiment
& experiment
,
1696 base::DictionaryValue
* dict
= new base::DictionaryValue();
1698 dict
->Set("experiment", ExperimentToValue(experiment
));
1699 dict
->SetInteger("result", result
);
1701 SendJavascriptCommand(
1702 "receivedCompletedConnectionTestExperiment",
1707 NetInternalsMessageHandler::IOThreadImpl::OnCompletedConnectionTestSuite() {
1708 SendJavascriptCommand(
1709 "receivedCompletedConnectionTestSuite",
1713 // Note that this can be called from ANY THREAD.
1714 void NetInternalsMessageHandler::IOThreadImpl::SendJavascriptCommand(
1715 const std::string
& command
,
1717 if (BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
1718 if (handler_
.get() && !was_webui_deleted_
) {
1719 // We check |handler_| in case it was deleted on the UI thread earlier
1720 // while we were running on the IO thread.
1721 handler_
->SendJavascriptCommand(command
, arg
);
1728 if (!BrowserThread::PostTask(
1729 BrowserThread::UI
, FROM_HERE
,
1730 base::Bind(&IOThreadImpl::SendJavascriptCommand
, this, command
, arg
))) {
1731 // Failed posting the task, avoid leaking.
1736 void NetInternalsMessageHandler::IOThreadImpl::AddEntryToQueue(
1737 base::Value
* entry
) {
1738 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
1739 if (!pending_entries_
.get()) {
1740 pending_entries_
.reset(new base::ListValue());
1741 BrowserThread::PostDelayedTask(
1742 BrowserThread::IO
, FROM_HERE
,
1743 base::Bind(&IOThreadImpl::PostPendingEntries
, this),
1744 base::TimeDelta::FromMilliseconds(kNetLogEventDelayMilliseconds
));
1746 pending_entries_
->Append(entry
);
1749 void NetInternalsMessageHandler::IOThreadImpl::PostPendingEntries() {
1750 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
1751 if (pending_entries_
.get())
1752 SendJavascriptCommand("receivedLogEntries", pending_entries_
.release());
1755 void NetInternalsMessageHandler::IOThreadImpl::PrePopulateEventList() {
1756 // Use a set to prevent duplicates.
1757 std::set
<net::URLRequestContext
*> contexts
;
1758 for (ContextGetterList::const_iterator getter
= context_getters_
.begin();
1759 getter
!= context_getters_
.end(); ++getter
) {
1760 contexts
.insert((*getter
)->GetURLRequestContext());
1762 contexts
.insert(io_thread_
->globals()->proxy_script_fetcher_context
.get());
1763 contexts
.insert(io_thread_
->globals()->system_request_context
.get());
1765 // Put together the list of all requests.
1766 std::vector
<const net::URLRequest
*> requests
;
1767 for (std::set
<net::URLRequestContext
*>::const_iterator context
=
1769 context
!= contexts
.end(); ++context
) {
1770 std::set
<const net::URLRequest
*>* context_requests
=
1771 (*context
)->url_requests();
1772 for (std::set
<const net::URLRequest
*>::const_iterator request_it
=
1773 context_requests
->begin();
1774 request_it
!= context_requests
->end(); ++request_it
) {
1775 DCHECK_EQ(io_thread_
->net_log(), (*request_it
)->net_log().net_log());
1776 requests
.push_back(*request_it
);
1780 // Sort by creation time.
1781 std::sort(requests
.begin(), requests
.end(), RequestCreatedBefore
);
1783 // Create fake events.
1784 for (std::vector
<const net::URLRequest
*>::const_iterator request_it
=
1786 request_it
!= requests
.end(); ++request_it
) {
1787 const net::URLRequest
* request
= *request_it
;
1788 net::NetLog::ParametersCallback callback
=
1789 base::Bind(&GetRequestStateAsValue
, base::Unretained(request
));
1791 // Create and add the entry directly, to avoid sending it to any other
1792 // NetLog observers.
1793 net::NetLog::Entry
entry(net::NetLog::TYPE_REQUEST_ALIVE
,
1794 request
->net_log().source(),
1795 net::NetLog::PHASE_BEGIN
,
1796 request
->creation_time(),
1798 request
->net_log().GetLogLevel());
1800 // Have to add |entry| to the queue synchronously, as there may already
1801 // be posted tasks queued up to add other events for |request|, which we
1802 // want |entry| to precede.
1803 AddEntryToQueue(entry
.ToValue());
1810 ////////////////////////////////////////////////////////////////////////////////
1814 ////////////////////////////////////////////////////////////////////////////////
1817 base::Value
* NetInternalsUI::GetConstants() {
1818 base::DictionaryValue
* constants_dict
= net::NetLogLogger::GetConstants();
1819 DCHECK(constants_dict
);
1821 // Add a dictionary with the version of the client and its command line
1824 base::DictionaryValue
* dict
= new base::DictionaryValue();
1826 chrome::VersionInfo version_info
;
1828 if (!version_info
.is_valid()) {
1829 DLOG(ERROR
) << "Unable to create chrome::VersionInfo";
1831 // We have everything we need to send the right values.
1832 dict
->SetString("name", version_info
.Name());
1833 dict
->SetString("version", version_info
.Version());
1834 dict
->SetString("cl", version_info
.LastChange());
1835 dict
->SetString("version_mod",
1836 chrome::VersionInfo::GetVersionStringModifier());
1837 dict
->SetString("official",
1838 version_info
.IsOfficialBuild() ? "official" :
1840 dict
->SetString("os_type", version_info
.OSType());
1841 dict
->SetString("command_line",
1842 CommandLine::ForCurrentProcess()->GetCommandLineString());
1845 constants_dict
->Set("clientInfo", dict
);
1848 return constants_dict
;
1851 NetInternalsUI::NetInternalsUI(content::WebUI
* web_ui
)
1852 : WebUIController(web_ui
) {
1853 web_ui
->AddMessageHandler(new NetInternalsMessageHandler());
1855 // Set up the chrome://net-internals/ source.
1856 Profile
* profile
= Profile::FromWebUI(web_ui
);
1857 content::WebUIDataSource::Add(profile
, CreateNetInternalsHTMLSource());