OLD | NEW |
| (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/feedback/feedback_util.h" | |
6 | |
7 #include <sstream> | |
8 #include <string> | |
9 #include <vector> | |
10 | |
11 #include "base/bind.h" | |
12 #include "base/command_line.h" | |
13 #include "base/file_util.h" | |
14 #include "base/file_version_info.h" | |
15 #include "base/memory/singleton.h" | |
16 #include "base/message_loop/message_loop.h" | |
17 #include "base/strings/string_number_conversions.h" | |
18 #include "base/strings/stringprintf.h" | |
19 #include "base/strings/utf_string_conversions.h" | |
20 #include "base/win/windows_version.h" | |
21 #include "chrome/browser/browser_process.h" | |
22 #include "chrome/browser/extensions/api/feedback_private/feedback_private_api.h" | |
23 #include "chrome/browser/feedback/feedback_data.h" | |
24 #include "chrome/browser/feedback/feedback_uploader.h" | |
25 #include "chrome/browser/feedback/feedback_uploader_factory.h" | |
26 #include "chrome/browser/metrics/variations/variations_http_header_provider.h" | |
27 #include "chrome/browser/profiles/profile.h" | |
28 #include "chrome/browser/profiles/profile_manager.h" | |
29 #include "chrome/browser/safe_browsing/safe_browsing_util.h" | |
30 #include "chrome/browser/ui/browser_finder.h" | |
31 #include "chrome/browser/ui/browser_list.h" | |
32 #include "chrome/browser/ui/browser_window.h" | |
33 #include "chrome/browser/ui/tabs/tab_strip_model.h" | |
34 #include "chrome/common/chrome_content_client.h" | |
35 #include "chrome/common/chrome_switches.h" | |
36 #include "chrome/common/chrome_version_info.h" | |
37 #include "components/metrics/metrics_log_manager.h" | |
38 #include "content/public/browser/navigation_controller.h" | |
39 #include "content/public/browser/web_contents.h" | |
40 #include "grit/generated_resources.h" | |
41 #include "grit/locale_settings.h" | |
42 #include "grit/theme_resources.h" | |
43 #include "net/base/load_flags.h" | |
44 #include "net/http/http_request_headers.h" | |
45 #include "net/url_request/url_fetcher.h" | |
46 #include "net/url_request/url_fetcher_delegate.h" | |
47 #include "net/url_request/url_request_status.h" | |
48 #include "third_party/icu/source/common/unicode/locid.h" | |
49 #include "third_party/zlib/google/zip.h" | |
50 #include "ui/base/l10n/l10n_util.h" | |
51 #include "url/gurl.h" | |
52 | |
53 namespace { | |
54 | |
55 GURL GetTargetTabUrl(int session_id, int index) { | |
56 Browser* browser = chrome::FindBrowserWithID(session_id); | |
57 // Sanity checks. | |
58 if (!browser || index >= browser->tab_strip_model()->count()) | |
59 return GURL(); | |
60 | |
61 if (index >= 0) { | |
62 content::WebContents* target_tab = | |
63 browser->tab_strip_model()->GetWebContentsAt(index); | |
64 if (target_tab) | |
65 return target_tab->GetURL(); | |
66 } | |
67 | |
68 return GURL(); | |
69 } | |
70 | |
71 const char kPngMimeType[] = "image/png"; | |
72 const char kArbitraryMimeType[] = "application/octet-stream"; | |
73 const char kHistogramsAttachmentName[] = "histograms.zip"; | |
74 const char kLogsAttachmentName[] = "system_logs.zip"; | |
75 | |
76 #if defined(OS_CHROMEOS) | |
77 const int kChromeOSProductId = 208; | |
78 #else | |
79 const int kChromeBrowserProductId = 237; | |
80 #endif | |
81 | |
82 void AddFeedbackData(userfeedback::ExtensionSubmit* feedback_data, | |
83 const std::string& key, const std::string& value) { | |
84 // Don't bother with empty keys or values | |
85 if (key == "" || value == "") return; | |
86 // Create log_value object and add it to the web_data object | |
87 userfeedback::ProductSpecificData log_value; | |
88 log_value.set_key(key); | |
89 log_value.set_value(value); | |
90 userfeedback::WebData* web_data = feedback_data->mutable_web_data(); | |
91 *(web_data->add_product_specific_data()) = log_value; | |
92 } | |
93 | |
94 // Adds data as an attachment to feedback_data if the data is non-empty. | |
95 void AddAttachment(userfeedback::ExtensionSubmit* feedback_data, | |
96 const char* name, | |
97 std::string* data) { | |
98 if (data == NULL || data->empty()) | |
99 return; | |
100 | |
101 userfeedback::ProductSpecificBinaryData* attachment = | |
102 feedback_data->add_product_specific_binary_data(); | |
103 attachment->set_mime_type(kArbitraryMimeType); | |
104 attachment->set_name(name); | |
105 attachment->set_data(*data); | |
106 } | |
107 | |
108 } // namespace | |
109 | |
110 namespace chrome { | |
111 | |
112 const char kAppLauncherCategoryTag[] = "AppLauncher"; | |
113 | |
114 void ShowFeedbackPage(Browser* browser, | |
115 const std::string& description_template, | |
116 const std::string& category_tag) { | |
117 GURL page_url; | |
118 if (browser) { | |
119 page_url = GetTargetTabUrl(browser->session_id().id(), | |
120 browser->tab_strip_model()->active_index()); | |
121 } | |
122 | |
123 Profile* profile = NULL; | |
124 if (browser) { | |
125 profile = browser->profile(); | |
126 } else { | |
127 profile = ProfileManager::GetLastUsedProfileAllowedByPolicy(); | |
128 } | |
129 if (!profile) { | |
130 LOG(ERROR) << "Cannot invoke feedback: No profile found!"; | |
131 return; | |
132 } | |
133 | |
134 // We do not want to launch on an OTR profile. | |
135 profile = profile->GetOriginalProfile(); | |
136 DCHECK(profile); | |
137 | |
138 extensions::FeedbackPrivateAPI* api = | |
139 extensions::FeedbackPrivateAPI::GetFactoryInstance()->Get(profile); | |
140 | |
141 api->RequestFeedback(description_template, | |
142 category_tag, | |
143 page_url); | |
144 } | |
145 | |
146 } // namespace chrome | |
147 | |
148 namespace feedback_util { | |
149 | |
150 void SendReport(scoped_refptr<FeedbackData> data) { | |
151 if (!data.get()) { | |
152 LOG(ERROR) << "SendReport called with NULL data!"; | |
153 NOTREACHED(); | |
154 return; | |
155 } | |
156 | |
157 userfeedback::ExtensionSubmit feedback_data; | |
158 // Unused field, needs to be 0 though. | |
159 feedback_data.set_type_id(0); | |
160 | |
161 userfeedback::CommonData* common_data = feedback_data.mutable_common_data(); | |
162 // We're not using gaia ids, we're using the e-mail field instead. | |
163 common_data->set_gaia_id(0); | |
164 common_data->set_user_email(data->user_email()); | |
165 common_data->set_description(data->description()); | |
166 | |
167 std::string chrome_locale = g_browser_process->GetApplicationLocale(); | |
168 common_data->set_source_description_language(chrome_locale); | |
169 | |
170 userfeedback::WebData* web_data = feedback_data.mutable_web_data(); | |
171 web_data->set_url(data->page_url()); | |
172 web_data->mutable_navigator()->set_user_agent(GetUserAgent()); | |
173 | |
174 gfx::Rect screen_size; | |
175 if (data->sys_info()) { | |
176 for (FeedbackData::SystemLogsMap::const_iterator i = | |
177 data->sys_info()->begin(); i != data->sys_info()->end(); ++i) { | |
178 if (FeedbackData::BelowCompressionThreshold(i->second)) | |
179 AddFeedbackData(&feedback_data, i->first, i->second); | |
180 } | |
181 | |
182 AddAttachment(&feedback_data, kLogsAttachmentName, data->compressed_logs()); | |
183 } | |
184 | |
185 if (data->histograms()) { | |
186 AddAttachment(&feedback_data, | |
187 kHistogramsAttachmentName, | |
188 data->compressed_histograms()); | |
189 } | |
190 | |
191 if (!data->attached_filename().empty()) { | |
192 // We need to use the UTF8Unsafe methods here to accomodate Windows, which | |
193 // uses wide strings to store filepaths. | |
194 std::string name = base::FilePath::FromUTF8Unsafe( | |
195 data->attached_filename()).BaseName().AsUTF8Unsafe(); | |
196 AddAttachment(&feedback_data, name.c_str(), data->attached_filedata()); | |
197 } | |
198 | |
199 // NOTE: Screenshot needs to be processed after system info since we'll get | |
200 // the screenshot dimensions from system info. | |
201 if (data->image() && data->image()->size()) { | |
202 userfeedback::PostedScreenshot screenshot; | |
203 screenshot.set_mime_type(kPngMimeType); | |
204 | |
205 // Set that we 'have' dimensions of the screenshot. These dimensions are | |
206 // ignored by the server but are a 'required' field in the protobuf. | |
207 userfeedback::Dimensions dimensions; | |
208 dimensions.set_width(0.0); | |
209 dimensions.set_height(0.0); | |
210 | |
211 *(screenshot.mutable_dimensions()) = dimensions; | |
212 screenshot.set_binary_content(*data->image()); | |
213 | |
214 *(feedback_data.mutable_screenshot()) = screenshot; | |
215 } | |
216 | |
217 if (data->category_tag().size()) | |
218 feedback_data.set_bucket(data->category_tag()); | |
219 | |
220 // Set whether we're reporting from ChromeOS or Chrome on another platform. | |
221 userfeedback::ChromeData chrome_data; | |
222 #if defined(OS_CHROMEOS) | |
223 chrome_data.set_chrome_platform( | |
224 userfeedback::ChromeData_ChromePlatform_CHROME_OS); | |
225 userfeedback::ChromeOsData chrome_os_data; | |
226 chrome_os_data.set_category( | |
227 userfeedback::ChromeOsData_ChromeOsCategory_OTHER); | |
228 *(chrome_data.mutable_chrome_os_data()) = chrome_os_data; | |
229 feedback_data.set_product_id(kChromeOSProductId); | |
230 #else | |
231 chrome_data.set_chrome_platform( | |
232 userfeedback::ChromeData_ChromePlatform_CHROME_BROWSER); | |
233 userfeedback::ChromeBrowserData chrome_browser_data; | |
234 chrome_browser_data.set_category( | |
235 userfeedback::ChromeBrowserData_ChromeBrowserCategory_OTHER); | |
236 *(chrome_data.mutable_chrome_browser_data()) = chrome_browser_data; | |
237 feedback_data.set_product_id(kChromeBrowserProductId); | |
238 #endif | |
239 | |
240 *(feedback_data.mutable_chrome_data()) = chrome_data; | |
241 | |
242 // This pointer will eventually get deleted by the PostCleanup class, after | |
243 // we've either managed to successfully upload the report or died trying. | |
244 std::string post_body; | |
245 feedback_data.SerializeToString(&post_body); | |
246 | |
247 feedback::FeedbackUploader *uploader = | |
248 feedback::FeedbackUploaderFactory::GetForBrowserContext(data->profile()); | |
249 uploader->QueueReport(post_body); | |
250 } | |
251 | |
252 bool ZipString(const base::FilePath& filename, | |
253 const std::string& data, std::string* compressed_logs) { | |
254 base::FilePath temp_path; | |
255 base::FilePath zip_file; | |
256 | |
257 // Create a temporary directory, put the logs into a file in it. Create | |
258 // another temporary file to receive the zip file in. | |
259 if (!base::CreateNewTempDirectory(base::FilePath::StringType(), &temp_path)) | |
260 return false; | |
261 if (base::WriteFile(temp_path.Append(filename), data.c_str(), data.size()) == | |
262 -1) | |
263 return false; | |
264 | |
265 bool succeed = base::CreateTemporaryFile(&zip_file) && | |
266 zip::Zip(temp_path, zip_file, false) && | |
267 base::ReadFileToString(zip_file, compressed_logs); | |
268 | |
269 base::DeleteFile(temp_path, true); | |
270 base::DeleteFile(zip_file, false); | |
271 | |
272 return succeed; | |
273 } | |
274 | |
275 } // namespace feedback_util | |
OLD | NEW |