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() { |