Index: chrome/browser/ui/webui/tracing_ui.cc |
diff --git a/chrome/browser/ui/webui/tracing_ui.cc b/chrome/browser/ui/webui/tracing_ui.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f91a7a8a80391c813f18c0699be0f8717de68a2f |
--- /dev/null |
+++ b/chrome/browser/ui/webui/tracing_ui.cc |
@@ -0,0 +1,376 @@ |
+// 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/tracing_ui.h" |
+ |
+#include <string> |
+ |
+#include "base/command_line.h" |
+#include "base/file_util.h" |
+#include "base/scoped_ptr.h" |
+#include "base/string_number_conversions.h" |
+#include "base/utf_string_conversions.h" |
+#include "chrome/browser/profiles/profile.h" |
+#include "chrome/browser/ui/shell_dialogs.h" |
+#include "chrome/browser/ui/webui/chrome_web_ui_data_source.h" |
+#include "chrome/common/chrome_version_info.h" |
+#include "chrome/common/url_constants.h" |
+#include "content/browser/gpu/gpu_data_manager.h" |
+#include "content/browser/renderer_host/render_view_host.h" |
+#include "content/browser/tab_contents/tab_contents.h" |
+#include "content/browser/tab_contents/tab_contents_view.h" |
+#include "content/browser/trace_controller.h" |
+#include "grit/browser_resources.h" |
+#include "grit/generated_resources.h" |
+#include "ui/base/l10n/l10n_util.h" |
+ |
+namespace { |
+ |
+ChromeWebUIDataSource* CreateTracingHTMLSource() { |
+ ChromeWebUIDataSource* source = |
+ new ChromeWebUIDataSource(chrome::kChromeUITracingHost); |
+ |
+ source->set_json_path("strings.js"); |
+ source->add_resource_path("tracing.js", IDR_TRACING_JS); |
+ source->set_default_resource(IDR_TRACING_HTML); |
+ return source; |
+} |
+ |
+// 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. |
+class TracingMessageHandler |
+ : public WebUIMessageHandler, |
+ public SelectFileDialog::Listener, |
+ public base::SupportsWeakPtr<TracingMessageHandler>, |
+ public TraceSubscriber { |
+ public: |
+ TracingMessageHandler(); |
+ virtual ~TracingMessageHandler(); |
+ |
+ // WebUIMessageHandler implementation. |
+ virtual WebUIMessageHandler* Attach(WebUI* web_ui); |
+ virtual void RegisterMessages(); |
+ |
+ // Mesages |
James Hawkins
2011/08/04 17:26:35
You're intermingling added methods with interface
dominich
2011/08/04 22:56:12
Done.
|
+ void OnBeginTracing(const ListValue* list); |
+ void OnEndTracingAsync(const ListValue* list); |
+ void OnBeginRequestBufferPercentFull(const ListValue* list); |
+ void OnLoadTraceFile(const ListValue* list); |
+ void OnSaveTraceFile(const ListValue* list); |
+ |
+ // SelectFileDialog::Listener implementation |
+ virtual void FileSelected(const FilePath& path, int index, void* params); |
+ virtual void FileSelectionCanceled(void* params); |
+ |
+ // Callbacks. |
+ void LoadTraceFileComplete(std::string* file_contents); |
+ void SaveTraceFileComplete(); |
+ |
+ // TraceSubscriber implementation. |
+ virtual void OnEndTracingComplete(); |
+ virtual void OnTraceDataCollected(const std::string& json_events); |
+ virtual void OnTraceBufferPercentFullReply(float percent_full); |
+ |
+ // 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: |
+ DISALLOW_COPY_AND_ASSIGN(TracingMessageHandler); |
James Hawkins
2011/08/04 17:26:35
This should be at the bottom of the class.
dominich
2011/08/04 22:56:12
Done.
|
+ |
+ scoped_refptr<SelectFileDialog> select_trace_file_dialog_; |
James Hawkins
2011/08/04 17:26:35
Document vars.
dominich
2011/08/04 22:56:12
Done.
|
+ SelectFileDialog::Type select_trace_file_dialog_type_; |
+ scoped_ptr<std::string> trace_data_to_save_; |
+ |
+ bool trace_enabled_; |
+}; |
+ |
+class TaskProxy : public base::RefCountedThreadSafe<TaskProxy> { |
James Hawkins
2011/08/04 17:26:35
Document this class.
dominich
2011/08/04 22:56:12
Done.
|
+ public: |
+ explicit TaskProxy(const base::WeakPtr<TracingMessageHandler>& handler) |
+ : handler_(handler) {} |
+ void LoadTraceFileCompleteProxy(std::string* file_contents) { |
+ if (handler_) |
+ handler_->LoadTraceFileComplete(file_contents); |
+ delete file_contents; |
+ } |
+ |
+ void SaveTraceFileCompleteProxy() { |
+ if (handler_) |
+ handler_->SaveTraceFileComplete(); |
+ } |
+ |
+ private: |
+ base::WeakPtr<TracingMessageHandler> handler_; |
James Hawkins
2011/08/04 17:26:35
Document vars.
dominich
2011/08/04 22:56:12
Done.
|
+ friend class base::RefCountedThreadSafe<TaskProxy>; |
+ DISALLOW_COPY_AND_ASSIGN(TaskProxy); |
+}; |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+// |
+// TracingMessageHandler |
+// |
+//////////////////////////////////////////////////////////////////////////////// |
+ |
+TracingMessageHandler::TracingMessageHandler() |
+ : select_trace_file_dialog_type_(SelectFileDialog::SELECT_NONE), |
+ trace_enabled_(false) { |
+} |
+ |
+TracingMessageHandler::~TracingMessageHandler() { |
+ if (select_trace_file_dialog_) |
+ select_trace_file_dialog_->ListenerDestroyed(); |
+ |
+ // If we are the current subscriber, this will result in ending tracing. |
+ TraceController::GetInstance()->CancelSubscriber(this); |
+} |
+ |
+WebUIMessageHandler* TracingMessageHandler::Attach(WebUI* web_ui) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ WebUIMessageHandler* result = WebUIMessageHandler::Attach(web_ui); |
+ return result; |
+} |
+ |
+void TracingMessageHandler::RegisterMessages() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ |
+ web_ui_->RegisterMessageCallback( |
+ "beginTracing", |
+ NewCallback(this, &TracingMessageHandler::OnBeginTracing)); |
+ web_ui_->RegisterMessageCallback( |
+ "endTracingAsync", |
+ NewCallback(this, &TracingMessageHandler::OnEndTracingAsync)); |
+ web_ui_->RegisterMessageCallback( |
+ "beginRequestBufferPercentFull", |
+ NewCallback(this, |
+ &TracingMessageHandler::OnBeginRequestBufferPercentFull)); |
+ web_ui_->RegisterMessageCallback( |
+ "loadTraceFile", |
+ NewCallback(this, &TracingMessageHandler::OnLoadTraceFile)); |
+ web_ui_->RegisterMessageCallback( |
+ "saveTraceFile", |
+ NewCallback(this, &TracingMessageHandler::OnSaveTraceFile)); |
+} |
+ |
+void TracingMessageHandler::OnBeginRequestBufferPercentFull( |
+ const ListValue* list) { |
+ TraceController::GetInstance()->GetTraceBufferPercentFullAsync(this); |
+} |
+ |
+class ReadTraceFileTask : public Task { |
+ public: |
+ ReadTraceFileTask(TaskProxy* proxy, const FilePath& path) |
+ : proxy_(proxy) |
+ , path_(path) {} |
James Hawkins
2011/08/04 17:26:35
Comma must be on the previous line.
dominich
2011/08/04 22:56:12
Done.
|
+ |
+ virtual void Run() { |
+ std::string* file_contents = new std::string(); |
+ if (!file_util::ReadFileToString(path_, file_contents)) { |
+ delete file_contents; |
+ return; |
+ } |
+ BrowserThread::PostTask( |
+ BrowserThread::UI, FROM_HERE, |
+ NewRunnableMethod(proxy_.get(), |
+ &TaskProxy::LoadTraceFileCompleteProxy, |
+ file_contents)); |
+ } |
+ |
+ private: |
+ scoped_refptr<TaskProxy> proxy_; |
+ |
+ // Path of the file to open. |
+ const FilePath path_; |
+}; |
+ |
+class WriteTraceFileTask : public Task { |
James Hawkins
2011/08/04 17:26:35
Document class.
dominich
2011/08/04 22:56:12
Done.
|
+ public: |
+ WriteTraceFileTask(TaskProxy* proxy, |
+ const FilePath& path, |
+ std::string* contents) |
+ : proxy_(proxy) |
+ , path_(path) |
+ , contents_(contents) {} |
+ |
+ virtual void Run() { |
+ if (!file_util::WriteFile(path_, contents_->c_str(), contents_->size())) |
+ return; |
+ BrowserThread::PostTask( |
+ BrowserThread::UI, FROM_HERE, |
+ NewRunnableMethod(proxy_.get(), |
+ &TaskProxy::SaveTraceFileCompleteProxy)); |
+ } |
+ |
+ private: |
+ scoped_refptr<TaskProxy> proxy_; |
+ |
+ // Path of the file to save. |
+ const FilePath path_; |
+ |
+ // What to save |
+ scoped_ptr<std::string> contents_; |
+}; |
+ |
+void TracingMessageHandler::FileSelected( |
+ const FilePath& path, int index, void* params) { |
+ if (select_trace_file_dialog_type_ == SelectFileDialog::SELECT_OPEN_FILE) |
+ BrowserThread::PostTask( |
+ BrowserThread::FILE, FROM_HERE, |
+ new ReadTraceFileTask(new TaskProxy(AsWeakPtr()), path)); |
+ else |
+ BrowserThread::PostTask( |
+ BrowserThread::FILE, FROM_HERE, |
+ new WriteTraceFileTask(new TaskProxy(AsWeakPtr()), path, |
+ trace_data_to_save_.release())); |
+ select_trace_file_dialog_.release(); |
+} |
+ |
+void TracingMessageHandler::FileSelectionCanceled(void* params) { |
+ select_trace_file_dialog_.release(); |
+ if (select_trace_file_dialog_type_ == SelectFileDialog::SELECT_OPEN_FILE) { |
+ web_ui_->CallJavascriptFunction( |
+ "tracingController.onLoadTraceFileCanceled"); |
+ } else { |
+ web_ui_->CallJavascriptFunction( |
+ "tracingController.onSaveTraceFileCanceled"); |
+ } |
+} |
+ |
+void TracingMessageHandler::OnLoadTraceFile(const ListValue* list) { |
+ // Only allow a single dialog at a time. |
+ if (select_trace_file_dialog_.get()) |
+ return; |
+ select_trace_file_dialog_type_ = SelectFileDialog::SELECT_OPEN_FILE; |
+ select_trace_file_dialog_ = SelectFileDialog::Create(this); |
+ select_trace_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 TracingMessageHandler::LoadTraceFileComplete(std::string* file_contents) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ std::wstring javascript; |
+ javascript += L"tracingController.onLoadTraceFileComplete("; |
+ javascript += UTF8ToWide(*file_contents); |
+ javascript += L");"; |
+ |
+ web_ui_->GetRenderViewHost()->ExecuteJavascriptInWebFrame(string16(), |
+ WideToUTF16Hack(javascript)); |
+} |
+ |
+void TracingMessageHandler::OnSaveTraceFile(const ListValue* list) { |
+ // Only allow a single dialog at a time. |
+ if (select_trace_file_dialog_.get()) |
+ return; |
+ |
+ DCHECK(list->GetSize() == 1); |
+ |
+ std::string* trace_data = new std::string(); |
+ bool ok = list->GetString(0, trace_data); |
+ DCHECK(ok); |
+ trace_data_to_save_.reset(trace_data); |
+ |
+ select_trace_file_dialog_type_ = SelectFileDialog::SELECT_SAVEAS_FILE; |
+ select_trace_file_dialog_ = SelectFileDialog::Create(this); |
+ select_trace_file_dialog_->SelectFile( |
+ SelectFileDialog::SELECT_SAVEAS_FILE, |
+ string16(), |
+ FilePath(), |
+ NULL, 0, FILE_PATH_LITERAL(""), web_ui_->tab_contents(), |
+ web_ui_->tab_contents()->view()->GetTopLevelNativeWindow(), NULL); |
+} |
+ |
+void TracingMessageHandler::SaveTraceFileComplete() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ std::wstring javascript; |
+ web_ui_->CallJavascriptFunction("tracingController.onSaveTraceFileComplete"); |
+} |
+ |
+DictionaryValue* NewDescriptionValuePair(const std::string& desc, |
+ const std::string& value) { |
+ DictionaryValue* dict = new DictionaryValue(); |
+ dict->SetString("description", desc); |
+ dict->SetString("value", value); |
+ return dict; |
+} |
+ |
+DictionaryValue* NewDescriptionValuePair(const std::string& desc, |
+ Value* value) { |
+ DictionaryValue* dict = new DictionaryValue(); |
+ dict->SetString("description", desc); |
+ dict->Set("value", value); |
+ return dict; |
+} |
+ |
+void TracingMessageHandler::OnBeginTracing(const ListValue* args) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ trace_enabled_ = true; |
+ // TODO(jbates) This may fail, but that's OK for current use cases. |
+ // Ex: Multiple about:gpu traces can not trace simultaneously. |
+ // TODO(nduca) send feedback to javascript about whether or not BeginTracing |
+ // was successful. |
+ TraceController::GetInstance()->BeginTracing(this); |
+} |
+ |
+void TracingMessageHandler::OnEndTracingAsync(const ListValue* list) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ |
+ // TODO(nduca): fix javascript code to make sure trace_enabled_ is always true |
+ // here. triggered a false condition by just clicking stop |
+ // trace a few times when it was going slow, and maybe switching |
+ // between tabs. |
+ if (trace_enabled_ && |
+ !TraceController::GetInstance()->EndTracingAsync(this)) { |
+ // Set to false now, since it turns out we never were the trace subscriber. |
+ OnEndTracingComplete(); |
+ } |
+} |
+ |
+void TracingMessageHandler::OnEndTracingComplete() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ trace_enabled_ = false; |
+ web_ui_->CallJavascriptFunction("tracingController.onEndTracingComplete"); |
+} |
+ |
+void TracingMessageHandler::OnTraceDataCollected( |
+ const std::string& json_events) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ std::wstring javascript; |
+ javascript += L"tracingController.onTraceDataCollected("; |
+ javascript += UTF8ToWide(json_events); |
+ javascript += L");"; |
+ |
+ web_ui_->GetRenderViewHost()->ExecuteJavascriptInWebFrame(string16(), |
+ WideToUTF16Hack(javascript)); |
+} |
+ |
+void TracingMessageHandler::OnTraceBufferPercentFullReply(float percent_full) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ web_ui_->CallJavascriptFunction( |
+ "tracingController.onRequestBufferPercentFullComplete", |
+ *scoped_ptr<Value>(Value::CreateDoubleValue(percent_full))); |
+} |
+ |
+} // namespace |
+ |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+// |
+// TracingUI |
+// |
+//////////////////////////////////////////////////////////////////////////////// |
+ |
+TracingUI::TracingUI(TabContents* contents) : ChromeWebUI(contents) { |
+ printf("TracingUI::TracingUI\n"); |
+ AddMessageHandler((new TracingMessageHandler())->Attach(this)); |
+ |
+ // Set up the chrome://tracing/ source. |
+ Profile::FromBrowserContext(contents->browser_context())-> |
+ GetChromeURLDataManager()->AddDataSource(CreateTracingHTMLSource()); |
+} |