Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(474)

Side by Side Diff: chrome/browser/ui/webui/tracing_ui.cc

Issue 12088099: Moving chrome://tracing to content. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/browser/ui/webui/tracing_ui.h ('k') | chrome/chrome_browser_ui.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/webui/tracing_ui.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/command_line.h"
12 #include "base/debug/trace_event.h"
13 #include "base/file_util.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/string_number_conversions.h"
16 #include "base/stringprintf.h"
17 #include "base/utf_string_conversions.h"
18 #include "base/values.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/ui/chrome_select_file_policy.h"
21 #include "chrome/common/chrome_version_info.h"
22 #include "chrome/common/url_constants.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/render_view_host.h"
25 #include "content/public/browser/trace_controller.h"
26 #include "content/public/browser/trace_subscriber.h"
27 #include "content/public/browser/web_contents.h"
28 #include "content/public/browser/web_contents_view.h"
29 #include "content/public/browser/web_ui.h"
30 #include "content/public/browser/web_ui_data_source.h"
31 #include "content/public/browser/web_ui_message_handler.h"
32 #include "grit/browser_resources.h"
33 #include "grit/generated_resources.h"
34 #include "ipc/ipc_channel.h"
35 #include "ui/base/l10n/l10n_util.h"
36 #include "ui/shell_dialogs/select_file_dialog.h"
37
38 #if defined(OS_CHROMEOS)
39 #include "chromeos/dbus/dbus_thread_manager.h"
40 #include "chromeos/dbus/debug_daemon_client.h"
41 #endif
42
43 using content::BrowserThread;
44 using content::TraceController;
45 using content::WebContents;
46 using content::WebUIMessageHandler;
47
48 namespace {
49
50 content::WebUIDataSource* CreateTracingHTMLSource() {
51 content::WebUIDataSource* source =
52 content::WebUIDataSource::Create(chrome::kChromeUITracingHost);
53
54 source->SetJsonPath("strings.js");
55 source->SetDefaultResource(IDR_TRACING_HTML);
56 source->AddResourcePath("tracing.js", IDR_TRACING_JS);
57 source->AddLocalizedString("tracingTitle", IDS_TRACING_TITLE);
58 return source;
59 }
60
61 // This class receives javascript messages from the renderer.
62 // Note that the WebUI infrastructure runs on the UI thread, therefore all of
63 // this class's methods are expected to run on the UI thread.
64 class TracingMessageHandler
65 : public WebUIMessageHandler,
66 public ui::SelectFileDialog::Listener,
67 public base::SupportsWeakPtr<TracingMessageHandler>,
68 public content::TraceSubscriber {
69 public:
70 TracingMessageHandler();
71 virtual ~TracingMessageHandler();
72
73 // WebUIMessageHandler implementation.
74 virtual void RegisterMessages();
75
76 // SelectFileDialog::Listener implementation
77 virtual void FileSelected(const FilePath& path, int index, void* params);
78 virtual void FileSelectionCanceled(void* params);
79
80 // TraceSubscriber implementation.
81 virtual void OnEndTracingComplete();
82 virtual void OnTraceDataCollected(
83 const scoped_refptr<base::RefCountedString>& trace_fragment);
84 virtual void OnTraceBufferPercentFullReply(float percent_full);
85
86 // Messages.
87 void OnTracingControllerInitialized(const ListValue* list);
88 void OnBeginTracing(const ListValue* list);
89 void OnEndTracingAsync(const ListValue* list);
90 void OnBeginRequestBufferPercentFull(const ListValue* list);
91 void OnLoadTraceFile(const ListValue* list);
92 void OnSaveTraceFile(const ListValue* list);
93
94 // Callbacks.
95 void LoadTraceFileComplete(string16* file_contents);
96 void SaveTraceFileComplete();
97
98 private:
99 // The file dialog to select a file for loading or saving traces.
100 scoped_refptr<ui::SelectFileDialog> select_trace_file_dialog_;
101
102 // The type of the file dialog as the same one is used for loading or saving
103 // traces.
104 ui::SelectFileDialog::Type select_trace_file_dialog_type_;
105
106 // The trace data that is to be written to the file on saving.
107 scoped_ptr<std::string> trace_data_to_save_;
108
109 // True while tracing is active.
110 bool trace_enabled_;
111
112 // True while system tracing is active.
113 bool system_trace_in_progress_;
114
115 void OnEndSystemTracingAck(
116 const scoped_refptr<base::RefCountedString>& events_str_ptr);
117
118 DISALLOW_COPY_AND_ASSIGN(TracingMessageHandler);
119 };
120
121 // A proxy passed to the Read and Write tasks used when loading or saving trace
122 // data.
123 class TaskProxy : public base::RefCountedThreadSafe<TaskProxy> {
124 public:
125 explicit TaskProxy(const base::WeakPtr<TracingMessageHandler>& handler)
126 : handler_(handler) {}
127 void LoadTraceFileCompleteProxy(string16* file_contents) {
128 if (handler_)
129 handler_->LoadTraceFileComplete(file_contents);
130 delete file_contents;
131 }
132
133 void SaveTraceFileCompleteProxy() {
134 if (handler_)
135 handler_->SaveTraceFileComplete();
136 }
137
138 private:
139 friend class base::RefCountedThreadSafe<TaskProxy>;
140 ~TaskProxy() {}
141
142 // The message handler to call callbacks on.
143 base::WeakPtr<TracingMessageHandler> handler_;
144
145 DISALLOW_COPY_AND_ASSIGN(TaskProxy);
146 };
147
148 ////////////////////////////////////////////////////////////////////////////////
149 //
150 // TracingMessageHandler
151 //
152 ////////////////////////////////////////////////////////////////////////////////
153
154 TracingMessageHandler::TracingMessageHandler()
155 : select_trace_file_dialog_type_(ui::SelectFileDialog::SELECT_NONE),
156 trace_enabled_(false),
157 system_trace_in_progress_(false) {
158 }
159
160 TracingMessageHandler::~TracingMessageHandler() {
161 if (select_trace_file_dialog_)
162 select_trace_file_dialog_->ListenerDestroyed();
163
164 // If we are the current subscriber, this will result in ending tracing.
165 TraceController::GetInstance()->CancelSubscriber(this);
166
167 // Shutdown any system tracing too.
168 if (system_trace_in_progress_) {
169 #if defined(OS_CHROMEOS)
170 chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->
171 RequestStopSystemTracing(
172 chromeos::DebugDaemonClient::EmptyStopSystemTracingCallback());
173 #endif
174 }
175 }
176
177 void TracingMessageHandler::RegisterMessages() {
178 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
179
180 web_ui()->RegisterMessageCallback("tracingControllerInitialized",
181 base::Bind(&TracingMessageHandler::OnTracingControllerInitialized,
182 base::Unretained(this)));
183 web_ui()->RegisterMessageCallback("beginTracing",
184 base::Bind(&TracingMessageHandler::OnBeginTracing,
185 base::Unretained(this)));
186 web_ui()->RegisterMessageCallback("endTracingAsync",
187 base::Bind(&TracingMessageHandler::OnEndTracingAsync,
188 base::Unretained(this)));
189 web_ui()->RegisterMessageCallback("beginRequestBufferPercentFull",
190 base::Bind(&TracingMessageHandler::OnBeginRequestBufferPercentFull,
191 base::Unretained(this)));
192 web_ui()->RegisterMessageCallback("loadTraceFile",
193 base::Bind(&TracingMessageHandler::OnLoadTraceFile,
194 base::Unretained(this)));
195 web_ui()->RegisterMessageCallback("saveTraceFile",
196 base::Bind(&TracingMessageHandler::OnSaveTraceFile,
197 base::Unretained(this)));
198 }
199
200 void TracingMessageHandler::OnTracingControllerInitialized(
201 const ListValue* args) {
202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
203
204 // Send the client info to the tracingController
205 {
206 scoped_ptr<DictionaryValue> dict(new DictionaryValue());
207 chrome::VersionInfo version_info;
208
209 if (!version_info.is_valid()) {
210 DLOG(ERROR) << "Unable to create chrome::VersionInfo";
211 } else {
212 // We have everything we need to send the right values.
213 dict->SetString("version", version_info.Version());
214 dict->SetString("cl", version_info.LastChange());
215 dict->SetString("version_mod",
216 chrome::VersionInfo::GetVersionStringModifier());
217 dict->SetString("official",
218 l10n_util::GetStringUTF16(
219 version_info.IsOfficialBuild() ?
220 IDS_ABOUT_VERSION_OFFICIAL :
221 IDS_ABOUT_VERSION_UNOFFICIAL));
222
223 dict->SetString("command_line",
224 CommandLine::ForCurrentProcess()->GetCommandLineString());
225 }
226
227 web_ui()->CallJavascriptFunction("tracingController.onClientInfoUpdate",
228 *dict);
229 }
230 }
231
232 void TracingMessageHandler::OnBeginRequestBufferPercentFull(
233 const ListValue* list) {
234 TraceController::GetInstance()->GetTraceBufferPercentFullAsync(this);
235 }
236
237 // A callback used for asynchronously reading a file to a string. Calls the
238 // TaskProxy callback when reading is complete.
239 void ReadTraceFileCallback(TaskProxy* proxy, const FilePath& path) {
240 std::string file_contents;
241 if (!file_util::ReadFileToString(path, &file_contents))
242 return;
243
244 // We need to escape the file contents, because it will go into a javascript
245 // quoted string in TracingMessageHandler::LoadTraceFileComplete. We need to
246 // escape control characters (to have well-formed javascript statements), as
247 // well as \ and ' (the only special characters in a ''-quoted string).
248 // Do the escaping on this thread, it may take a little while for big files
249 // and we don't want to block the UI during that time. Also do the UTF-16
250 // conversion here.
251 // Note: we're using UTF-16 because we'll need to cut the string into slices
252 // to give to Javascript, and it's easier to cut than UTF-8 (since JS strings
253 // are arrays of 16-bit values, UCS-2 really, whereas we can't cut inside of a
254 // multibyte UTF-8 codepoint).
255 size_t size = file_contents.size();
256 std::string escaped_contents;
257 escaped_contents.reserve(size);
258 for (size_t i = 0; i < size; ++i) {
259 char c = file_contents[i];
260 if (c < ' ') {
261 escaped_contents += base::StringPrintf("\\u%04x", c);
262 continue;
263 }
264 if (c == '\\' || c == '\'')
265 escaped_contents.push_back('\\');
266 escaped_contents.push_back(c);
267 }
268 file_contents.clear();
269
270 scoped_ptr<string16> contents16(new string16);
271 UTF8ToUTF16(escaped_contents).swap(*contents16);
272
273 BrowserThread::PostTask(
274 BrowserThread::UI, FROM_HERE,
275 base::Bind(&TaskProxy::LoadTraceFileCompleteProxy, proxy,
276 contents16.release()));
277 }
278
279 // A callback used for asynchronously writing a file from a string. Calls the
280 // TaskProxy callback when writing is complete.
281 void WriteTraceFileCallback(TaskProxy* proxy,
282 const FilePath& path,
283 std::string* contents) {
284 if (!file_util::WriteFile(path, contents->c_str(), contents->size()))
285 return;
286
287 BrowserThread::PostTask(
288 BrowserThread::UI, FROM_HERE,
289 base::Bind(&TaskProxy::SaveTraceFileCompleteProxy, proxy));
290 }
291
292 void TracingMessageHandler::FileSelected(
293 const FilePath& path, int index, void* params) {
294 if (select_trace_file_dialog_type_ ==
295 ui::SelectFileDialog::SELECT_OPEN_FILE) {
296 BrowserThread::PostTask(
297 BrowserThread::FILE, FROM_HERE,
298 base::Bind(&ReadTraceFileCallback,
299 make_scoped_refptr(new TaskProxy(AsWeakPtr())), path));
300 } else {
301 BrowserThread::PostTask(
302 BrowserThread::FILE, FROM_HERE,
303 base::Bind(&WriteTraceFileCallback,
304 make_scoped_refptr(new TaskProxy(AsWeakPtr())), path,
305 trace_data_to_save_.release()));
306 }
307
308 select_trace_file_dialog_ = NULL;
309 }
310
311 void TracingMessageHandler::FileSelectionCanceled(void* params) {
312 select_trace_file_dialog_ = NULL;
313 if (select_trace_file_dialog_type_ ==
314 ui::SelectFileDialog::SELECT_OPEN_FILE) {
315 web_ui()->CallJavascriptFunction(
316 "tracingController.onLoadTraceFileCanceled");
317 } else {
318 web_ui()->CallJavascriptFunction(
319 "tracingController.onSaveTraceFileCanceled");
320 }
321 }
322
323 void TracingMessageHandler::OnLoadTraceFile(const ListValue* list) {
324 // Only allow a single dialog at a time.
325 if (select_trace_file_dialog_.get())
326 return;
327 select_trace_file_dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_FILE;
328 select_trace_file_dialog_ = ui::SelectFileDialog::Create(
329 this, new ChromeSelectFilePolicy(web_ui()->GetWebContents()));
330 select_trace_file_dialog_->SelectFile(
331 ui::SelectFileDialog::SELECT_OPEN_FILE,
332 string16(),
333 FilePath(),
334 NULL, 0, FILE_PATH_LITERAL(""),
335 web_ui()->GetWebContents()->GetView()->GetTopLevelNativeWindow(), NULL);
336 }
337
338 void TracingMessageHandler::LoadTraceFileComplete(string16* contents) {
339 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
340
341 // We need to pass contents to tracingController.onLoadTraceFileComplete, but
342 // that may be arbitrarily big, and IPCs messages are limited in size. So we
343 // need to cut it into pieces and rebuild the string in Javascript.
344 // |contents| has already been escaped in ReadTraceFileCallback.
345 // IPC::Channel::kMaximumMessageSize is in bytes, and we need to account for
346 // overhead.
347 const size_t kMaxSize = IPC::Channel::kMaximumMessageSize / 2 - 128;
348 string16 first_prefix = UTF8ToUTF16("window.traceData = '");
349 string16 prefix = UTF8ToUTF16("window.traceData += '");
350 string16 suffix = UTF8ToUTF16("';");
351
352 content::RenderViewHost* rvh =
353 web_ui()->GetWebContents()->GetRenderViewHost();
354 for (size_t i = 0; i < contents->size(); i += kMaxSize) {
355 string16 javascript = i == 0 ? first_prefix : prefix;
356 javascript += contents->substr(i, kMaxSize) + suffix;
357 rvh->ExecuteJavascriptInWebFrame(string16(), javascript);
358 }
359 rvh->ExecuteJavascriptInWebFrame(string16(), UTF8ToUTF16(
360 "tracingController.onLoadTraceFileComplete(JSON.parse(window.traceData));"
361 "delete window.traceData;"));
362 }
363
364 void TracingMessageHandler::OnSaveTraceFile(const ListValue* list) {
365 // Only allow a single dialog at a time.
366 if (select_trace_file_dialog_.get())
367 return;
368
369 DCHECK(list->GetSize() == 1);
370
371 std::string* trace_data = new std::string();
372 bool ok = list->GetString(0, trace_data);
373 DCHECK(ok);
374 trace_data_to_save_.reset(trace_data);
375
376 select_trace_file_dialog_type_ = ui::SelectFileDialog::SELECT_SAVEAS_FILE;
377 select_trace_file_dialog_ = ui::SelectFileDialog::Create(
378 this, new ChromeSelectFilePolicy(web_ui()->GetWebContents()));
379 select_trace_file_dialog_->SelectFile(
380 ui::SelectFileDialog::SELECT_SAVEAS_FILE,
381 string16(),
382 FilePath(),
383 NULL, 0, FILE_PATH_LITERAL(""),
384 web_ui()->GetWebContents()->GetView()->GetTopLevelNativeWindow(), NULL);
385 }
386
387 void TracingMessageHandler::SaveTraceFileComplete() {
388 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
389 web_ui()->CallJavascriptFunction("tracingController.onSaveTraceFileComplete");
390 }
391
392 void TracingMessageHandler::OnBeginTracing(const ListValue* args) {
393 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
394 DCHECK_EQ(args->GetSize(), (size_t) 2);
395
396 bool system_tracing_requested = false;
397 bool ok = args->GetBoolean(0, &system_tracing_requested);
398 DCHECK(ok);
399
400 std::string chrome_categories;
401 ok = args->GetString(1, &chrome_categories);
402 DCHECK(ok);
403
404 trace_enabled_ = true;
405 // TODO(jbates) This may fail, but that's OK for current use cases.
406 // Ex: Multiple about:gpu traces can not trace simultaneously.
407 // TODO(nduca) send feedback to javascript about whether or not BeginTracing
408 // was successful.
409 TraceController::GetInstance()->BeginTracing(this, chrome_categories);
410
411 if (system_tracing_requested) {
412 #if defined(OS_CHROMEOS)
413 DCHECK(!system_trace_in_progress_);
414 chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->
415 StartSystemTracing();
416 // TODO(sleffler) async, could wait for completion
417 system_trace_in_progress_ = true;
418 #endif
419 }
420 }
421
422 void TracingMessageHandler::OnEndTracingAsync(const ListValue* list) {
423 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
424
425 // TODO(nduca): fix javascript code to make sure trace_enabled_ is always true
426 // here. triggered a false condition by just clicking stop
427 // trace a few times when it was going slow, and maybe switching
428 // between tabs.
429 if (trace_enabled_ &&
430 !TraceController::GetInstance()->EndTracingAsync(this)) {
431 // Set to false now, since it turns out we never were the trace subscriber.
432 OnEndTracingComplete();
433 }
434 }
435
436 void TracingMessageHandler::OnEndTracingComplete() {
437 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
438 trace_enabled_ = false;
439 if (system_trace_in_progress_) {
440 // Disable system tracing now that the local trace has shutdown.
441 // This must be done last because we potentially need to push event
442 // records into the system event log for synchronizing system event
443 // timestamps with chrome event timestamps--and since the system event
444 // log is a ring-buffer (on linux) adding them at the end is the only
445 // way we're confident we'll have them in the final result.
446 system_trace_in_progress_ = false;
447 #if defined(OS_CHROMEOS)
448 chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->
449 RequestStopSystemTracing(
450 base::Bind(&TracingMessageHandler::OnEndSystemTracingAck,
451 base::Unretained(this)));
452 return;
453 #endif
454 }
455 web_ui()->CallJavascriptFunction("tracingController.onEndTracingComplete");
456 }
457
458 void TracingMessageHandler::OnEndSystemTracingAck(
459 const scoped_refptr<base::RefCountedString>& events_str_ptr) {
460 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
461
462 web_ui()->CallJavascriptFunction(
463 "tracingController.onSystemTraceDataCollected",
464 *scoped_ptr<Value>(Value::CreateStringValue(events_str_ptr->data())));
465 DCHECK(!system_trace_in_progress_);
466
467 OnEndTracingComplete();
468 }
469
470 void TracingMessageHandler::OnTraceDataCollected(
471 const scoped_refptr<base::RefCountedString>& trace_fragment) {
472 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
473
474 base::debug::TraceResultBuffer::SimpleOutput output;
475 base::debug::TraceResultBuffer trace_buffer;
476 trace_buffer.SetOutputCallback(output.GetCallback());
477 output.Append("tracingController.onTraceDataCollected(");
478 trace_buffer.Start();
479 trace_buffer.AddFragment(trace_fragment->data());
480 trace_buffer.Finish();
481 output.Append(");");
482
483 web_ui()->GetWebContents()->GetRenderViewHost()->
484 ExecuteJavascriptInWebFrame(string16(), UTF8ToUTF16(output.json_output));
485 }
486
487 void TracingMessageHandler::OnTraceBufferPercentFullReply(float percent_full) {
488 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
489 web_ui()->CallJavascriptFunction(
490 "tracingController.onRequestBufferPercentFullComplete",
491 *scoped_ptr<Value>(Value::CreateDoubleValue(percent_full)));
492 }
493
494 } // namespace
495
496
497 ////////////////////////////////////////////////////////////////////////////////
498 //
499 // TracingUI
500 //
501 ////////////////////////////////////////////////////////////////////////////////
502
503 TracingUI::TracingUI(content::WebUI* web_ui) : WebUIController(web_ui) {
504 web_ui->AddMessageHandler(new TracingMessageHandler());
505
506 // Set up the chrome://tracing/ source.
507 Profile* profile = Profile::FromWebUI(web_ui);
508 content::WebUIDataSource::Add(profile, CreateTracingHTMLSource());
509 }
OLDNEW
« no previous file with comments | « chrome/browser/ui/webui/tracing_ui.h ('k') | chrome/chrome_browser_ui.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698