| // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/dom_ui/net_internals_ui.h" |
| |
| #include <algorithm> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "app/l10n_util.h" |
| #include "app/resource_bundle.h" |
| #include "base/command_line.h" |
| #include "base/file_util.h" |
| #include "base/message_loop.h" |
| #include "base/path_service.h" |
| #include "base/singleton.h" |
| #include "base/string_number_conversions.h" |
| #include "base/string_piece.h" |
| #include "base/utf_string_conversions.h" |
| #include "base/values.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/chrome_thread.h" |
| #include "chrome/browser/dom_ui/chrome_url_data_manager.h" |
| #include "chrome/browser/io_thread.h" |
| #include "chrome/browser/net/chrome_net_log.h" |
| #include "chrome/browser/net/connection_tester.h" |
| #include "chrome/browser/net/passive_log_collector.h" |
| #include "chrome/browser/net/url_fixer_upper.h" |
| #include "chrome/browser/platform_util.h" |
| #include "chrome/browser/profile.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "chrome/common/chrome_version_info.h" |
| #include "chrome/common/net/url_request_context_getter.h" |
| #include "chrome/common/url_constants.h" |
| #include "grit/generated_resources.h" |
| #include "grit/net_internals_resources.h" |
| #include "net/base/escape.h" |
| #include "net/base/host_resolver_impl.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/net_util.h" |
| #include "net/base/sys_addrinfo.h" |
| #include "net/disk_cache/disk_cache.h" |
| #include "net/http/http_cache.h" |
| #include "net/proxy/proxy_service.h" |
| #include "net/url_request/url_request_context.h" |
| |
| namespace { |
| |
| // Formats |t| as a decimal number, in milliseconds. |
| std::string TickCountToString(const base::TimeTicks& t) { |
| return base::Int64ToString((t - base::TimeTicks()).InMilliseconds()); |
| } |
| |
| // Returns the HostCache for |context|'s primary HostResolver, or NULL if |
| // there is none. |
| net::HostCache* GetHostResolverCache(URLRequestContext* context) { |
| net::HostResolverImpl* host_resolver_impl = |
| context->host_resolver()->GetAsHostResolverImpl(); |
| |
| if (!host_resolver_impl) |
| return NULL; |
| |
| return host_resolver_impl->cache(); |
| } |
| |
| // Returns the disk cache backend for |context| if there is one, or NULL. |
| disk_cache::Backend* GetDiskCacheBackend(URLRequestContext* context) { |
| if (!context->http_transaction_factory()) |
| return NULL; |
| |
| net::HttpCache* http_cache = context->http_transaction_factory()->GetCache(); |
| if (!http_cache) |
| return NULL; |
| |
| return http_cache->GetCurrentBackend(); |
| } |
| |
| Value* ExperimentToValue(const ConnectionTester::Experiment& experiment) { |
| DictionaryValue* dict = new DictionaryValue(); |
| |
| if (experiment.url.is_valid()) |
| dict->SetString("url", experiment.url.spec()); |
| |
| dict->SetString("proxy_settings_experiment", |
| ConnectionTester::ProxySettingsExperimentDescription( |
| experiment.proxy_settings_experiment)); |
| dict->SetString("host_resolver_experiment", |
| ConnectionTester::HostResolverExperimentDescription( |
| experiment.host_resolver_experiment)); |
| return dict; |
| } |
| |
| class NetInternalsHTMLSource : public ChromeURLDataManager::DataSource { |
| public: |
| NetInternalsHTMLSource(); |
| |
| // Called when the network layer has requested a resource underneath |
| // the path we registered. |
| virtual void StartDataRequest(const std::string& path, |
| bool is_off_the_record, |
| int request_id); |
| virtual std::string GetMimeType(const std::string&) const; |
| |
| private: |
| ~NetInternalsHTMLSource() {} |
| DISALLOW_COPY_AND_ASSIGN(NetInternalsHTMLSource); |
| }; |
| |
| // This class receives javascript messages from the renderer. |
| // Note that the DOMUI infrastructure runs on the UI thread, therefore all of |
| // this class's methods are expected to run on the UI thread. |
| // |
| // Since the network code we want to run lives on the IO thread, we proxy |
| // everything over to NetInternalsMessageHandler::IOThreadImpl, which runs |
| // on the IO thread. |
| // |
| // TODO(eroman): Can we start on the IO thread to begin with? |
| class NetInternalsMessageHandler |
| : public DOMMessageHandler, |
| public base::SupportsWeakPtr<NetInternalsMessageHandler> { |
| public: |
| NetInternalsMessageHandler(); |
| virtual ~NetInternalsMessageHandler(); |
| |
| // DOMMessageHandler implementation. |
| virtual DOMMessageHandler* Attach(DOMUI* dom_ui); |
| virtual void RegisterMessages(); |
| |
| // Executes the javascript function |function_name| in the renderer, passing |
| // it the argument |value|. |
| void CallJavascriptFunction(const std::wstring& function_name, |
| const Value* value); |
| |
| private: |
| class IOThreadImpl; |
| |
| // This is the "real" message handler, which lives on the IO thread. |
| scoped_refptr<IOThreadImpl> proxy_; |
| |
| DISALLOW_COPY_AND_ASSIGN(NetInternalsMessageHandler); |
| }; |
| |
| // This class is the "real" message handler. With the exception of being |
| // allocated and destroyed on the UI thread, its methods are expected to be |
| // called from the IO thread. |
| class NetInternalsMessageHandler::IOThreadImpl |
| : public base::RefCountedThreadSafe< |
| NetInternalsMessageHandler::IOThreadImpl, |
| ChromeThread::DeleteOnUIThread>, |
| public ChromeNetLog::Observer, |
| public ConnectionTester::Delegate { |
| public: |
| // Type for methods that can be used as MessageHandler callbacks. |
| typedef void (IOThreadImpl::*MessageHandler)(const ListValue*); |
| |
| // Creates a proxy for |handler| that will live on the IO thread. |
| // |handler| is a weak pointer, since it is possible for the DOMMessageHandler |
| // to be deleted on the UI thread while we were executing on the IO thread. |
| // |io_thread| is the global IOThread (it is passed in as an argument since |
| // we need to grab it from the UI thread). |
| IOThreadImpl( |
| const base::WeakPtr<NetInternalsMessageHandler>& handler, |
| IOThread* io_thread, |
| URLRequestContextGetter* context_getter); |
| |
| ~IOThreadImpl(); |
| |
| // Creates a callback that will run |method| on the IO thread. |
| // |
| // This can be used with DOMUI::RegisterMessageCallback() to bind to a method |
| // on the IO thread. |
| DOMUI::MessageCallback* CreateCallback(MessageHandler method); |
| |
| // Called once the DOMUI has been deleted (i.e. renderer went away), on the |
| // IO thread. |
| void Detach(); |
| |
| //-------------------------------- |
| // Javascript message handlers: |
| //-------------------------------- |
| |
| // This message is called after the webpage's onloaded handler has fired. |
| // it indicates that the renderer is ready to start receiving captured data. |
| void OnRendererReady(const ListValue* list); |
| |
| void OnGetProxySettings(const ListValue* list); |
| void OnReloadProxySettings(const ListValue* list); |
| void OnGetBadProxies(const ListValue* list); |
| void OnClearBadProxies(const ListValue* list); |
| void OnGetHostResolverCache(const ListValue* list); |
| void OnClearHostResolverCache(const ListValue* list); |
| void OnGetPassiveLogEntries(const ListValue* list); |
| void OnStartConnectionTests(const ListValue* list); |
| void OnGetHttpCacheInfo(const ListValue* list); |
| |
| // ChromeNetLog::Observer implementation: |
| virtual void OnAddEntry(net::NetLog::EventType type, |
| const base::TimeTicks& time, |
| const net::NetLog::Source& source, |
| net::NetLog::EventPhase phase, |
| net::NetLog::EventParameters* params); |
| |
| // ConnectionTester::Delegate implementation: |
| virtual void OnStartConnectionTestSuite(); |
| virtual void OnStartConnectionTestExperiment( |
| const ConnectionTester::Experiment& experiment); |
| virtual void OnCompletedConnectionTestExperiment( |
| const ConnectionTester::Experiment& experiment, |
| int result); |
| virtual void OnCompletedConnectionTestSuite(); |
| |
| private: |
| class CallbackHelper; |
| |
| // Helper that runs |method| with |arg|, and deletes |arg| on completion. |
| void DispatchToMessageHandler(ListValue* arg, MessageHandler method); |
| |
| // Helper that executes |function_name| in the attached renderer. |
| // The function takes ownership of |arg|. |
| void CallJavascriptFunction(const std::wstring& function_name, |
| Value* arg); |
| |
| // Pointer to the UI-thread message handler. Only access this from |
| // the UI thread. |
| base::WeakPtr<NetInternalsMessageHandler> handler_; |
| |
| // The global IOThread, which contains the global NetLog to observer. |
| IOThread* io_thread_; |
| |
| scoped_refptr<URLRequestContextGetter> context_getter_; |
| |
| // Helper that runs the suite of connection tests. |
| scoped_ptr<ConnectionTester> connection_tester_; |
| |
| // True if we have attached an observer to the NetLog already. |
| bool is_observing_log_; |
| friend class base::RefCountedThreadSafe<IOThreadImpl>; |
| }; |
| |
| // Helper class for a DOMUI::MessageCallback which when excuted calls |
| // instance->*method(value) on the IO thread. |
| class NetInternalsMessageHandler::IOThreadImpl::CallbackHelper |
| : public DOMUI::MessageCallback { |
| public: |
| CallbackHelper(IOThreadImpl* instance, IOThreadImpl::MessageHandler method) |
| : instance_(instance), |
| method_(method) { |
| DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| } |
| |
| virtual void RunWithParams(const Tuple1<const ListValue*>& params) { |
| DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| |
| // We need to make a copy of the value in order to pass it over to the IO |
| // thread. We will delete this in IOThreadImpl::DispatchMessageHandler(). |
| ListValue* list_copy = static_cast<ListValue*>( |
| params.a ? params.a->DeepCopy() : NULL); |
| |
| if (!ChromeThread::PostTask( |
| ChromeThread::IO, FROM_HERE, |
| NewRunnableMethod(instance_.get(), |
| &IOThreadImpl::DispatchToMessageHandler, |
| list_copy, method_))) { |
| // Failed posting the task, avoid leaking |list_copy|. |
| delete list_copy; |
| } |
| } |
| |
| private: |
| scoped_refptr<IOThreadImpl> instance_; |
| IOThreadImpl::MessageHandler method_; |
| }; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // NetInternalsHTMLSource |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| NetInternalsHTMLSource::NetInternalsHTMLSource() |
| : DataSource(chrome::kChromeUINetInternalsHost, MessageLoop::current()) { |
| } |
| |
| void NetInternalsHTMLSource::StartDataRequest(const std::string& path, |
| bool is_off_the_record, |
| int request_id) { |
| // The provided "path" may contain a fragment, or query section. We only |
| // care about the path itself, and will disregard anything else. |
| std::string filename = |
| GURL(std::string("chrome://net/") + path).path().substr(1); |
| |
| // The source for the net internals page is flattened during compilation, so |
| // the only resource that should legitimately be requested is the main file. |
| // Note that users can type anything into the address bar, though, so we must |
| // handle arbitrary input. |
| if (filename.empty() || filename == "index.html") { |
| scoped_refptr<RefCountedStaticMemory> bytes( |
| ResourceBundle::GetSharedInstance().LoadDataResourceBytes( |
| IDR_NET_INTERNALS_INDEX_HTML)); |
| if (bytes && bytes->front()) { |
| SendResponse(request_id, bytes); |
| return; |
| } |
| } |
| |
| const std::string data_string("<p style='color:red'>Failed to read resource" + |
| EscapeForHTML(filename) + "</p>"); |
| scoped_refptr<RefCountedBytes> bytes(new RefCountedBytes); |
| bytes->data.resize(data_string.size()); |
| std::copy(data_string.begin(), data_string.end(), bytes->data.begin()); |
| SendResponse(request_id, bytes); |
| } |
| |
| std::string NetInternalsHTMLSource::GetMimeType(const std::string&) const { |
| return "text/html"; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // NetInternalsMessageHandler |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| NetInternalsMessageHandler::NetInternalsMessageHandler() {} |
| |
| NetInternalsMessageHandler::~NetInternalsMessageHandler() { |
| if (proxy_) { |
| // Notify the handler on the IO thread that the renderer is gone. |
| ChromeThread::PostTask(ChromeThread::IO, FROM_HERE, |
| NewRunnableMethod(proxy_.get(), &IOThreadImpl::Detach)); |
| } |
| } |
| |
| DOMMessageHandler* NetInternalsMessageHandler::Attach(DOMUI* dom_ui) { |
| DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| proxy_ = new IOThreadImpl(this->AsWeakPtr(), g_browser_process->io_thread(), |
| dom_ui->GetProfile()->GetRequestContext()); |
| DOMMessageHandler* result = DOMMessageHandler::Attach(dom_ui); |
| return result; |
| } |
| |
| void NetInternalsMessageHandler::RegisterMessages() { |
| DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| |
| dom_ui_->RegisterMessageCallback( |
| "notifyReady", |
| proxy_->CreateCallback(&IOThreadImpl::OnRendererReady)); |
| dom_ui_->RegisterMessageCallback( |
| "getProxySettings", |
| proxy_->CreateCallback(&IOThreadImpl::OnGetProxySettings)); |
| dom_ui_->RegisterMessageCallback( |
| "reloadProxySettings", |
| proxy_->CreateCallback(&IOThreadImpl::OnReloadProxySettings)); |
| dom_ui_->RegisterMessageCallback( |
| "getBadProxies", |
| proxy_->CreateCallback(&IOThreadImpl::OnGetBadProxies)); |
| dom_ui_->RegisterMessageCallback( |
| "clearBadProxies", |
| proxy_->CreateCallback(&IOThreadImpl::OnClearBadProxies)); |
| dom_ui_->RegisterMessageCallback( |
| "getHostResolverCache", |
| proxy_->CreateCallback(&IOThreadImpl::OnGetHostResolverCache)); |
| dom_ui_->RegisterMessageCallback( |
| "clearHostResolverCache", |
| proxy_->CreateCallback(&IOThreadImpl::OnClearHostResolverCache)); |
| dom_ui_->RegisterMessageCallback( |
| "getPassiveLogEntries", |
| proxy_->CreateCallback(&IOThreadImpl::OnGetPassiveLogEntries)); |
| dom_ui_->RegisterMessageCallback( |
| "startConnectionTests", |
| proxy_->CreateCallback(&IOThreadImpl::OnStartConnectionTests)); |
| dom_ui_->RegisterMessageCallback( |
| "getHttpCacheInfo", |
| proxy_->CreateCallback(&IOThreadImpl::OnGetHttpCacheInfo)); |
| } |
| |
| void NetInternalsMessageHandler::CallJavascriptFunction( |
| const std::wstring& function_name, |
| const Value* value) { |
| DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| if (value) { |
| dom_ui_->CallJavascriptFunction(function_name, *value); |
| } else { |
| dom_ui_->CallJavascriptFunction(function_name); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // NetInternalsMessageHandler::IOThreadImpl |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| NetInternalsMessageHandler::IOThreadImpl::IOThreadImpl( |
| const base::WeakPtr<NetInternalsMessageHandler>& handler, |
| IOThread* io_thread, |
| URLRequestContextGetter* context_getter) |
| : handler_(handler), |
| io_thread_(io_thread), |
| context_getter_(context_getter), |
| is_observing_log_(false) { |
| DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| } |
| |
| NetInternalsMessageHandler::IOThreadImpl::~IOThreadImpl() { |
| DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| } |
| |
| DOMUI::MessageCallback* |
| NetInternalsMessageHandler::IOThreadImpl::CreateCallback( |
| MessageHandler method) { |
| DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| return new CallbackHelper(this, method); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::Detach() { |
| DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
| // Unregister with network stack to observe events. |
| if (is_observing_log_) |
| io_thread_->globals()->net_log->RemoveObserver(this); |
| |
| // Cancel any in-progress connection tests. |
| connection_tester_.reset(); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::OnRendererReady( |
| const ListValue* list) { |
| DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
| DCHECK(!is_observing_log_) << "notifyReady called twice"; |
| |
| // Register with network stack to observe events. |
| is_observing_log_ = true; |
| io_thread_->globals()->net_log->AddObserver(this); |
| |
| // Tell the javascript about the relationship between event type enums and |
| // their symbolic name. |
| { |
| std::vector<net::NetLog::EventType> event_types = |
| net::NetLog::GetAllEventTypes(); |
| |
| DictionaryValue* dict = new DictionaryValue(); |
| |
| for (size_t i = 0; i < event_types.size(); ++i) { |
| const char* name = net::NetLog::EventTypeToString(event_types[i]); |
| dict->SetInteger(name, static_cast<int>(event_types[i])); |
| } |
| |
| CallJavascriptFunction(L"g_browser.receivedLogEventTypeConstants", dict); |
| } |
| |
| // Tell the javascript about the version of the client and its |
| // command line arguments. |
| { |
| DictionaryValue* dict = new DictionaryValue(); |
| |
| chrome::VersionInfo version_info; |
| |
| if (version_info.is_valid()) { |
| DLOG(ERROR) << "Unable to create chrome::VersionInfo"; |
| } else { |
| // We have everything we need to send the right values. |
| dict->SetString("version", version_info.Version()); |
| dict->SetString("cl", version_info.LastChange()); |
| dict->SetString("version_mod", |
| platform_util::GetVersionStringModifier()); |
| dict->SetString("official", |
| l10n_util::GetStringUTF16( |
| version_info.IsOfficialBuild() ? |
| IDS_ABOUT_VERSION_OFFICIAL |
| : IDS_ABOUT_VERSION_UNOFFICIAL)); |
| |
| dict->SetString("command_line", |
| CommandLine::ForCurrentProcess()->command_line_string()); |
| } |
| |
| CallJavascriptFunction(L"g_browser.receivedClientInfo", |
| dict); |
| } |
| |
| // Tell the javascript about the relationship between load flag enums and |
| // their symbolic name. |
| { |
| DictionaryValue* dict = new DictionaryValue(); |
| |
| #define LOAD_FLAG(label, value) \ |
| dict->SetInteger(# label, static_cast<int>(value)); |
| #include "net/base/load_flags_list.h" |
| #undef LOAD_FLAG |
| |
| CallJavascriptFunction(L"g_browser.receivedLoadFlagConstants", dict); |
| } |
| |
| // Tell the javascript about the relationship between net error codes and |
| // their symbolic name. |
| { |
| DictionaryValue* dict = new DictionaryValue(); |
| |
| #define NET_ERROR(label, value) \ |
| dict->SetInteger(# label, static_cast<int>(value)); |
| #include "net/base/net_error_list.h" |
| #undef NET_ERROR |
| |
| CallJavascriptFunction(L"g_browser.receivedNetErrorConstants", dict); |
| } |
| |
| // Tell the javascript about the relationship between event phase enums and |
| // their symbolic name. |
| { |
| DictionaryValue* dict = new DictionaryValue(); |
| |
| dict->SetInteger("PHASE_BEGIN", net::NetLog::PHASE_BEGIN); |
| dict->SetInteger("PHASE_END", net::NetLog::PHASE_END); |
| dict->SetInteger("PHASE_NONE", net::NetLog::PHASE_NONE); |
| |
| CallJavascriptFunction(L"g_browser.receivedLogEventPhaseConstants", dict); |
| } |
| |
| // Tell the javascript about the relationship between source type enums and |
| // their symbolic name. |
| { |
| DictionaryValue* dict = new DictionaryValue(); |
| |
| #define SOURCE_TYPE(label, value) dict->SetInteger(# label, value); |
| #include "net/base/net_log_source_type_list.h" |
| #undef SOURCE_TYPE |
| |
| CallJavascriptFunction(L"g_browser.receivedLogSourceTypeConstants", dict); |
| } |
| |
| // Tell the javascript how the "time ticks" values we have given it relate to |
| // actual system times. (We used time ticks throughout since they are stable |
| // across system clock changes). |
| { |
| int64 cur_time_ms = (base::Time::Now() - base::Time()).InMilliseconds(); |
| |
| int64 cur_time_ticks_ms = |
| (base::TimeTicks::Now() - base::TimeTicks()).InMilliseconds(); |
| |
| // If we add this number to a time tick value, it gives the timestamp. |
| int64 tick_to_time_ms = cur_time_ms - cur_time_ticks_ms; |
| |
| // Chrome on all platforms stores times using the Windows epoch |
| // (Jan 1 1601), but the javascript wants a unix epoch. |
| // TODO(eroman): Getting the timestamp relative the to unix epoch should |
| // be part of the time library. |
| const int64 kUnixEpochMs = 11644473600000LL; |
| int64 tick_to_unix_time_ms = tick_to_time_ms - kUnixEpochMs; |
| |
| // Pass it as a string, since it may be too large to fit in an integer. |
| CallJavascriptFunction(L"g_browser.receivedTimeTickOffset", |
| Value::CreateStringValue( |
| base::Int64ToString(tick_to_unix_time_ms))); |
| } |
| |
| OnGetPassiveLogEntries(NULL); |
| OnGetProxySettings(NULL); |
| OnGetBadProxies(NULL); |
| OnGetHostResolverCache(NULL); |
| OnGetHttpCacheInfo(NULL); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::OnGetProxySettings( |
| const ListValue* list) { |
| URLRequestContext* context = context_getter_->GetURLRequestContext(); |
| net::ProxyService* proxy_service = context->proxy_service(); |
| |
| Value* settings_value = NULL; |
| if (proxy_service->config_has_been_initialized()) { |
| settings_value = proxy_service->config().ToValue(); |
| } else { |
| settings_value = Value::CreateStringValue(std::string()); |
| } |
| |
| CallJavascriptFunction(L"g_browser.receivedProxySettings", |
| settings_value); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::OnReloadProxySettings( |
| const ListValue* list) { |
| URLRequestContext* context = context_getter_->GetURLRequestContext(); |
| context->proxy_service()->ForceReloadProxyConfig(); |
| |
| // Cause the renderer to be notified of the new values. |
| OnGetProxySettings(NULL); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::OnGetBadProxies( |
| const ListValue* list) { |
| URLRequestContext* context = context_getter_->GetURLRequestContext(); |
| |
| const net::ProxyRetryInfoMap& bad_proxies_map = |
| context->proxy_service()->proxy_retry_info(); |
| |
| ListValue* dict_list = new ListValue(); |
| |
| for (net::ProxyRetryInfoMap::const_iterator it = bad_proxies_map.begin(); |
| it != bad_proxies_map.end(); ++it) { |
| const std::string& proxy_uri = it->first; |
| const net::ProxyRetryInfo& retry_info = it->second; |
| |
| DictionaryValue* dict = new DictionaryValue(); |
| dict->SetString("proxy_uri", proxy_uri); |
| dict->SetString("bad_until", TickCountToString(retry_info.bad_until)); |
| |
| dict_list->Append(dict); |
| } |
| |
| CallJavascriptFunction(L"g_browser.receivedBadProxies", dict_list); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::OnClearBadProxies( |
| const ListValue* list) { |
| URLRequestContext* context = context_getter_->GetURLRequestContext(); |
| context->proxy_service()->ClearBadProxiesCache(); |
| |
| // Cause the renderer to be notified of the new values. |
| OnGetBadProxies(NULL); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::OnGetHostResolverCache( |
| const ListValue* list) { |
| |
| net::HostCache* cache = |
| GetHostResolverCache(context_getter_->GetURLRequestContext()); |
| |
| if (!cache) { |
| CallJavascriptFunction(L"g_browser.receivedHostResolverCache", NULL); |
| return; |
| } |
| |
| DictionaryValue* dict = new DictionaryValue(); |
| |
| dict->SetInteger("capacity", static_cast<int>(cache->max_entries())); |
| dict->SetInteger( |
| "ttl_success_ms", |
| static_cast<int>(cache->success_entry_ttl().InMilliseconds())); |
| dict->SetInteger( |
| "ttl_failure_ms", |
| static_cast<int>(cache->failure_entry_ttl().InMilliseconds())); |
| |
| ListValue* entry_list = new ListValue(); |
| |
| for (net::HostCache::EntryMap::const_iterator it = |
| cache->entries().begin(); |
| it != cache->entries().end(); |
| ++it) { |
| const net::HostCache::Key& key = it->first; |
| const net::HostCache::Entry* entry = it->second.get(); |
| |
| DictionaryValue* entry_dict = new DictionaryValue(); |
| |
| entry_dict->SetString("hostname", key.hostname); |
| entry_dict->SetInteger("address_family", |
| static_cast<int>(key.address_family)); |
| entry_dict->SetString("expiration", TickCountToString(entry->expiration)); |
| |
| if (entry->error != net::OK) { |
| entry_dict->SetInteger("error", entry->error); |
| } else { |
| // Append all of the resolved addresses. |
| ListValue* address_list = new ListValue(); |
| const struct addrinfo* current_address = entry->addrlist.head(); |
| while (current_address) { |
| address_list->Append(Value::CreateStringValue( |
| net::NetAddressToStringWithPort(current_address))); |
| current_address = current_address->ai_next; |
| } |
| entry_dict->Set("addresses", address_list); |
| } |
| |
| entry_list->Append(entry_dict); |
| } |
| |
| dict->Set("entries", entry_list); |
| |
| CallJavascriptFunction(L"g_browser.receivedHostResolverCache", dict); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::OnClearHostResolverCache( |
| const ListValue* list) { |
| net::HostCache* cache = |
| GetHostResolverCache(context_getter_->GetURLRequestContext()); |
| |
| if (cache) |
| cache->clear(); |
| |
| // Cause the renderer to be notified of the new values. |
| OnGetHostResolverCache(NULL); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::OnGetPassiveLogEntries( |
| const ListValue* list) { |
| ChromeNetLog* net_log = io_thread_->globals()->net_log.get(); |
| |
| PassiveLogCollector::EntryList passive_entries; |
| net_log->passive_collector()->GetAllCapturedEvents(&passive_entries); |
| |
| ListValue* dict_list = new ListValue(); |
| for (size_t i = 0; i < passive_entries.size(); ++i) { |
| const PassiveLogCollector::Entry& e = passive_entries[i]; |
| dict_list->Append(net::NetLog::EntryToDictionaryValue(e.type, |
| e.time, |
| e.source, |
| e.phase, |
| e.params, |
| false)); |
| } |
| |
| CallJavascriptFunction(L"g_browser.receivedPassiveLogEntries", dict_list); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::OnStartConnectionTests( |
| const ListValue* list) { |
| // |value| should be: [<URL to test>]. |
| string16 url_str; |
| CHECK(list->GetString(0, &url_str)); |
| |
| // Try to fix-up the user provided URL into something valid. |
| // For example, turn "www.google.com" into "http://www.google.com". |
| GURL url(URLFixerUpper::FixupURL(UTF16ToUTF8(url_str), std::string())); |
| |
| connection_tester_.reset(new ConnectionTester(this)); |
| connection_tester_->RunAllTests(url); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::OnGetHttpCacheInfo( |
| const ListValue* list) { |
| DictionaryValue* info_dict = new DictionaryValue(); |
| DictionaryValue* stats_dict = new DictionaryValue(); |
| |
| disk_cache::Backend* disk_cache = GetDiskCacheBackend( |
| context_getter_->GetURLRequestContext()); |
| |
| if (disk_cache) { |
| // Extract the statistics key/value pairs from the backend. |
| std::vector<std::pair<std::string, std::string> > stats; |
| disk_cache->GetStats(&stats); |
| for (size_t i = 0; i < stats.size(); ++i) { |
| stats_dict->Set(stats[i].first, |
| Value::CreateStringValue(stats[i].second)); |
| } |
| } |
| |
| info_dict->Set("stats", stats_dict); |
| |
| CallJavascriptFunction(L"g_browser.receivedHttpCacheInfo", info_dict); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::OnAddEntry( |
| net::NetLog::EventType type, |
| const base::TimeTicks& time, |
| const net::NetLog::Source& source, |
| net::NetLog::EventPhase phase, |
| net::NetLog::EventParameters* params) { |
| DCHECK(is_observing_log_); |
| |
| CallJavascriptFunction( |
| L"g_browser.receivedLogEntry", |
| net::NetLog::EntryToDictionaryValue(type, time, source, phase, params, |
| false)); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::OnStartConnectionTestSuite() { |
| CallJavascriptFunction(L"g_browser.receivedStartConnectionTestSuite", NULL); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::OnStartConnectionTestExperiment( |
| const ConnectionTester::Experiment& experiment) { |
| CallJavascriptFunction( |
| L"g_browser.receivedStartConnectionTestExperiment", |
| ExperimentToValue(experiment)); |
| } |
| |
| void |
| NetInternalsMessageHandler::IOThreadImpl::OnCompletedConnectionTestExperiment( |
| const ConnectionTester::Experiment& experiment, |
| int result) { |
| DictionaryValue* dict = new DictionaryValue(); |
| |
| dict->Set("experiment", ExperimentToValue(experiment)); |
| dict->SetInteger("result", result); |
| |
| CallJavascriptFunction( |
| L"g_browser.receivedCompletedConnectionTestExperiment", |
| dict); |
| } |
| |
| void |
| NetInternalsMessageHandler::IOThreadImpl::OnCompletedConnectionTestSuite() { |
| CallJavascriptFunction( |
| L"g_browser.receivedCompletedConnectionTestSuite", |
| NULL); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::DispatchToMessageHandler( |
| ListValue* arg, MessageHandler method) { |
| DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
| (this->*method)(arg); |
| delete arg; |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::CallJavascriptFunction( |
| const std::wstring& function_name, |
| Value* arg) { |
| if (ChromeThread::CurrentlyOn(ChromeThread::UI)) { |
| if (handler_) { |
| // We check |handler_| in case it was deleted on the UI thread earlier |
| // while we were running on the IO thread. |
| handler_->CallJavascriptFunction(function_name, arg); |
| } |
| delete arg; |
| return; |
| } |
| |
| // Otherwise if we were called from the IO thread, bridge the request over to |
| // the UI thread. |
| |
| DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
| if (!ChromeThread::PostTask( |
| ChromeThread::UI, FROM_HERE, |
| NewRunnableMethod( |
| this, |
| &IOThreadImpl::CallJavascriptFunction, |
| function_name, arg))) { |
| // Failed posting the task, avoid leaking. |
| delete arg; |
| } |
| } |
| |
| } // namespace |
| |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // NetInternalsUI |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| NetInternalsUI::NetInternalsUI(TabContents* contents) : DOMUI(contents) { |
| AddMessageHandler((new NetInternalsMessageHandler())->Attach(this)); |
| |
| NetInternalsHTMLSource* html_source = new NetInternalsHTMLSource(); |
| |
| // Set up the chrome://net-internals/ source. |
| ChromeThread::PostTask( |
| ChromeThread::IO, FROM_HERE, |
| NewRunnableMethod( |
| Singleton<ChromeURLDataManager>::get(), |
| &ChromeURLDataManager::AddDataSource, |
| make_scoped_refptr(html_source))); |
| } |