Chromium Code Reviews| Index: chrome/browser/dom_ui/net_internals_ui.cc |
| =================================================================== |
| --- chrome/browser/dom_ui/net_internals_ui.cc (revision 70414) |
| +++ chrome/browser/dom_ui/net_internals_ui.cc (working copy) |
| @@ -30,6 +30,9 @@ |
| #include "chrome/browser/net/url_fixer_upper.h" |
| #include "chrome/browser/platform_util.h" |
| #include "chrome/browser/profiles/profile.h" |
| +#include "chrome/browser/shell_dialogs.h" |
| +#include "chrome/browser/tab_contents/tab_contents.h" |
| +#include "chrome/browser/tab_contents/tab_contents_view.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "chrome/common/chrome_version_info.h" |
| #include "chrome/common/jstemplate_builder.h" |
| @@ -54,6 +57,11 @@ |
| 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(URLRequestContext* context) { |
| @@ -129,6 +137,7 @@ |
| // TODO(eroman): Can we start on the IO thread to begin with? |
| class NetInternalsMessageHandler |
| : public DOMMessageHandler, |
| + public SelectFileDialog::Listener, |
| public base::SupportsWeakPtr<NetInternalsMessageHandler> { |
| public: |
| NetInternalsMessageHandler(); |
| @@ -143,12 +152,43 @@ |
| void CallJavascriptFunction(const std::wstring& function_name, |
| const Value* value); |
| + // 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 |dom_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_; |
| + }; |
| + |
| // 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); |
| }; |
| @@ -236,18 +276,28 @@ |
| 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: |
| 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|. Note that this can be called from |
| - // any thread. |
| - void CallJavascriptFunction(const std::wstring& function_name, |
| - Value* arg); |
| + // 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_; |
| @@ -271,6 +321,11 @@ |
| // True if we have attached an observer to the NetLog already. |
| bool is_observing_log_; |
| friend class base::RefCountedThreadSafe<IOThreadImpl>; |
| + |
| + // 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 DOMUI::MessageCallback which when excuted calls |
| @@ -376,6 +431,8 @@ |
| BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
| NewRunnableMethod(proxy_.get(), &IOThreadImpl::Detach)); |
| } |
| + if (select_log_file_dialog_) |
| + select_log_file_dialog_->ListenerDestroyed(); |
| } |
| DOMMessageHandler* NetInternalsMessageHandler::Attach(DOMUI* dom_ui) { |
| @@ -386,8 +443,34 @@ |
| 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(""), |
| + dom_ui_->tab_contents()->view()->GetTopLevelNativeWindow(), NULL); |
| +} |
| + |
| void NetInternalsMessageHandler::RegisterMessages() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + // Only callback handled on UI thread. |
| + dom_ui_->RegisterMessageCallback("loadLogFile", |
|
eroman
2011/01/24 20:45:56
nit: can you break the line here, for consistency
mmenke
2011/01/25 20:08:13
Done. It also violated Google style guidelines.
|
| + NewCallback(this, &NetInternalsMessageHandler::OnLoadLogFile)); |
| dom_ui_->RegisterMessageCallback( |
| "notifyReady", |
| @@ -449,6 +532,25 @@ |
| //////////////////////////////////////////////////////////////////////////////// |
| // |
| +// 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 |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| @@ -951,10 +1053,31 @@ |
| 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.receivedLogEntry", |
| - net::NetLog::EntryToDictionaryValue(type, time, source, phase, params, |
| - false)); |
| + L"g_browser.receivedLogEntries", |
| + pending_entries_.release()); |
| } |
| void NetInternalsMessageHandler::IOThreadImpl::OnStartConnectionTestSuite() { |