Adds shutdown when FrameTreeServer goes away
[chromium-blink-merge.git] / components / feedback / feedback_common.cc
blobe3e27480c7a0c90805a1c316ef4ae35dbb66ef8b
1 // Copyright 2014 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 "feedback_common.h"
7 #include "base/strings/string_util.h"
8 #include "components/feedback/proto/common.pb.h"
9 #include "components/feedback/proto/dom.pb.h"
10 #include "components/feedback/proto/extension.pb.h"
11 #include "components/feedback/proto/math.pb.h"
13 namespace {
15 const char kMultilineIndicatorString[] = "<multiline>\n";
16 const char kMultilineStartString[] = "---------- START ----------\n";
17 const char kMultilineEndString[] = "---------- END ----------\n\n";
19 const size_t kFeedbackMaxLength = 4 * 1024;
20 const size_t kFeedbackMaxLineCount = 40;
22 const base::FilePath::CharType kLogsFilename[] =
23 FILE_PATH_LITERAL("system_logs.txt");
24 const char kLogsAttachmentName[] = "system_logs.zip";
26 const char kZipExt[] = ".zip";
28 const char kPngMimeType[] = "image/png";
29 const char kArbitraryMimeType[] = "application/octet-stream";
31 // Converts the system logs into a string that we can compress and send
32 // with the report. This method only converts those logs that we want in
33 // the compressed zip file sent with the report, hence it ignores any logs
34 // below the size threshold of what we want compressed.
35 std::string* LogsToString(const FeedbackCommon::SystemLogsMap& sys_info) {
36 std::string* syslogs_string = new std::string;
37 for (FeedbackCommon::SystemLogsMap::const_iterator it = sys_info.begin();
38 it != sys_info.end();
39 ++it) {
40 std::string key = it->first;
41 std::string value = it->second;
43 if (FeedbackCommon::BelowCompressionThreshold(value))
44 continue;
46 base::TrimString(key, "\n ", &key);
47 base::TrimString(value, "\n ", &value);
49 if (value.find("\n") != std::string::npos) {
50 syslogs_string->append(key + "=" + kMultilineIndicatorString +
51 kMultilineStartString + value + "\n" +
52 kMultilineEndString);
53 } else {
54 syslogs_string->append(key + "=" + value + "\n");
57 return syslogs_string;
60 void AddFeedbackData(userfeedback::ExtensionSubmit* feedback_data,
61 const std::string& key,
62 const std::string& value) {
63 // Don't bother with empty keys or values.
64 if (key.empty() || value.empty())
65 return;
66 // Create log_value object and add it to the web_data object.
67 userfeedback::ProductSpecificData log_value;
68 log_value.set_key(key);
69 log_value.set_value(value);
70 userfeedback::WebData* web_data = feedback_data->mutable_web_data();
71 *(web_data->add_product_specific_data()) = log_value;
74 // Adds data as an attachment to feedback_data if the data is non-empty.
75 void AddAttachment(userfeedback::ExtensionSubmit* feedback_data,
76 const char* name,
77 const std::string& data) {
78 if (data.empty())
79 return;
81 userfeedback::ProductSpecificBinaryData* attachment =
82 feedback_data->add_product_specific_binary_data();
83 attachment->set_mime_type(kArbitraryMimeType);
84 attachment->set_name(name);
85 attachment->set_data(data);
88 } // namespace
90 FeedbackCommon::AttachedFile::AttachedFile(const std::string& filename,
91 scoped_ptr<std::string> data)
92 : name(filename), data(data.Pass()) {}
94 FeedbackCommon::AttachedFile::~AttachedFile() {}
97 FeedbackCommon::FeedbackCommon() : product_id_(0) {}
99 FeedbackCommon::~FeedbackCommon() {}
101 // static
102 bool FeedbackCommon::BelowCompressionThreshold(const std::string& content) {
103 if (content.length() > kFeedbackMaxLength)
104 return false;
105 const size_t line_count = std::count(content.begin(), content.end(), '\n');
106 if (line_count > kFeedbackMaxLineCount)
107 return false;
108 return true;
111 void FeedbackCommon::CompressFile(const base::FilePath& filename,
112 const std::string& zipname,
113 scoped_ptr<std::string> data) {
114 AttachedFile* file = new AttachedFile(
115 zipname, scoped_ptr<std::string>(new std::string()));
116 if (file->name.empty()) {
117 // We need to use the UTF8Unsafe methods here to accomodate Windows, which
118 // uses wide strings to store filepaths.
119 file->name = filename.BaseName().AsUTF8Unsafe();
120 file->name.append(kZipExt);
122 if (feedback_util::ZipString(filename, *(data.get()), file->data.get())) {
123 base::AutoLock lock(attachments_lock_);
124 attachments_.push_back(file);
125 } else {
126 delete file;
130 void FeedbackCommon::AddFile(const std::string& filename,
131 scoped_ptr<std::string> data) {
132 base::AutoLock lock(attachments_lock_);
133 attachments_.push_back(new AttachedFile(filename, data.Pass()));
136 void FeedbackCommon::AddLog(const std::string& name, const std::string& value) {
137 if (!logs_.get())
138 logs_ = scoped_ptr<SystemLogsMap>(new SystemLogsMap);
139 (*logs_.get())[name] = value;
142 void FeedbackCommon::AddLogs(scoped_ptr<SystemLogsMap> logs) {
143 if (logs_) {
144 logs_->insert(logs->begin(), logs->end());
145 } else {
146 logs_ = logs.Pass();
150 void FeedbackCommon::CompressLogs() {
151 if (!logs_)
152 return;
153 std::string* logs = LogsToString(*logs_.get());
154 if (!logs->empty())
155 CompressFile(
156 base::FilePath(kLogsFilename), kLogsAttachmentName,
157 scoped_ptr<std::string>(logs));
160 void FeedbackCommon::AddFilesAndLogsToReport(
161 userfeedback::ExtensionSubmit* feedback_data) const {
162 if (sys_info()) {
163 for (FeedbackCommon::SystemLogsMap::const_iterator i = sys_info()->begin();
164 i != sys_info()->end();
165 ++i) {
166 if (BelowCompressionThreshold(i->second))
167 AddFeedbackData(feedback_data, i->first, i->second);
171 for (size_t i = 0; i < attachments(); i++) {
172 const AttachedFile* file = attachment(i);
173 AddAttachment(feedback_data, file->name.c_str(), *file->data.get());
177 void FeedbackCommon::PrepareReport(
178 userfeedback::ExtensionSubmit* feedback_data) const {
179 // Unused field, needs to be 0 though.
180 feedback_data->set_type_id(0);
181 feedback_data->set_product_id(product_id_);
183 userfeedback::CommonData* common_data = feedback_data->mutable_common_data();
184 // We're not using gaia ids, we're using the e-mail field instead.
185 common_data->set_gaia_id(0);
186 common_data->set_user_email(user_email());
187 common_data->set_description(description());
188 common_data->set_source_description_language(locale());
190 userfeedback::WebData* web_data = feedback_data->mutable_web_data();
191 web_data->set_url(page_url());
192 web_data->mutable_navigator()->set_user_agent(user_agent());
194 AddFilesAndLogsToReport(feedback_data);
196 if (image() && image()->size()) {
197 userfeedback::PostedScreenshot screenshot;
198 screenshot.set_mime_type(kPngMimeType);
200 // Set that we 'have' dimensions of the screenshot. These dimensions are
201 // ignored by the server but are a 'required' field in the protobuf.
202 userfeedback::Dimensions dimensions;
203 dimensions.set_width(0.0);
204 dimensions.set_height(0.0);
206 *(screenshot.mutable_dimensions()) = dimensions;
207 screenshot.set_binary_content(*image());
209 *(feedback_data->mutable_screenshot()) = screenshot;
212 if (category_tag().size())
213 feedback_data->set_bucket(category_tag());