| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/webui/net_internals_ui.h" |
| |
| #include <algorithm> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/base64.h" |
| #include "base/command_line.h" |
| #include "base/file_util.h" |
| #include "base/memory/singleton.h" |
| #include "base/message_loop.h" |
| #include "base/path_service.h" |
| #include "base/string_number_conversions.h" |
| #include "base/string_piece.h" |
| #include "base/string_split.h" |
| #include "base/string_util.h" |
| #include "base/utf_string_conversions.h" |
| #include "base/values.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/io_thread.h" |
| #include "chrome/browser/net/chrome_net_log.h" |
| #include "chrome/browser/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/prefs/pref_member.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/shell_dialogs.h" |
| #include "chrome/browser/ui/webui/chrome_url_data_manager.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "chrome/common/chrome_version_info.h" |
| #include "chrome/common/jstemplate_builder.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/url_constants.h" |
| #include "content/browser/browser_thread.h" |
| #include "content/browser/tab_contents/tab_contents.h" |
| #include "content/browser/tab_contents/tab_contents_view.h" |
| #include "content/common/notification_details.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/base/x509_cert_types.h" |
| #include "net/disk_cache/disk_cache.h" |
| #include "net/http/http_alternate_protocols.h" |
| #include "net/http/http_cache.h" |
| #include "net/http/http_network_layer.h" |
| #include "net/http/http_network_session.h" |
| #include "net/http/http_stream_factory.h" |
| #include "net/proxy/proxy_service.h" |
| #include "net/url_request/url_request_context.h" |
| #include "net/url_request/url_request_context_getter.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/resource/resource_bundle.h" |
| |
| #ifdef OS_WIN |
| #include "chrome/browser/net/service_providers_win.h" |
| #endif |
| |
| namespace { |
| |
| // Delay between when an event occurs and when it is passed to the Javascript |
| // page. All events that occur during this period are grouped together and |
| // sent to the page at once, which reduces context switching and CPU usage. |
| const int kNetLogEventDelayMilliseconds = 100; |
| |
| // Returns the HostCache for |context|'s primary HostResolver, or NULL if |
| // there is none. |
| net::HostCache* GetHostResolverCache(net::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(net::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(); |
| } |
| |
| // Returns the http network session for |context| if there is one. |
| // Otherwise, returns NULL. |
| net::HttpNetworkSession* GetHttpNetworkSession( |
| net::URLRequestContext* context) { |
| if (!context->http_transaction_factory()) |
| return NULL; |
| |
| return context->http_transaction_factory()->GetSession(); |
| } |
| |
| 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_incognito, |
| 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 WebUI 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 |
| // almost 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 WebUIMessageHandler, |
| public SelectFileDialog::Listener, |
| public base::SupportsWeakPtr<NetInternalsMessageHandler>, |
| public NotificationObserver { |
| public: |
| NetInternalsMessageHandler(); |
| virtual ~NetInternalsMessageHandler(); |
| |
| // WebUIMessageHandler implementation. |
| virtual WebUIMessageHandler* Attach(WebUI* web_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); |
| |
| // NotificationObserver implementation. |
| virtual void Observe(NotificationType type, |
| const NotificationSource& source, |
| const NotificationDetails& details); |
| |
| // Javascript message handlers. |
| void OnRendererReady(const ListValue* list); |
| void OnEnableHttpThrottling(const ListValue* list); |
| |
| // SelectFileDialog::Listener implementation |
| virtual void FileSelected(const FilePath& path, int index, void* params); |
| virtual void FileSelectionCanceled(void* params); |
| |
| // The only callback handled on the UI thread. As it needs to access fields |
| // from |web_ui_|, it can't be called on the IO thread. |
| void OnLoadLogFile(const ListValue* list); |
| |
| private: |
| class IOThreadImpl; |
| |
| // Task run on the FILE thread to read the contents of a log file. The result |
| // is then passed to IOThreadImpl's CallJavascriptFunction, which sends it |
| // back to the web page. IOThreadImpl is used instead of the |
| // NetInternalsMessageHandler directly because it checks if the message |
| // handler has been destroyed in the meantime. |
| class ReadLogFileTask : public Task { |
| public: |
| ReadLogFileTask(IOThreadImpl* proxy, const FilePath& path); |
| |
| virtual void Run(); |
| |
| private: |
| // IOThreadImpl implements existence checks already. Simpler to reused them |
| // then to reimplement them. |
| scoped_refptr<IOThreadImpl> proxy_; |
| |
| // Path of the file to open. |
| const FilePath path_; |
| }; |
| |
| // The pref member about whether HTTP throttling is enabled, which needs to |
| // be accessed on the UI thread. |
| BooleanPrefMember http_throttling_enabled_; |
| |
| // OnRendererReady invokes this callback to do the part of message handling |
| // that needs to happen on the IO thread. |
| scoped_ptr<WebUI::MessageCallback> renderer_ready_io_callback_; |
| |
| // This is the "real" message handler, which lives on the IO thread. |
| scoped_refptr<IOThreadImpl> proxy_; |
| |
| // Used for loading log files. |
| scoped_refptr<SelectFileDialog> select_log_file_dialog_; |
| |
| DISALLOW_COPY_AND_ASSIGN(NetInternalsMessageHandler); |
| }; |
| |
| // This class is the "real" message handler. It is allocated and destroyed on |
| // the UI thread. With the exception of OnAddEntry, OnWebUIDeleted, and |
| // CallJavascriptFunction, its methods are all expected to be called from the IO |
| // thread. OnAddEntry and CallJavascriptFunction can be called from any thread, |
| // and OnWebUIDeleted can only be called from the UI thread. |
| class NetInternalsMessageHandler::IOThreadImpl |
| : public base::RefCountedThreadSafe< |
| NetInternalsMessageHandler::IOThreadImpl, |
| BrowserThread::DeleteOnUIThread>, |
| public ChromeNetLog::ThreadSafeObserver, |
| 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 |
| // WebUIMessageHandler 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, |
| net::URLRequestContextGetter* context_getter); |
| |
| // Creates a callback that will run |method| on the IO thread. |
| // |
| // This can be used with WebUI::RegisterMessageCallback() to bind to a method |
| // on the IO thread. |
| WebUI::MessageCallback* CreateCallback(MessageHandler method); |
| |
| // Called once the WebUI has been deleted (i.e. renderer went away), on the |
| // IO thread. |
| void Detach(); |
| |
| // Sends all passive log entries in |passive_entries| to the Javascript |
| // handler, called on the IO thread. |
| void SendPassiveLogEntries(const ChromeNetLog::EntryList& passive_entries); |
| |
| // Called when the WebUI is deleted. Prevents calling Javascript functions |
| // afterwards. Called on UI thread. |
| void OnWebUIDeleted(); |
| |
| //-------------------------------- |
| // Javascript message handlers: |
| //-------------------------------- |
| |
| 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 OnGetHostResolverInfo(const ListValue* list); |
| void OnClearHostResolverCache(const ListValue* list); |
| void OnEnableIPv6(const ListValue* list); |
| void OnStartConnectionTests(const ListValue* list); |
| void OnHSTSQuery(const ListValue* list); |
| void OnHSTSAdd(const ListValue* list); |
| void OnHSTSDelete(const ListValue* list); |
| void OnGetHttpCacheInfo(const ListValue* list); |
| void OnGetSocketPoolInfo(const ListValue* list); |
| void OnCloseIdleSockets(const ListValue* list); |
| void OnFlushSocketPools(const ListValue* list); |
| void OnGetSpdySessionInfo(const ListValue* list); |
| void OnGetSpdyStatus(const ListValue* list); |
| void OnGetSpdyAlternateProtocolMappings(const ListValue* list); |
| #ifdef OS_WIN |
| void OnGetServiceProviders(const ListValue* list); |
| #endif |
| |
| void OnSetLogLevel(const ListValue* list); |
| |
| // ChromeNetLog::ThreadSafeObserver 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(); |
| |
| // Helper that executes |function_name| in the attached renderer. |
| // The function takes ownership of |arg|. Note that this can be called from |
| // any thread. |
| void CallJavascriptFunction(const std::wstring& function_name, Value* arg); |
| |
| private: |
| friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>; |
| friend class DeleteTask<IOThreadImpl>; |
| |
| ~IOThreadImpl(); |
| |
| class CallbackHelper; |
| |
| // Helper that runs |method| with |arg|, and deletes |arg| on completion. |
| void DispatchToMessageHandler(ListValue* arg, MessageHandler method); |
| |
| // Adds |entry| to the queue of pending log entries to be sent to the page via |
| // Javascript. Must be called on the IO Thread. Also creates a delayed task |
| // that will call PostPendingEntries, if there isn't one already. |
| void AddEntryToQueue(Value* entry); |
| |
| // Sends all pending entries to the page via Javascript, and clears the list |
| // of pending entries. Sending multiple entries at once results in a |
| // significant reduction of CPU usage when a lot of events are happening. |
| // Must be called on the IO Thread. |
| void PostPendingEntries(); |
| |
| // 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<net::URLRequestContextGetter> context_getter_; |
| |
| // Helper that runs the suite of connection tests. |
| scoped_ptr<ConnectionTester> connection_tester_; |
| |
| // True if the Web UI has been deleted. This is used to prevent calling |
| // Javascript functions after the Web UI is destroyed. On refresh, the |
| // messages can end up being sent to the refreshed page, causing duplicate |
| // or partial entries. |
| // |
| // This is only read and written to on the UI thread. |
| bool was_webui_deleted_; |
| |
| // True if we have attached an observer to the NetLog already. |
| bool is_observing_log_; |
| |
| // Log entries that have yet to be passed along to Javascript page. Non-NULL |
| // when and only when there is a pending delayed task to call |
| // PostPendingEntries. Read and written to exclusively on the IO Thread. |
| scoped_ptr<ListValue> pending_entries_; |
| }; |
| |
| // Helper class for a WebUI::MessageCallback which when executed calls |
| // instance->*method(value) on the IO thread. |
| class NetInternalsMessageHandler::IOThreadImpl::CallbackHelper |
| : public WebUI::MessageCallback { |
| public: |
| CallbackHelper(IOThreadImpl* instance, IOThreadImpl::MessageHandler method) |
| : instance_(instance), |
| method_(method) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| } |
| |
| virtual void RunWithParams(const Tuple1<const ListValue*>& params) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::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 (!BrowserThread::PostTask( |
| BrowserThread::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_incognito, |
| int request_id) { |
| DictionaryValue localized_strings; |
| SetFontAndTextDirection(&localized_strings); |
| |
| // 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") { |
| base::StringPiece html( |
| ResourceBundle::GetSharedInstance().GetRawDataResource( |
| IDR_NET_INTERNALS_INDEX_HTML)); |
| std::string full_html(html.data(), html.size()); |
| jstemplate_builder::AppendJsonHtml(&localized_strings, &full_html); |
| jstemplate_builder::AppendI18nTemplateSourceHtml(&full_html); |
| jstemplate_builder::AppendI18nTemplateProcessHtml(&full_html); |
| jstemplate_builder::AppendJsTemplateSourceHtml(&full_html); |
| |
| scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes); |
| html_bytes->data.resize(full_html.size()); |
| std::copy(full_html.begin(), full_html.end(), html_bytes->data.begin()); |
| SendResponse(request_id, html_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_) { |
| proxy_.get()->OnWebUIDeleted(); |
| // Notify the handler on the IO thread that the renderer is gone. |
| BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
| NewRunnableMethod(proxy_.get(), &IOThreadImpl::Detach)); |
| } |
| if (select_log_file_dialog_) |
| select_log_file_dialog_->ListenerDestroyed(); |
| } |
| |
| WebUIMessageHandler* NetInternalsMessageHandler::Attach(WebUI* web_ui) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| PrefService* pref_service = web_ui->GetProfile()->GetPrefs(); |
| http_throttling_enabled_.Init(prefs::kHttpThrottlingEnabled, pref_service, |
| this); |
| |
| proxy_ = new IOThreadImpl(this->AsWeakPtr(), g_browser_process->io_thread(), |
| web_ui->GetProfile()->GetRequestContext()); |
| renderer_ready_io_callback_.reset( |
| proxy_->CreateCallback(&IOThreadImpl::OnRendererReady)); |
| |
| WebUIMessageHandler* result = WebUIMessageHandler::Attach(web_ui); |
| return result; |
| } |
| |
| void NetInternalsMessageHandler::FileSelected( |
| const FilePath& path, int index, void* params) { |
| select_log_file_dialog_.release(); |
| BrowserThread::PostTask( |
| BrowserThread::FILE, FROM_HERE, |
| new ReadLogFileTask(proxy_.get(), path)); |
| } |
| |
| void NetInternalsMessageHandler::FileSelectionCanceled(void* params) { |
| select_log_file_dialog_.release(); |
| } |
| |
| void NetInternalsMessageHandler::OnLoadLogFile(const ListValue* list) { |
| // Only allow a single dialog at a time. |
| if (select_log_file_dialog_.get()) |
| return; |
| select_log_file_dialog_ = SelectFileDialog::Create(this); |
| select_log_file_dialog_->SelectFile( |
| SelectFileDialog::SELECT_OPEN_FILE, string16(), FilePath(), NULL, 0, |
| FILE_PATH_LITERAL(""), web_ui_->tab_contents(), |
| web_ui_->tab_contents()->view()->GetTopLevelNativeWindow(), NULL); |
| } |
| |
| void NetInternalsMessageHandler::RegisterMessages() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| // Only callback handled on UI thread. |
| web_ui_->RegisterMessageCallback( |
| "loadLogFile", |
| NewCallback(this, &NetInternalsMessageHandler::OnLoadLogFile)); |
| |
| web_ui_->RegisterMessageCallback( |
| "notifyReady", |
| NewCallback(this, &NetInternalsMessageHandler::OnRendererReady)); |
| web_ui_->RegisterMessageCallback( |
| "getProxySettings", |
| proxy_->CreateCallback(&IOThreadImpl::OnGetProxySettings)); |
| web_ui_->RegisterMessageCallback( |
| "reloadProxySettings", |
| proxy_->CreateCallback(&IOThreadImpl::OnReloadProxySettings)); |
| web_ui_->RegisterMessageCallback( |
| "getBadProxies", |
| proxy_->CreateCallback(&IOThreadImpl::OnGetBadProxies)); |
| web_ui_->RegisterMessageCallback( |
| "clearBadProxies", |
| proxy_->CreateCallback(&IOThreadImpl::OnClearBadProxies)); |
| web_ui_->RegisterMessageCallback( |
| "getHostResolverInfo", |
| proxy_->CreateCallback(&IOThreadImpl::OnGetHostResolverInfo)); |
| web_ui_->RegisterMessageCallback( |
| "clearHostResolverCache", |
| proxy_->CreateCallback(&IOThreadImpl::OnClearHostResolverCache)); |
| web_ui_->RegisterMessageCallback( |
| "enableIPv6", |
| proxy_->CreateCallback(&IOThreadImpl::OnEnableIPv6)); |
| web_ui_->RegisterMessageCallback( |
| "startConnectionTests", |
| proxy_->CreateCallback(&IOThreadImpl::OnStartConnectionTests)); |
| web_ui_->RegisterMessageCallback( |
| "hstsQuery", |
| proxy_->CreateCallback(&IOThreadImpl::OnHSTSQuery)); |
| web_ui_->RegisterMessageCallback( |
| "hstsAdd", |
| proxy_->CreateCallback(&IOThreadImpl::OnHSTSAdd)); |
| web_ui_->RegisterMessageCallback( |
| "hstsDelete", |
| proxy_->CreateCallback(&IOThreadImpl::OnHSTSDelete)); |
| web_ui_->RegisterMessageCallback( |
| "getHttpCacheInfo", |
| proxy_->CreateCallback(&IOThreadImpl::OnGetHttpCacheInfo)); |
| web_ui_->RegisterMessageCallback( |
| "getSocketPoolInfo", |
| proxy_->CreateCallback(&IOThreadImpl::OnGetSocketPoolInfo)); |
| web_ui_->RegisterMessageCallback( |
| "closeIdleSockets", |
| proxy_->CreateCallback(&IOThreadImpl::OnCloseIdleSockets)); |
| web_ui_->RegisterMessageCallback( |
| "flushSocketPools", |
| proxy_->CreateCallback(&IOThreadImpl::OnFlushSocketPools)); |
| web_ui_->RegisterMessageCallback( |
| "getSpdySessionInfo", |
| proxy_->CreateCallback(&IOThreadImpl::OnGetSpdySessionInfo)); |
| web_ui_->RegisterMessageCallback( |
| "getSpdyStatus", |
| proxy_->CreateCallback(&IOThreadImpl::OnGetSpdyStatus)); |
| web_ui_->RegisterMessageCallback( |
| "getSpdyAlternateProtocolMappings", |
| proxy_->CreateCallback( |
| &IOThreadImpl::OnGetSpdyAlternateProtocolMappings)); |
| #ifdef OS_WIN |
| web_ui_->RegisterMessageCallback( |
| "getServiceProviders", |
| proxy_->CreateCallback(&IOThreadImpl::OnGetServiceProviders)); |
| #endif |
| |
| web_ui_->RegisterMessageCallback( |
| "setLogLevel", |
| proxy_->CreateCallback(&IOThreadImpl::OnSetLogLevel)); |
| |
| web_ui_->RegisterMessageCallback( |
| "enableHttpThrottling", |
| NewCallback(this, &NetInternalsMessageHandler::OnEnableHttpThrottling)); |
| } |
| |
| void NetInternalsMessageHandler::CallJavascriptFunction( |
| const std::wstring& function_name, |
| const Value* value) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| if (value) { |
| web_ui_->CallJavascriptFunction(WideToASCII(function_name), *value); |
| } else { |
| web_ui_->CallJavascriptFunction(WideToASCII(function_name)); |
| } |
| } |
| |
| void NetInternalsMessageHandler::Observe(NotificationType type, |
| const NotificationSource& source, |
| const NotificationDetails& details) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| DCHECK_EQ(type.value, NotificationType::PREF_CHANGED); |
| |
| std::string* pref_name = Details<std::string>(details).ptr(); |
| if (*pref_name == prefs::kHttpThrottlingEnabled) { |
| scoped_ptr<Value> enabled( |
| Value::CreateBooleanValue(*http_throttling_enabled_)); |
| |
| CallJavascriptFunction( |
| L"g_browser.receivedHttpThrottlingEnabledPrefChanged", enabled.get()); |
| } |
| } |
| |
| void NetInternalsMessageHandler::OnRendererReady(const ListValue* list) { |
| CHECK(renderer_ready_io_callback_.get()); |
| renderer_ready_io_callback_->Run(list); |
| |
| scoped_ptr<Value> enabled( |
| Value::CreateBooleanValue(*http_throttling_enabled_)); |
| CallJavascriptFunction( |
| L"g_browser.receivedHttpThrottlingEnabledPrefChanged", enabled.get()); |
| } |
| |
| void NetInternalsMessageHandler::OnEnableHttpThrottling(const ListValue* list) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| bool enable = false; |
| if (!list->GetBoolean(0, &enable)) { |
| NOTREACHED(); |
| return; |
| } |
| |
| http_throttling_enabled_.SetValue(enable); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // NetInternalsMessageHandler::ReadLogFileTask |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| NetInternalsMessageHandler::ReadLogFileTask::ReadLogFileTask( |
| IOThreadImpl* proxy, const FilePath& path) |
| : proxy_(proxy), path_(path) { |
| } |
| |
| void NetInternalsMessageHandler::ReadLogFileTask::Run() { |
| std::string file_contents; |
| if (!file_util::ReadFileToString(path_, &file_contents)) |
| return; |
| proxy_->CallJavascriptFunction(L"g_browser.loadedLogFile", |
| new StringValue(file_contents)); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // NetInternalsMessageHandler::IOThreadImpl |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| NetInternalsMessageHandler::IOThreadImpl::IOThreadImpl( |
| const base::WeakPtr<NetInternalsMessageHandler>& handler, |
| IOThread* io_thread, |
| net::URLRequestContextGetter* context_getter) |
| : ThreadSafeObserver(net::NetLog::LOG_ALL_BUT_BYTES), |
| handler_(handler), |
| io_thread_(io_thread), |
| context_getter_(context_getter), |
| was_webui_deleted_(false), |
| is_observing_log_(false) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| } |
| |
| NetInternalsMessageHandler::IOThreadImpl::~IOThreadImpl() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| } |
| |
| WebUI::MessageCallback* |
| NetInternalsMessageHandler::IOThreadImpl::CreateCallback( |
| MessageHandler method) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| return new CallbackHelper(this, method); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::Detach() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| // Unregister with network stack to observe events. |
| if (is_observing_log_) |
| io_thread_->net_log()->RemoveObserver(this); |
| |
| // Cancel any in-progress connection tests. |
| connection_tester_.reset(); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::SendPassiveLogEntries( |
| const ChromeNetLog::EntryList& passive_entries) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| ListValue* dict_list = new ListValue(); |
| for (size_t i = 0; i < passive_entries.size(); ++i) { |
| const ChromeNetLog::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::OnWebUIDeleted() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| was_webui_deleted_ = true; |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::OnRendererReady( |
| const ListValue* list) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| DCHECK(!is_observing_log_) << "notifyReady called twice"; |
| |
| // 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 names. |
| { |
| 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 about the relationship between LogLevel enums and their |
| // symbolic names. |
| { |
| DictionaryValue* dict = new DictionaryValue(); |
| |
| dict->SetInteger("LOG_ALL", net::NetLog::LOG_ALL); |
| dict->SetInteger("LOG_ALL_BUT_BYTES", net::NetLog::LOG_ALL_BUT_BYTES); |
| dict->SetInteger("LOG_BASIC", net::NetLog::LOG_BASIC); |
| |
| CallJavascriptFunction(L"g_browser.receivedLogLevelConstants", dict); |
| } |
| |
| // Tell the javascript about the relationship between address family enums and |
| // their symbolic names. |
| { |
| DictionaryValue* dict = new DictionaryValue(); |
| |
| dict->SetInteger("ADDRESS_FAMILY_UNSPECIFIED", |
| net::ADDRESS_FAMILY_UNSPECIFIED); |
| dict->SetInteger("ADDRESS_FAMILY_IPV4", |
| net::ADDRESS_FAMILY_IPV4); |
| dict->SetInteger("ADDRESS_FAMILY_IPV6", |
| net::ADDRESS_FAMILY_IPV6); |
| |
| CallJavascriptFunction(L"g_browser.receivedAddressFamilyConstants", 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))); |
| } |
| |
| // Register with network stack to observe events. |
| is_observing_log_ = true; |
| ChromeNetLog::EntryList entries; |
| io_thread_->net_log()->AddObserverAndGetAllPassivelyCapturedEvents(this, |
| &entries); |
| SendPassiveLogEntries(entries); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::OnGetProxySettings( |
| const ListValue* list) { |
| net::URLRequestContext* context = context_getter_->GetURLRequestContext(); |
| net::ProxyService* proxy_service = context->proxy_service(); |
| |
| DictionaryValue* dict = new DictionaryValue(); |
| if (proxy_service->fetched_config().is_valid()) |
| dict->Set("original", proxy_service->fetched_config().ToValue()); |
| if (proxy_service->config().is_valid()) |
| dict->Set("effective", proxy_service->config().ToValue()); |
| |
| CallJavascriptFunction(L"g_browser.receivedProxySettings", dict); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::OnReloadProxySettings( |
| const ListValue* list) { |
| net::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) { |
| net::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", |
| net::NetLog::TickCountToString(retry_info.bad_until)); |
| |
| dict_list->Append(dict); |
| } |
| |
| CallJavascriptFunction(L"g_browser.receivedBadProxies", dict_list); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::OnClearBadProxies( |
| const ListValue* list) { |
| net::URLRequestContext* context = context_getter_->GetURLRequestContext(); |
| context->proxy_service()->ClearBadProxiesCache(); |
| |
| // Cause the renderer to be notified of the new values. |
| OnGetBadProxies(NULL); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::OnGetHostResolverInfo( |
| const ListValue* list) { |
| net::URLRequestContext* context = context_getter_->GetURLRequestContext(); |
| net::HostResolverImpl* host_resolver_impl = |
| context->host_resolver()->GetAsHostResolverImpl(); |
| net::HostCache* cache = GetHostResolverCache(context); |
| |
| if (!host_resolver_impl || !cache) { |
| CallJavascriptFunction(L"g_browser.receivedHostResolverInfo", NULL); |
| return; |
| } |
| |
| DictionaryValue* dict = new DictionaryValue(); |
| |
| dict->SetInteger( |
| "default_address_family", |
| static_cast<int>(host_resolver_impl->GetDefaultAddressFamily())); |
| |
| DictionaryValue* cache_info_dict = new DictionaryValue(); |
| |
| cache_info_dict->SetInteger( |
| "capacity", |
| static_cast<int>(cache->max_entries())); |
| cache_info_dict->SetInteger( |
| "ttl_success_ms", |
| static_cast<int>(cache->success_entry_ttl().InMilliseconds())); |
| cache_info_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", |
| net::NetLog::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); |
| } |
| |
| cache_info_dict->Set("entries", entry_list); |
| dict->Set("cache", cache_info_dict); |
| |
| CallJavascriptFunction(L"g_browser.receivedHostResolverInfo", 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. |
| OnGetHostResolverInfo(NULL); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::OnEnableIPv6( |
| const ListValue* list) { |
| net::URLRequestContext* context = context_getter_->GetURLRequestContext(); |
| net::HostResolverImpl* host_resolver_impl = |
| context->host_resolver()->GetAsHostResolverImpl(); |
| |
| if (host_resolver_impl) { |
| host_resolver_impl->SetDefaultAddressFamily( |
| net::ADDRESS_FAMILY_UNSPECIFIED); |
| } |
| |
| // Cause the renderer to be notified of the new value. |
| OnGetHostResolverInfo(NULL); |
| } |
| |
| 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, io_thread_->globals()->proxy_script_fetcher_context.get())); |
| connection_tester_->RunAllTests(url); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::OnHSTSQuery( |
| const ListValue* list) { |
| // |list| should be: [<domain to query>]. |
| std::string domain; |
| CHECK(list->GetString(0, &domain)); |
| DictionaryValue* result = new(DictionaryValue); |
| |
| if (!IsStringASCII(domain)) { |
| result->SetString("error", "non-ASCII domain name"); |
| } else { |
| net::TransportSecurityState* transport_security_state = |
| context_getter_->GetURLRequestContext()->transport_security_state(); |
| if (!transport_security_state) { |
| result->SetString("error", "no TransportSecurityState active"); |
| } else { |
| net::TransportSecurityState::DomainState state; |
| const bool found = transport_security_state->IsEnabledForHost( |
| &state, domain, true); |
| |
| result->SetBoolean("result", found); |
| if (found) { |
| result->SetInteger("mode", static_cast<int>(state.mode)); |
| result->SetBoolean("subdomains", state.include_subdomains); |
| result->SetBoolean("preloaded", state.preloaded); |
| result->SetString("domain", state.domain); |
| |
| std::vector<std::string> parts; |
| for (std::vector<net::SHA1Fingerprint>::const_iterator |
| i = state.public_key_hashes.begin(); |
| i != state.public_key_hashes.end(); i++) { |
| std::string part = "sha1/"; |
| std::string hash_str(reinterpret_cast<const char*>(i->data), |
| sizeof(i->data)); |
| std::string b64; |
| base::Base64Encode(hash_str, &b64); |
| part += b64; |
| parts.push_back(part); |
| } |
| result->SetString("public_key_hashes", JoinString(parts, ',')); |
| } |
| } |
| } |
| |
| CallJavascriptFunction(L"g_browser.receivedHSTSResult", result); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::OnHSTSAdd( |
| const ListValue* list) { |
| // |list| should be: [<domain to query>, <include subdomains>, <cert pins>]. |
| std::string domain; |
| CHECK(list->GetString(0, &domain)); |
| if (!IsStringASCII(domain)) { |
| // Silently fail. The user will get a helpful error if they query for the |
| // name. |
| return; |
| } |
| bool include_subdomains; |
| CHECK(list->GetBoolean(1, &include_subdomains)); |
| std::string hashes_str; |
| CHECK(list->GetString(2, &hashes_str)); |
| |
| net::TransportSecurityState* transport_security_state = |
| context_getter_->GetURLRequestContext()->transport_security_state(); |
| if (!transport_security_state) |
| return; |
| |
| net::TransportSecurityState::DomainState state; |
| state.expiry = state.created + base::TimeDelta::FromDays(1000); |
| state.include_subdomains = include_subdomains; |
| state.public_key_hashes.clear(); |
| if (!hashes_str.empty()) { |
| std::vector<std::string> type_and_b64s; |
| base::SplitString(hashes_str, ',', &type_and_b64s); |
| for (std::vector<std::string>::const_iterator |
| i = type_and_b64s.begin(); i != type_and_b64s.end(); i++) { |
| std::string type_and_b64; |
| RemoveChars(*i, " \t\r\n", &type_and_b64); |
| if (type_and_b64.find("sha1/") != 0) |
| continue; |
| std::string b64 = type_and_b64.substr(5, type_and_b64.size() - 5); |
| std::string hash_str; |
| if (!base::Base64Decode(b64, &hash_str)) |
| continue; |
| net::SHA1Fingerprint hash; |
| if (hash_str.size() != sizeof(hash.data)) |
| continue; |
| memcpy(hash.data, hash_str.data(), sizeof(hash.data)); |
| state.public_key_hashes.push_back(hash); |
| } |
| } |
| |
| transport_security_state->EnableHost(domain, state); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::OnHSTSDelete( |
| const ListValue* list) { |
| // |list| should be: [<domain to query>]. |
| std::string domain; |
| CHECK(list->GetString(0, &domain)); |
| if (!IsStringASCII(domain)) { |
| // There cannot be a unicode entry in the HSTS set. |
| return; |
| } |
| net::TransportSecurityState* transport_security_state = |
| context_getter_->GetURLRequestContext()->transport_security_state(); |
| if (!transport_security_state) |
| return; |
| |
| transport_security_state->DeleteHost(domain); |
| } |
| |
| 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::OnGetSocketPoolInfo( |
| const ListValue* list) { |
| net::HttpNetworkSession* http_network_session = |
| GetHttpNetworkSession(context_getter_->GetURLRequestContext()); |
| |
| Value* socket_pool_info = NULL; |
| if (http_network_session) |
| socket_pool_info = http_network_session->SocketPoolInfoToValue(); |
| |
| CallJavascriptFunction(L"g_browser.receivedSocketPoolInfo", socket_pool_info); |
| } |
| |
| |
| void NetInternalsMessageHandler::IOThreadImpl::OnFlushSocketPools( |
| const ListValue* list) { |
| net::HttpNetworkSession* http_network_session = |
| GetHttpNetworkSession(context_getter_->GetURLRequestContext()); |
| |
| if (http_network_session) |
| http_network_session->CloseAllConnections(); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::OnCloseIdleSockets( |
| const ListValue* list) { |
| net::HttpNetworkSession* http_network_session = |
| GetHttpNetworkSession(context_getter_->GetURLRequestContext()); |
| |
| if (http_network_session) |
| http_network_session->CloseIdleConnections(); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::OnGetSpdySessionInfo( |
| const ListValue* list) { |
| net::HttpNetworkSession* http_network_session = |
| GetHttpNetworkSession(context_getter_->GetURLRequestContext()); |
| |
| Value* spdy_info = NULL; |
| if (http_network_session) { |
| spdy_info = http_network_session->SpdySessionPoolInfoToValue(); |
| } |
| |
| CallJavascriptFunction(L"g_browser.receivedSpdySessionInfo", spdy_info); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::OnGetSpdyStatus( |
| const ListValue* list) { |
| DictionaryValue* status_dict = new DictionaryValue(); |
| |
| status_dict->Set("spdy_enabled", |
| Value::CreateBooleanValue( |
| net::HttpStreamFactory::spdy_enabled())); |
| status_dict->Set("use_alternate_protocols", |
| Value::CreateBooleanValue( |
| net::HttpStreamFactory::use_alternate_protocols())); |
| status_dict->Set("force_spdy_over_ssl", |
| Value::CreateBooleanValue( |
| net::HttpStreamFactory::force_spdy_over_ssl())); |
| status_dict->Set("force_spdy_always", |
| Value::CreateBooleanValue( |
| net::HttpStreamFactory::force_spdy_always())); |
| status_dict->Set("next_protos", |
| Value::CreateStringValue( |
| *net::HttpStreamFactory::next_protos())); |
| |
| CallJavascriptFunction(L"g_browser.receivedSpdyStatus", status_dict); |
| } |
| |
| void |
| NetInternalsMessageHandler::IOThreadImpl::OnGetSpdyAlternateProtocolMappings( |
| const ListValue* list) { |
| net::HttpNetworkSession* http_network_session = |
| GetHttpNetworkSession(context_getter_->GetURLRequestContext()); |
| |
| ListValue* dict_list = new ListValue(); |
| |
| if (http_network_session) { |
| const net::HttpAlternateProtocols& http_alternate_protocols = |
| http_network_session->alternate_protocols(); |
| const net::HttpAlternateProtocols::ProtocolMap& map = |
| http_alternate_protocols.protocol_map(); |
| |
| for (net::HttpAlternateProtocols::ProtocolMap::const_iterator it = |
| map.begin(); |
| it != map.end(); ++it) { |
| DictionaryValue* dict = new DictionaryValue(); |
| dict->SetString("host_port_pair", it->first.ToString()); |
| dict->SetString("alternate_protocol", it->second.ToString()); |
| dict_list->Append(dict); |
| } |
| } |
| |
| CallJavascriptFunction(L"g_browser.receivedSpdyAlternateProtocolMappings", |
| dict_list); |
| } |
| |
| #ifdef OS_WIN |
| void NetInternalsMessageHandler::IOThreadImpl::OnGetServiceProviders( |
| const ListValue* list) { |
| |
| DictionaryValue* service_providers = new DictionaryValue(); |
| |
| WinsockLayeredServiceProviderList layered_providers; |
| GetWinsockLayeredServiceProviders(&layered_providers); |
| ListValue* layered_provider_list = new ListValue(); |
| for (size_t i = 0; i < layered_providers.size(); ++i) { |
| DictionaryValue* service_dict = new DictionaryValue(); |
| service_dict->SetString("name", layered_providers[i].name); |
| service_dict->SetInteger("version", layered_providers[i].version); |
| service_dict->SetInteger("chain_length", layered_providers[i].chain_length); |
| service_dict->SetInteger("socket_type", layered_providers[i].socket_type); |
| service_dict->SetInteger("socket_protocol", |
| layered_providers[i].socket_protocol); |
| service_dict->SetString("path", layered_providers[i].path); |
| |
| layered_provider_list->Append(service_dict); |
| } |
| service_providers->Set("service_providers", layered_provider_list); |
| |
| WinsockNamespaceProviderList namespace_providers; |
| GetWinsockNamespaceProviders(&namespace_providers); |
| ListValue* namespace_list = new ListValue; |
| for (size_t i = 0; i < namespace_providers.size(); ++i) { |
| DictionaryValue* namespace_dict = new DictionaryValue(); |
| namespace_dict->SetString("name", namespace_providers[i].name); |
| namespace_dict->SetBoolean("active", namespace_providers[i].active); |
| namespace_dict->SetInteger("version", namespace_providers[i].version); |
| namespace_dict->SetInteger("type", namespace_providers[i].type); |
| |
| namespace_list->Append(namespace_dict); |
| } |
| service_providers->Set("namespace_providers", namespace_list); |
| |
| CallJavascriptFunction(L"g_browser.receivedServiceProviders", |
| service_providers); |
| } |
| #endif |
| |
| void NetInternalsMessageHandler::IOThreadImpl::OnSetLogLevel( |
| const ListValue* list) { |
| int log_level; |
| std::string log_level_string; |
| if (!list->GetString(0, &log_level_string) || |
| !base::StringToInt(log_level_string, &log_level)) { |
| NOTREACHED(); |
| return; |
| } |
| |
| DCHECK_GE(log_level, net::NetLog::LOG_ALL); |
| DCHECK_LE(log_level, net::NetLog::LOG_BASIC); |
| SetLogLevel(static_cast<net::NetLog::LogLevel>(log_level)); |
| } |
| |
| // Note that unlike other methods of IOThreadImpl, this function |
| // can be called from ANY THREAD. |
| 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) { |
| BrowserThread::PostTask( |
| BrowserThread::IO, FROM_HERE, |
| NewRunnableMethod( |
| this, &IOThreadImpl::AddEntryToQueue, |
| net::NetLog::EntryToDictionaryValue(type, time, source, phase, |
| params, false))); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::AddEntryToQueue(Value* entry) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| if (!pending_entries_.get()) { |
| pending_entries_.reset(new ListValue()); |
| BrowserThread::PostDelayedTask( |
| BrowserThread::IO, FROM_HERE, |
| NewRunnableMethod(this, &IOThreadImpl::PostPendingEntries), |
| kNetLogEventDelayMilliseconds); |
| } |
| pending_entries_->Append(entry); |
| } |
| |
| void NetInternalsMessageHandler::IOThreadImpl::PostPendingEntries() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| CallJavascriptFunction( |
| L"g_browser.receivedLogEntries", |
| pending_entries_.release()); |
| } |
| |
| 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(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| (this->*method)(arg); |
| delete arg; |
| } |
| |
| // Note that this can be called from ANY THREAD. |
| void NetInternalsMessageHandler::IOThreadImpl::CallJavascriptFunction( |
| const std::wstring& function_name, |
| Value* arg) { |
| if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
| if (handler_ && !was_webui_deleted_) { |
| // 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; |
| } |
| |
| if (!BrowserThread::PostTask( |
| BrowserThread::UI, FROM_HERE, |
| NewRunnableMethod( |
| this, |
| &IOThreadImpl::CallJavascriptFunction, |
| function_name, arg))) { |
| // Failed posting the task, avoid leaking. |
| delete arg; |
| } |
| } |
| |
| } // namespace |
| |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // NetInternalsUI |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| NetInternalsUI::NetInternalsUI(TabContents* contents) : WebUI(contents) { |
| AddMessageHandler((new NetInternalsMessageHandler())->Attach(this)); |
| |
| NetInternalsHTMLSource* html_source = new NetInternalsHTMLSource(); |
| |
| // Set up the chrome://net-internals/ source. |
| contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source); |
| } |