Add ICU message format support
[chromium-blink-merge.git] / content / browser / tracing / tracing_ui.cc
blob4829c367ebd2ef2130877992267d61fee29b81b5
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.
5 #include "content/browser/tracing/tracing_ui.h"
7 #include <set>
8 #include <string>
9 #include <vector>
11 #include "base/base64.h"
12 #include "base/bind.h"
13 #include "base/bind_helpers.h"
14 #include "base/command_line.h"
15 #include "base/format_macros.h"
16 #include "base/json/json_reader.h"
17 #include "base/json/json_writer.h"
18 #include "base/memory/scoped_ptr.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_split.h"
21 #include "base/strings/string_util.h"
22 #include "base/strings/stringprintf.h"
23 #include "base/trace_event/trace_event.h"
24 #include "base/values.h"
25 #include "content/browser/tracing/grit/tracing_resources.h"
26 #include "content/browser/tracing/tracing_controller_impl.h"
27 #include "content/public/browser/browser_context.h"
28 #include "content/public/browser/browser_thread.h"
29 #include "content/public/browser/content_browser_client.h"
30 #include "content/public/browser/trace_uploader.h"
31 #include "content/public/browser/tracing_controller.h"
32 #include "content/public/browser/tracing_delegate.h"
33 #include "content/public/browser/web_contents.h"
34 #include "content/public/browser/web_ui.h"
35 #include "content/public/browser/web_ui_data_source.h"
36 #include "content/public/common/content_client.h"
37 #include "content/public/common/url_constants.h"
39 namespace content {
40 namespace {
42 void OnGotCategories(const WebUIDataSource::GotDataCallback& callback,
43 const std::set<std::string>& categorySet) {
44 base::ListValue category_list;
45 for (std::set<std::string>::const_iterator it = categorySet.begin();
46 it != categorySet.end(); it++) {
47 category_list.AppendString(*it);
50 base::RefCountedString* res = new base::RefCountedString();
51 base::JSONWriter::Write(category_list, &res->data());
52 callback.Run(res);
55 bool GetTracingOptions(const std::string& data64,
56 base::trace_event::TraceConfig* trace_config) {
57 std::string data;
58 if (!base::Base64Decode(data64, &data)) {
59 LOG(ERROR) << "Options were not base64 encoded.";
60 return false;
63 scoped_ptr<base::Value> optionsRaw = base::JSONReader::Read(data);
64 if (!optionsRaw) {
65 LOG(ERROR) << "Options were not valid JSON";
66 return false;
68 base::DictionaryValue* options;
69 if (!optionsRaw->GetAsDictionary(&options)) {
70 LOG(ERROR) << "Options must be dict";
71 return false;
74 if (!trace_config) {
75 LOG(ERROR) << "trace_config can't be passed as NULL";
76 return false;
79 bool options_ok = true;
80 std::string category_filter_string;
81 options_ok &= options->GetString("categoryFilter", &category_filter_string);
83 std::string record_mode;
84 options_ok &= options->GetString("tracingRecordMode", &record_mode);
86 *trace_config = base::trace_event::TraceConfig(category_filter_string,
87 record_mode);
89 bool enable_systrace;
90 options_ok &= options->GetBoolean("useSystemTracing", &enable_systrace);
91 if (enable_systrace)
92 trace_config->EnableSystrace();
94 bool enable_sampling;
95 options_ok &= options->GetBoolean("useSampling", &enable_sampling);
96 if (enable_sampling)
97 trace_config->EnableSampling();
99 if (!options_ok) {
100 LOG(ERROR) << "Malformed options";
101 return false;
103 return true;
106 void OnRecordingEnabledAck(const WebUIDataSource::GotDataCallback& callback);
108 bool BeginRecording(const std::string& data64,
109 const WebUIDataSource::GotDataCallback& callback) {
110 base::trace_event::TraceConfig trace_config("", "");
111 if (!GetTracingOptions(data64, &trace_config))
112 return false;
114 return TracingController::GetInstance()->EnableRecording(
115 trace_config,
116 base::Bind(&OnRecordingEnabledAck, callback));
119 void OnRecordingEnabledAck(const WebUIDataSource::GotDataCallback& callback) {
120 base::RefCountedString* res = new base::RefCountedString();
121 callback.Run(res);
124 void OnTraceBufferUsageResult(const WebUIDataSource::GotDataCallback& callback,
125 float percent_full,
126 size_t approximate_event_count) {
127 std::string str = base::DoubleToString(percent_full);
128 callback.Run(base::RefCountedString::TakeString(&str));
131 void OnTraceBufferStatusResult(const WebUIDataSource::GotDataCallback& callback,
132 float percent_full,
133 size_t approximate_event_count) {
134 base::DictionaryValue status;
135 status.SetDouble("percentFull", percent_full);
136 status.SetInteger("approximateEventCount", approximate_event_count);
138 std::string status_json;
139 base::JSONWriter::Write(status, &status_json);
141 base::RefCountedString* status_base64 = new base::RefCountedString();
142 base::Base64Encode(status_json, &status_base64->data());
143 callback.Run(status_base64);
146 void OnMonitoringEnabledAck(const WebUIDataSource::GotDataCallback& callback);
148 bool EnableMonitoring(const std::string& data64,
149 const WebUIDataSource::GotDataCallback& callback) {
150 base::trace_event::TraceConfig trace_config("", "");
151 if (!GetTracingOptions(data64, &trace_config))
152 return false;
154 return TracingController::GetInstance()->EnableMonitoring(
155 trace_config,
156 base::Bind(OnMonitoringEnabledAck, callback));
159 void OnMonitoringEnabledAck(const WebUIDataSource::GotDataCallback& callback) {
160 base::RefCountedString* res = new base::RefCountedString();
161 callback.Run(res);
164 void OnMonitoringDisabled(const WebUIDataSource::GotDataCallback& callback) {
165 base::RefCountedString* res = new base::RefCountedString();
166 callback.Run(res);
169 void GetMonitoringStatus(const WebUIDataSource::GotDataCallback& callback) {
170 bool is_monitoring;
171 base::trace_event::TraceConfig config("", "");
172 TracingController::GetInstance()->GetMonitoringStatus(
173 &is_monitoring, &config);
175 base::DictionaryValue monitoring_options;
176 monitoring_options.SetBoolean("isMonitoring", is_monitoring);
177 monitoring_options.SetString("categoryFilter",
178 config.ToCategoryFilterString());
179 monitoring_options.SetBoolean("useSystemTracing", config.IsSystraceEnabled());
180 monitoring_options.SetBoolean(
181 "useContinuousTracing",
182 config.GetTraceRecordMode() == base::trace_event::RECORD_CONTINUOUSLY);
183 monitoring_options.SetBoolean("useSampling", config.IsSamplingEnabled());
185 std::string monitoring_options_json;
186 base::JSONWriter::Write(monitoring_options, &monitoring_options_json);
188 base::RefCountedString* monitoring_options_base64 =
189 new base::RefCountedString();
190 base::Base64Encode(monitoring_options_json,
191 &monitoring_options_base64->data());
192 callback.Run(monitoring_options_base64);
195 void TracingCallbackWrapperBase64(
196 const WebUIDataSource::GotDataCallback& callback,
197 base::RefCountedString* data) {
198 base::RefCountedString* data_base64 = new base::RefCountedString();
199 base::Base64Encode(data->data(), &data_base64->data());
200 callback.Run(data_base64);
203 std::string GenerateMetadataJSON() {
204 // Serialize metadata to json.
205 scoped_ptr<base::DictionaryValue> metadata_dict(new base::DictionaryValue());
206 metadata_dict->SetString("version", GetContentClient()->GetProduct());
207 metadata_dict->SetString(
208 "command_line",
209 base::CommandLine::ForCurrentProcess()->GetCommandLineString());
211 std::string results;
212 if (base::JSONWriter::Write(*metadata_dict.get(), &results))
213 return results;
214 return std::string();
217 bool OnBeginJSONRequest(const std::string& path,
218 const WebUIDataSource::GotDataCallback& callback) {
219 if (path == "json/categories") {
220 return TracingController::GetInstance()->GetCategories(
221 base::Bind(OnGotCategories, callback));
224 const char* beginRecordingPath = "json/begin_recording?";
225 if (base::StartsWith(path, beginRecordingPath,
226 base::CompareCase::SENSITIVE)) {
227 std::string data = path.substr(strlen(beginRecordingPath));
228 return BeginRecording(data, callback);
230 if (path == "json/get_buffer_percent_full") {
231 return TracingController::GetInstance()->GetTraceBufferUsage(
232 base::Bind(OnTraceBufferUsageResult, callback));
234 if (path == "json/get_buffer_status") {
235 return TracingController::GetInstance()->GetTraceBufferUsage(
236 base::Bind(OnTraceBufferStatusResult, callback));
238 if (path == "json/end_recording_compressed") {
239 scoped_refptr<TracingControllerImpl::TraceDataSink> data_sink =
240 TracingController::CreateCompressedStringSink(
241 TracingController::CreateCallbackEndpoint(
242 base::Bind(TracingCallbackWrapperBase64, callback)));
243 data_sink->SetMetadata(GenerateMetadataJSON());
244 return TracingController::GetInstance()->DisableRecording(data_sink);
247 const char* enableMonitoringPath = "json/begin_monitoring?";
248 if (path.find(enableMonitoringPath) == 0) {
249 std::string data = path.substr(strlen(enableMonitoringPath));
250 return EnableMonitoring(data, callback);
252 if (path == "json/end_monitoring") {
253 return TracingController::GetInstance()->DisableMonitoring(
254 base::Bind(OnMonitoringDisabled, callback));
256 if (path == "json/capture_monitoring_compressed") {
257 scoped_refptr<TracingControllerImpl::TraceDataSink> data_sink =
258 TracingController::CreateCompressedStringSink(
259 TracingController::CreateCallbackEndpoint(
260 base::Bind(TracingCallbackWrapperBase64, callback)));
261 data_sink->SetMetadata(GenerateMetadataJSON());
262 TracingController::GetInstance()->CaptureMonitoringSnapshot(data_sink);
263 return true;
265 if (path == "json/get_monitoring_status") {
266 GetMonitoringStatus(callback);
267 return true;
270 LOG(ERROR) << "Unhandled request to " << path;
271 return false;
274 bool OnTracingRequest(const std::string& path,
275 const WebUIDataSource::GotDataCallback& callback) {
276 if (base::StartsWith(path, "json/", base::CompareCase::SENSITIVE)) {
277 if (!OnBeginJSONRequest(path, callback)) {
278 std::string error("##ERROR##");
279 callback.Run(base::RefCountedString::TakeString(&error));
281 return true;
283 return false;
286 } // namespace
289 ////////////////////////////////////////////////////////////////////////////////
291 // TracingUI
293 ////////////////////////////////////////////////////////////////////////////////
295 TracingUI::TracingUI(WebUI* web_ui)
296 : WebUIController(web_ui),
297 delegate_(GetContentClient()->browser()->GetTracingDelegate()),
298 weak_factory_(this) {
299 web_ui->RegisterMessageCallback(
300 "doUpload",
301 base::Bind(&TracingUI::DoUpload, base::Unretained(this)));
302 web_ui->RegisterMessageCallback(
303 "doUploadBase64",
304 base::Bind(&TracingUI::DoUploadBase64Encoded, base::Unretained(this)));
306 // Set up the chrome://tracing/ source.
307 BrowserContext* browser_context =
308 web_ui->GetWebContents()->GetBrowserContext();
310 WebUIDataSource* source = WebUIDataSource::Create(kChromeUITracingHost);
311 source->SetJsonPath("strings.js");
312 source->SetDefaultResource(IDR_TRACING_HTML);
313 source->AddResourcePath("tracing.js", IDR_TRACING_JS);
314 source->SetRequestFilter(base::Bind(OnTracingRequest));
315 WebUIDataSource::Add(browser_context, source);
316 TracingControllerImpl::GetInstance()->RegisterTracingUI(this);
319 TracingUI::~TracingUI() {
320 TracingControllerImpl::GetInstance()->UnregisterTracingUI(this);
323 void TracingUI::OnMonitoringStateChanged(bool is_monitoring) {
324 web_ui()->CallJavascriptFunction(
325 "onMonitoringStateChanged", base::FundamentalValue(is_monitoring));
328 void TracingUI::DoUploadBase64Encoded(const base::ListValue* args) {
329 std::string file_contents_base64;
330 if (!args || args->empty() || !args->GetString(0, &file_contents_base64)) {
331 web_ui()->CallJavascriptFunction("onUploadError",
332 base::StringValue("Missing data"));
333 return;
336 std::string file_contents;
337 base::Base64Decode(file_contents_base64, &file_contents);
339 // doUploadBase64 is used to upload binary data which is assumed to already
340 // be compressed.
341 DoUploadInternal(file_contents, TraceUploader::UNCOMPRESSED_UPLOAD);
344 void TracingUI::DoUpload(const base::ListValue* args) {
345 std::string file_contents;
346 if (!args || args->empty() || !args->GetString(0, &file_contents)) {
347 web_ui()->CallJavascriptFunction("onUploadError",
348 base::StringValue("Missing data"));
349 return;
352 DoUploadInternal(file_contents, TraceUploader::COMPRESSED_UPLOAD);
355 void TracingUI::DoUploadInternal(const std::string& file_contents,
356 TraceUploader::UploadMode upload_mode) {
357 if (!delegate_) {
358 web_ui()->CallJavascriptFunction("onUploadError",
359 base::StringValue("Not implemented"));
360 return;
363 if (trace_uploader_) {
364 web_ui()->CallJavascriptFunction("onUploadError",
365 base::StringValue("Upload in progress"));
366 return;
369 TraceUploader::UploadProgressCallback progress_callback =
370 base::Bind(&TracingUI::OnTraceUploadProgress,
371 weak_factory_.GetWeakPtr());
372 TraceUploader::UploadDoneCallback done_callback =
373 base::Bind(&TracingUI::OnTraceUploadComplete,
374 weak_factory_.GetWeakPtr());
376 trace_uploader_ = delegate_->GetTraceUploader(
377 web_ui()->GetWebContents()->GetBrowserContext()->GetRequestContext());
378 DCHECK(trace_uploader_);
379 trace_uploader_->DoUpload(file_contents, upload_mode, nullptr,
380 progress_callback, done_callback);
381 // TODO(mmandlis): Add support for stopping the upload in progress.
384 void TracingUI::OnTraceUploadProgress(int64 current, int64 total) {
385 DCHECK(current <= total);
386 int percent = (current / total) * 100;
387 web_ui()->CallJavascriptFunction(
388 "onUploadProgress",
389 base::FundamentalValue(percent),
390 base::StringValue(base::StringPrintf("%" PRId64, current)),
391 base::StringValue(base::StringPrintf("%" PRId64, total)));
394 void TracingUI::OnTraceUploadComplete(bool success,
395 const std::string& feedback) {
396 if (success) {
397 web_ui()->CallJavascriptFunction("onUploadComplete",
398 base::StringValue(feedback));
399 } else {
400 web_ui()->CallJavascriptFunction("onUploadError",
401 base::StringValue(feedback));
403 trace_uploader_.reset();
406 } // namespace content