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

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

Issue 7555005: Moving the contents of chrome://gpu Profiling to chrome://tracing. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Remove tabs Created 9 years, 4 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
OLDNEW
(Empty)
1 // Copyright (c) 2011 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/command_line.h"
10 #include "base/file_util.h"
11 #include "base/scoped_ptr.h"
12 #include "base/string_number_conversions.h"
13 #include "base/utf_string_conversions.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/ui/shell_dialogs.h"
16 #include "chrome/browser/ui/webui/chrome_web_ui_data_source.h"
17 #include "chrome/common/chrome_version_info.h"
18 #include "chrome/common/url_constants.h"
19 #include "content/browser/gpu/gpu_data_manager.h"
20 #include "content/browser/renderer_host/render_view_host.h"
21 #include "content/browser/tab_contents/tab_contents.h"
22 #include "content/browser/tab_contents/tab_contents_view.h"
23 #include "content/browser/trace_controller.h"
24 #include "grit/browser_resources.h"
25 #include "grit/generated_resources.h"
26 #include "ui/base/l10n/l10n_util.h"
27
28 namespace {
29
30 ChromeWebUIDataSource* CreateTracingHTMLSource() {
31 ChromeWebUIDataSource* source =
32 new ChromeWebUIDataSource(chrome::kChromeUITracingHost);
33
34 source->set_json_path("strings.js");
35 source->add_resource_path("tracing.js", IDR_TRACING_JS);
36 source->set_default_resource(IDR_TRACING_HTML);
37 return source;
38 }
39
40 // This class receives javascript messages from the renderer.
41 // Note that the WebUI infrastructure runs on the UI thread, therefore all of
42 // this class's methods are expected to run on the UI thread.
43 class TracingMessageHandler
44 : public WebUIMessageHandler,
45 public SelectFileDialog::Listener,
46 public base::SupportsWeakPtr<TracingMessageHandler>,
47 public TraceSubscriber {
48 public:
49 TracingMessageHandler();
50 virtual ~TracingMessageHandler();
51
52 // WebUIMessageHandler implementation.
53 virtual WebUIMessageHandler* Attach(WebUI* web_ui);
54 virtual void RegisterMessages();
55
56 // Mesages
57 void OnBeginTracing(const ListValue* list);
58 void OnEndTracingAsync(const ListValue* list);
59 void OnBrowserBridgeInitialized(const ListValue* list);
60 void OnCallAsync(const ListValue* list);
61 void OnBeginRequestBufferPercentFull(const ListValue* list);
62 void OnLoadTraceFile(const ListValue* list);
63 void OnSaveTraceFile(const ListValue* list);
64
65 // Submessages dispatched from OnCallAsync
66 Value* OnRequestClientInfo(const ListValue* list);
67 Value* OnRequestLogMessages(const ListValue* list);
68
69 // SelectFileDialog::Listener implementation
70 virtual void FileSelected(const FilePath& path, int index, void* params);
71 virtual void FileSelectionCanceled(void* params);
72
73 // Callbacks.
74 void OnGpuInfoUpdate();
75 void LoadTraceFileComplete(std::string* file_contents);
76 void SaveTraceFileComplete();
77
78 // TraceSubscriber implementation.
79 virtual void OnEndTracingComplete();
80 virtual void OnTraceDataCollected(const std::string& json_events);
81 virtual void OnTraceBufferPercentFullReply(float percent_full);
82
83 // Executes the javascript function |function_name| in the renderer, passing
84 // it the argument |value|.
85 void CallJavascriptFunction(const std::wstring& function_name,
86 const Value* value);
87
88 private:
89 DISALLOW_COPY_AND_ASSIGN(TracingMessageHandler);
90
91 // Cache the Singleton for efficiency.
92 GpuDataManager* gpu_data_manager_;
93
94 Callback0::Type* gpu_info_update_callback_;
95
96 scoped_refptr<SelectFileDialog> select_trace_file_dialog_;
97 SelectFileDialog::Type select_trace_file_dialog_type_;
98 scoped_ptr<std::string> trace_data_to_save_;
99
100 bool trace_enabled_;
101 };
102
103 class TaskProxy : public base::RefCountedThreadSafe<TaskProxy> {
104 public:
105 explicit TaskProxy(const base::WeakPtr<TracingMessageHandler>& handler)
106 : handler_(handler) {}
107 void LoadTraceFileCompleteProxy(std::string* file_contents) {
108 if (handler_)
109 handler_->LoadTraceFileComplete(file_contents);
110 delete file_contents;
111 }
112
113 void SaveTraceFileCompleteProxy() {
114 if (handler_)
115 handler_->SaveTraceFileComplete();
116 }
117
118 private:
119 base::WeakPtr<TracingMessageHandler> handler_;
120 friend class base::RefCountedThreadSafe<TaskProxy>;
121 DISALLOW_COPY_AND_ASSIGN(TaskProxy);
122 };
123
124 ////////////////////////////////////////////////////////////////////////////////
125 //
126 // TracingMessageHandler
127 //
128 ////////////////////////////////////////////////////////////////////////////////
129
130 TracingMessageHandler::TracingMessageHandler()
131 : gpu_info_update_callback_(NULL),
132 select_trace_file_dialog_type_(SelectFileDialog::SELECT_NONE),
133 trace_enabled_(false) {
134 gpu_data_manager_ = GpuDataManager::GetInstance();
135 DCHECK(gpu_data_manager_);
136 }
137
138 TracingMessageHandler::~TracingMessageHandler() {
139 if (gpu_info_update_callback_) {
140 gpu_data_manager_->RemoveGpuInfoUpdateCallback(gpu_info_update_callback_);
141 delete gpu_info_update_callback_;
142 }
143
144 if (select_trace_file_dialog_)
145 select_trace_file_dialog_->ListenerDestroyed();
146
147 // If we are the current subscriber, this will result in ending tracing.
148 TraceController::GetInstance()->CancelSubscriber(this);
149 }
150
151 WebUIMessageHandler* TracingMessageHandler::Attach(WebUI* web_ui) {
152 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
153 WebUIMessageHandler* result = WebUIMessageHandler::Attach(web_ui);
154 return result;
155 }
156
157 /* BrowserBridge.callAsync prepends a requestID to these messages. */
158 void TracingMessageHandler::RegisterMessages() {
159 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
160
161 web_ui_->RegisterMessageCallback(
162 "beginTracing",
163 NewCallback(this, &TracingMessageHandler::OnBeginTracing));
164 web_ui_->RegisterMessageCallback(
165 "endTracingAsync",
166 NewCallback(this, &TracingMessageHandler::OnEndTracingAsync));
167 web_ui_->RegisterMessageCallback(
168 "browserBridgeInitialized",
169 NewCallback(this, &TracingMessageHandler::OnBrowserBridgeInitialized));
170 web_ui_->RegisterMessageCallback(
171 "callAsync",
172 NewCallback(this, &TracingMessageHandler::OnCallAsync));
nduca 2011/08/04 00:50:48 I dont think this send is used. Lets kill it if it
dominich 2011/08/04 16:55:28 Done.
173 web_ui_->RegisterMessageCallback(
174 "beginRequestBufferPercentFull",
175 NewCallback(this,
176 &TracingMessageHandler::OnBeginRequestBufferPercentFull));
177 web_ui_->RegisterMessageCallback(
178 "loadTraceFile",
179 NewCallback(this, &TracingMessageHandler::OnLoadTraceFile));
180 web_ui_->RegisterMessageCallback(
181 "saveTraceFile",
182 NewCallback(this, &TracingMessageHandler::OnSaveTraceFile));
183 }
184
185 void TracingMessageHandler::OnCallAsync(const ListValue* args) {
186 DCHECK_GE(args->GetSize(), static_cast<size_t>(2));
187 // unpack args into requestId, submessage and submessageArgs
188 bool ok;
189 Value* requestId;
190 ok = args->Get(0, &requestId);
191 DCHECK(ok);
192
193 std::string submessage;
194 ok = args->GetString(1, &submessage);
195 DCHECK(ok);
196
197 ListValue* submessageArgs = new ListValue();
198 for (size_t i = 2; i < args->GetSize(); ++i) {
199 Value* arg;
200 ok = args->Get(i, &arg);
201 DCHECK(ok);
202
203 Value* argCopy = arg->DeepCopy();
204 submessageArgs->Append(argCopy);
205 }
206
207 // call the submessage handler
208 Value* ret = NULL;
209 if (submessage == "requestClientInfo") {
210 ret = OnRequestClientInfo(submessageArgs);
211 } else if (submessage == "requestLogMessages") {
212 ret = OnRequestLogMessages(submessageArgs);
213 } else { // unrecognized submessage
214 NOTREACHED();
215 delete submessageArgs;
216 return;
217 }
218 delete submessageArgs;
219
220 // call BrowserBridge.onCallAsyncReply with result
221 if (ret) {
222 web_ui_->CallJavascriptFunction("browserBridge.onCallAsyncReply",
223 *requestId,
224 *ret);
225 delete ret;
226 } else {
227 web_ui_->CallJavascriptFunction("browserBridge.onCallAsyncReply",
228 *requestId);
229 }
230 }
231
232 void TracingMessageHandler::OnBeginRequestBufferPercentFull(
233 const ListValue* list) {
234 TraceController::GetInstance()->GetTraceBufferPercentFullAsync(this);
235 }
236
237 class ReadTraceFileTask : public Task {
238 public:
239 ReadTraceFileTask(TaskProxy* proxy, const FilePath& path)
240 : proxy_(proxy)
241 , path_(path) {}
242
243 virtual void Run() {
244 std::string* file_contents = new std::string();
245 if (!file_util::ReadFileToString(path_, file_contents)) {
246 delete file_contents;
247 return;
248 }
249 BrowserThread::PostTask(
250 BrowserThread::UI, FROM_HERE,
251 NewRunnableMethod(proxy_.get(),
252 &TaskProxy::LoadTraceFileCompleteProxy,
253 file_contents));
254 }
255
256 private:
257 scoped_refptr<TaskProxy> proxy_;
258
259 // Path of the file to open.
260 const FilePath path_;
261 };
262
263 class WriteTraceFileTask : public Task {
264 public:
265 WriteTraceFileTask(TaskProxy* proxy,
266 const FilePath& path,
267 std::string* contents)
268 : proxy_(proxy)
269 , path_(path)
270 , contents_(contents) {}
271
272 virtual void Run() {
273 if (!file_util::WriteFile(path_, contents_->c_str(), contents_->size()))
274 return;
275 BrowserThread::PostTask(
276 BrowserThread::UI, FROM_HERE,
277 NewRunnableMethod(proxy_.get(),
278 &TaskProxy::SaveTraceFileCompleteProxy));
279 }
280
281 private:
282 scoped_refptr<TaskProxy> proxy_;
283
284 // Path of the file to save.
285 const FilePath path_;
286
287 // What to save
288 scoped_ptr<std::string> contents_;
289 };
290
291 void TracingMessageHandler::FileSelected(
292 const FilePath& path, int index, void* params) {
293 if (select_trace_file_dialog_type_ == SelectFileDialog::SELECT_OPEN_FILE)
294 BrowserThread::PostTask(
295 BrowserThread::FILE, FROM_HERE,
296 new ReadTraceFileTask(new TaskProxy(AsWeakPtr()), path));
297 else
298 BrowserThread::PostTask(
299 BrowserThread::FILE, FROM_HERE,
300 new WriteTraceFileTask(new TaskProxy(AsWeakPtr()), path,
301 trace_data_to_save_.release()));
302 select_trace_file_dialog_.release();
303 }
304
305 void TracingMessageHandler::FileSelectionCanceled(void* params) {
306 select_trace_file_dialog_.release();
307 if (select_trace_file_dialog_type_ == SelectFileDialog::SELECT_OPEN_FILE) {
308 web_ui_->CallJavascriptFunction(
309 "tracingController.onLoadTraceFileCanceled");
310 } else {
311 web_ui_->CallJavascriptFunction(
312 "tracingController.onSaveTraceFileCanceled");
313 }
314 }
315
316 void TracingMessageHandler::OnLoadTraceFile(const ListValue* list) {
317 // Only allow a single dialog at a time.
318 if (select_trace_file_dialog_.get())
319 return;
320 select_trace_file_dialog_type_ = SelectFileDialog::SELECT_OPEN_FILE;
321 select_trace_file_dialog_ = SelectFileDialog::Create(this);
322 select_trace_file_dialog_->SelectFile(
323 SelectFileDialog::SELECT_OPEN_FILE,
324 string16(),
325 FilePath(),
326 NULL, 0, FILE_PATH_LITERAL(""), web_ui_->tab_contents(),
327 web_ui_->tab_contents()->view()->GetTopLevelNativeWindow(), NULL);
328 }
329
330 void TracingMessageHandler::LoadTraceFileComplete(std::string* file_contents) {
331 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
332 std::wstring javascript;
333 javascript += L"tracingController.onLoadTraceFileComplete(";
334 javascript += UTF8ToWide(*file_contents);
335 javascript += L");";
336
337 web_ui_->GetRenderViewHost()->ExecuteJavascriptInWebFrame(string16(),
338 WideToUTF16Hack(javascript));
339 }
340
341 void TracingMessageHandler::OnSaveTraceFile(const ListValue* list) {
342 // Only allow a single dialog at a time.
343 if (select_trace_file_dialog_.get())
344 return;
345
346 DCHECK(list->GetSize() == 1);
347
348 std::string* trace_data = new std::string();
349 bool ok = list->GetString(0, trace_data);
350 DCHECK(ok);
351 trace_data_to_save_.reset(trace_data);
352
353 select_trace_file_dialog_type_ = SelectFileDialog::SELECT_SAVEAS_FILE;
354 select_trace_file_dialog_ = SelectFileDialog::Create(this);
355 select_trace_file_dialog_->SelectFile(
356 SelectFileDialog::SELECT_SAVEAS_FILE,
357 string16(),
358 FilePath(),
359 NULL, 0, FILE_PATH_LITERAL(""), web_ui_->tab_contents(),
360 web_ui_->tab_contents()->view()->GetTopLevelNativeWindow(), NULL);
361 }
362
363 void TracingMessageHandler::SaveTraceFileComplete() {
364 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
365 std::wstring javascript;
366 web_ui_->CallJavascriptFunction("tracingController.onSaveTraceFileComplete");
367 }
368
369 void TracingMessageHandler::OnBrowserBridgeInitialized(const ListValue* args) {
nduca 2011/08/04 00:50:48 Dead
dominich 2011/08/04 16:55:28 Done.
370 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
371
372 DCHECK(!gpu_info_update_callback_);
373
374 // Watch for changes in GPUInfo
375 gpu_info_update_callback_ =
376 NewCallback(this, &TracingMessageHandler::OnGpuInfoUpdate);
377 gpu_data_manager_->AddGpuInfoUpdateCallback(gpu_info_update_callback_);
378
379 // Tell GpuDataManager it should have full GpuInfo. If the
380 // Gpu process has not run yet, this will trigger its launch.
381 gpu_data_manager_->RequestCompleteGpuInfoIfNeeded();
382
383 // Run callback immediately in case the info is ready and no update in the
384 // future.
385 OnGpuInfoUpdate();
386 }
387
388 Value* TracingMessageHandler::OnRequestClientInfo(const ListValue* list) {
nduca 2011/08/04 00:50:48 Dead
dominich 2011/08/04 16:55:28 Done.
389 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
390
391 DictionaryValue* dict = new DictionaryValue();
392
393 chrome::VersionInfo version_info;
394
395 if (!version_info.is_valid()) {
396 DLOG(ERROR) << "Unable to create chrome::VersionInfo";
397 } else {
398 // We have everything we need to send the right values.
399 dict->SetString("version", version_info.Version());
400 dict->SetString("cl", version_info.LastChange());
401 dict->SetString("version_mod",
402 chrome::VersionInfo::GetVersionStringModifier());
403 dict->SetString("official",
404 l10n_util::GetStringUTF16(
405 version_info.IsOfficialBuild() ?
406 IDS_ABOUT_VERSION_OFFICIAL :
407 IDS_ABOUT_VERSION_UNOFFICIAL));
408
409 dict->SetString("command_line",
410 CommandLine::ForCurrentProcess()->GetCommandLineString());
411 }
412
413 dict->SetString("blacklist_version",
414 GpuDataManager::GetInstance()->GetBlacklistVersion());
415
416 return dict;
417 }
418
419 DictionaryValue* NewDescriptionValuePair(const std::string& desc,
420 const std::string& value) {
421 DictionaryValue* dict = new DictionaryValue();
422 dict->SetString("description", desc);
423 dict->SetString("value", value);
424 return dict;
425 }
426
427 DictionaryValue* NewDescriptionValuePair(const std::string& desc,
428 Value* value) {
429 DictionaryValue* dict = new DictionaryValue();
430 dict->SetString("description", desc);
431 dict->Set("value", value);
432 return dict;
433 }
434
435 #if defined(OS_WIN)
nduca 2011/08/04 00:50:48 Dead code
dominich 2011/08/04 16:55:28 Done.
436 // Output DxDiagNode tree as nested array of {description,value} pairs
437 ListValue* DxDiagNodeToList(const DxDiagNode& node) {
438 ListValue* list = new ListValue();
439 for (std::map<std::string, std::string>::const_iterator it =
440 node.values.begin();
441 it != node.values.end();
442 ++it) {
443 list->Append(NewDescriptionValuePair(it->first, it->second));
444 }
445
446 for (std::map<std::string, DxDiagNode>::const_iterator it =
447 node.children.begin();
448 it != node.children.end();
449 ++it) {
450 ListValue* sublist = DxDiagNodeToList(it->second);
451 list->Append(NewDescriptionValuePair(it->first, sublist));
452 }
453 return list;
454 }
455
456 #endif // OS_WIN
457
458 DictionaryValue* GpuInfoToDict(const GPUInfo& gpu_info) {
nduca 2011/08/04 00:50:48 Dead code
dominich 2011/08/04 16:55:28 Done.
459 ListValue* basic_info = new ListValue();
460 basic_info->Append(NewDescriptionValuePair("Initialization time",
461 base::Int64ToString(gpu_info.initialization_time.InMilliseconds())));
462 basic_info->Append(NewDescriptionValuePair("Vendor Id",
463 base::StringPrintf("0x%04x", gpu_info.vendor_id)));
464 basic_info->Append(NewDescriptionValuePair("Device Id",
465 base::StringPrintf("0x%04x", gpu_info.device_id)));
466 basic_info->Append(NewDescriptionValuePair("Driver vendor",
467 gpu_info.driver_vendor));
468 basic_info->Append(NewDescriptionValuePair("Driver version",
469 gpu_info.driver_version));
470 basic_info->Append(NewDescriptionValuePair("Driver date",
471 gpu_info.driver_date));
472 basic_info->Append(NewDescriptionValuePair("Pixel shader version",
473 gpu_info.pixel_shader_version));
474 basic_info->Append(NewDescriptionValuePair("Vertex shader version",
475 gpu_info.vertex_shader_version));
476 basic_info->Append(NewDescriptionValuePair("GL version",
477 gpu_info.gl_version));
478 basic_info->Append(NewDescriptionValuePair("GL_VENDOR",
479 gpu_info.gl_vendor));
480 basic_info->Append(NewDescriptionValuePair("GL_RENDERER",
481 gpu_info.gl_renderer));
482 basic_info->Append(NewDescriptionValuePair("GL_VERSION",
483 gpu_info.gl_version_string));
484 basic_info->Append(NewDescriptionValuePair("GL_EXTENSIONS",
485 gpu_info.gl_extensions));
486
487 DictionaryValue* info = new DictionaryValue();
488 info->Set("basic_info", basic_info);
489
490 #if defined(OS_WIN)
491 Value* dx_info;
492 if (gpu_info.dx_diagnostics.children.size())
493 dx_info = DxDiagNodeToList(gpu_info.dx_diagnostics);
494 else
495 dx_info = Value::CreateNullValue();
496 info->Set("diagnostics", dx_info);
497 #endif
498
499 return info;
500 }
501
502 Value* TracingMessageHandler::OnRequestLogMessages(const ListValue*) {
503 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
504
505 return gpu_data_manager_->log_messages().DeepCopy();
506 }
507
508 void TracingMessageHandler::OnGpuInfoUpdate() {
509 const GPUInfo& gpu_info = gpu_data_manager_->gpu_info();
510
511 // Get GPU Info.
512 DictionaryValue* gpu_info_val = GpuInfoToDict(gpu_info);
513
514 // Add in blacklisting features
515 Value* feature_status = gpu_data_manager_->GetFeatureStatus();
516 if (feature_status)
517 gpu_info_val->Set("featureStatus", feature_status);
518
519 // Send GPU Info to javascript.
520 web_ui_->CallJavascriptFunction("browserBridge.onGpuInfoUpdate",
521 *gpu_info_val);
522
523 delete gpu_info_val;
524 }
525
526 void TracingMessageHandler::OnBeginTracing(const ListValue* args) {
527 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
528 trace_enabled_ = true;
529 // TODO(jbates) This may fail, but that's OK for current use cases.
530 // Ex: Multiple about:gpu traces can not trace simultaneously.
531 // TODO(nduca) send feedback to javascript about whether or not BeginTracing
532 // was successful.
533 TraceController::GetInstance()->BeginTracing(this);
534 }
535
536 void TracingMessageHandler::OnEndTracingAsync(const ListValue* list) {
537 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
538
539 // TODO(nduca): fix javascript code to make sure trace_enabled_ is always true
540 // here. triggered a false condition by just clicking stop
541 // trace a few times when it was going slow, and maybe switching
542 // between tabs.
543 if (trace_enabled_ &&
544 !TraceController::GetInstance()->EndTracingAsync(this)) {
545 // Set to false now, since it turns out we never were the trace subscriber.
546 OnEndTracingComplete();
547 }
548 }
549
550 void TracingMessageHandler::OnEndTracingComplete() {
551 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
552 trace_enabled_ = false;
553 web_ui_->CallJavascriptFunction("tracingController.onEndTracingComplete");
554 }
555
556 void TracingMessageHandler::OnTraceDataCollected(
557 const std::string& json_events) {
558 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
559 std::wstring javascript;
560 javascript += L"tracingController.onTraceDataCollected(";
561 javascript += UTF8ToWide(json_events);
562 javascript += L");";
563
564 web_ui_->GetRenderViewHost()->ExecuteJavascriptInWebFrame(string16(),
565 WideToUTF16Hack(javascript));
566 }
567
568 void TracingMessageHandler::OnTraceBufferPercentFullReply(float percent_full) {
569 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
570 web_ui_->CallJavascriptFunction(
571 "tracingController.onRequestBufferPercentFullComplete",
572 *scoped_ptr<Value>(Value::CreateDoubleValue(percent_full)));
573 }
574
575 } // namespace
576
577
578 ////////////////////////////////////////////////////////////////////////////////
579 //
580 // TracingUI
581 //
582 ////////////////////////////////////////////////////////////////////////////////
583
584 TracingUI::TracingUI(TabContents* contents) : ChromeWebUI(contents) {
585 printf("TracingUI::TracingUI\n");
586 AddMessageHandler((new TracingMessageHandler())->Attach(this));
587
588 // Set up the chrome://tracing/ source.
589 Profile::FromBrowserContext(contents->browser_context())->
590 GetChromeURLDataManager()->AddDataSource(CreateTracingHTMLSource());
591 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698