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.
5 #include "chrome/browser/bug_report_util.h"
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/file_util.h"
13 #include "base/file_version_info.h"
14 #include "base/memory/singleton.h"
15 #include "base/string_util.h"
16 #include "base/stringprintf.h"
17 #include "base/utf_string_conversions.h"
18 #include "base/win/windows_version.h"
19 #include "chrome/browser/browser_process_impl.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/safe_browsing/safe_browsing_util.h"
22 #include "chrome/browser/ui/browser_list.h"
23 #include "chrome/common/chrome_switches.h"
24 #include "chrome/common/chrome_version_info.h"
25 #include "content/browser/tab_contents/tab_contents.h"
26 #include "content/public/common/url_fetcher.h"
27 #include "content/public/common/url_fetcher_delegate.h"
28 #include "googleurl/src/gurl.h"
29 #include "grit/generated_resources.h"
30 #include "grit/locale_settings.h"
31 #include "grit/theme_resources.h"
32 #include "net/url_request/url_request_status.h"
33 #include "ui/base/l10n/l10n_util.h"
34 #include "unicode/locid.h"
36 #if defined(OS_CHROMEOS)
37 #include "chrome/browser/chromeos/notifications/system_notification.h"
42 const int kBugReportVersion
= 1;
44 const char kReportPhishingUrl
[] =
45 "http://www.google.com/safebrowsing/report_phish/";
47 // URL to post bug reports to.
48 static char const kBugReportPostUrl
[] =
49 "https://www.google.com/tools/feedback/chrome/__submit";
51 static char const kProtBufMimeType
[] = "application/x-protobuf";
52 static char const kPngMimeType
[] = "image/png";
54 // Tags we use in product specific data
55 static char const kChromeVersionTag
[] = "CHROME VERSION";
56 static char const kOsVersionTag
[] = "OS VERSION";
58 static char const kNotificationId
[] = "feedback.chromeos";
60 static int const kHttpPostSuccessNoContent
= 204;
61 static int const kHttpPostFailNoConnection
= -1;
62 static int const kHttpPostFailClientError
= 400;
63 static int const kHttpPostFailServerError
= 500;
65 #if defined(OS_CHROMEOS)
66 static char const kBZip2MimeType
[] = "application/x-bzip2";
67 static char const kLogsAttachmentName
[] = "system_logs.bz2";
68 // Maximum number of lines in system info log chunk to be still included
69 // in product specific data.
70 const size_t kMaxLineCount
= 40;
71 // Maximum number of bytes in system info log chunk to be still included
72 // in product specific data.
73 const size_t kMaxSystemLogLength
= 4 * 1024;
76 const int64 kInitialRetryDelay
= 900000; // 15 minutes
77 const int64 kRetryDelayIncreaseFactor
= 2;
78 const int64 kRetryDelayLimit
= 14400000; // 4 hours
84 // Simple content::URLFetcherDelegate to clean up URLFetcher on completion.
85 class BugReportUtil::PostCleanup
: public content::URLFetcherDelegate
{
87 PostCleanup(Profile
* profile
, std::string
* post_body
,
88 int64 previous_delay
) : profile_(profile
),
89 post_body_(post_body
),
90 previous_delay_(previous_delay
) { }
91 // Overridden from content::URLFetcherDelegate.
92 virtual void OnURLFetchComplete(const content::URLFetcher
* source
);
95 virtual ~PostCleanup() {}
99 std::string
* post_body_
;
100 int64 previous_delay_
;
102 DISALLOW_COPY_AND_ASSIGN(PostCleanup
);
105 // Don't use the data parameter, instead use the pointer we pass into every
106 // post cleanup object - that pointer will be deleted and deleted only on a
107 // successful post to the feedback server.
108 void BugReportUtil::PostCleanup::OnURLFetchComplete(
109 const content::URLFetcher
* source
) {
110 std::stringstream error_stream
;
111 int response_code
= source
->GetResponseCode();
112 if (response_code
== kHttpPostSuccessNoContent
) {
113 // We've sent our report, delete the report data
116 error_stream
<< "Success";
118 // Uh oh, feedback failed, send it off to retry
119 if (previous_delay_
) {
120 if (previous_delay_
< kRetryDelayLimit
)
121 previous_delay_
*= kRetryDelayIncreaseFactor
;
123 previous_delay_
= kInitialRetryDelay
;
125 BugReportUtil::DispatchFeedback(profile_
, post_body_
, previous_delay_
);
127 // Process the error for debug output
128 if (response_code
== kHttpPostFailNoConnection
) {
129 error_stream
<< "No connection to server.";
130 } else if ((response_code
> kHttpPostFailClientError
) &&
131 (response_code
< kHttpPostFailServerError
)) {
132 error_stream
<< "Client error: HTTP response code " << response_code
;
133 } else if (response_code
> kHttpPostFailServerError
) {
134 error_stream
<< "Server error: HTTP response code " << response_code
;
136 error_stream
<< "Unknown error: HTTP response code " << response_code
;
140 LOG(WARNING
) << "FEEDBACK: Submission to feedback server (" <<
141 source
->GetURL() << ") status: " << error_stream
.str();
143 // Delete the URLFetcher.
145 // And then delete ourselves.
150 void BugReportUtil::SetOSVersion(std::string
* os_version
) {
152 base::win::OSInfo
* os_info
= base::win::OSInfo::GetInstance();
153 base::win::OSInfo::VersionNumber version_number
= os_info
->version_number();
154 *os_version
= base::StringPrintf("%d.%d.%d",
155 version_number
.major
,
156 version_number
.minor
,
157 version_number
.build
);
158 int service_pack
= os_info
->service_pack().major
;
159 if (service_pack
> 0)
160 os_version
->append(base::StringPrintf("Service Pack %d", service_pack
));
161 #elif defined(OS_MACOSX)
162 *os_version
= base::SysInfo::OperatingSystemVersion();
164 *os_version
= "unknown";
169 void BugReportUtil::DispatchFeedback(Profile
* profile
,
170 std::string
* post_body
,
174 MessageLoop::current()->PostDelayedTask(FROM_HERE
, base::Bind(
175 &BugReportUtil::SendFeedback
, profile
, post_body
, delay
), delay
);
179 void BugReportUtil::SendFeedback(Profile
* profile
,
180 std::string
* post_body
,
181 int64 previous_delay
) {
185 if (CommandLine::ForCurrentProcess()->
186 HasSwitch(switches::kFeedbackServer
))
187 post_url
= GURL(CommandLine::ForCurrentProcess()->
188 GetSwitchValueASCII(switches::kFeedbackServer
));
190 post_url
= GURL(kBugReportPostUrl
);
192 content::URLFetcher
* fetcher
= content::URLFetcher::Create(
193 post_url
, content::URLFetcher::POST
,
194 new BugReportUtil::PostCleanup(profile
, post_body
, previous_delay
));
195 fetcher
->SetRequestContext(profile
->GetRequestContext());
197 fetcher
->SetUploadData(std::string(kProtBufMimeType
), *post_body
);
203 void BugReportUtil::AddFeedbackData(
204 userfeedback::ExternalExtensionSubmit
* feedback_data
,
205 const std::string
& key
, const std::string
& value
) {
206 // Don't bother with empty keys or values
207 if (key
=="" || value
== "") return;
208 // Create log_value object and add it to the web_data object
209 userfeedback::ProductSpecificData log_value
;
210 log_value
.set_key(key
);
211 log_value
.set_value(value
);
212 userfeedback::WebData
* web_data
= feedback_data
->mutable_web_data();
213 *(web_data
->add_product_specific_data()) = log_value
;
216 #if defined(OS_CHROMEOS)
217 bool BugReportUtil::ValidFeedbackSize(const std::string
& content
) {
218 if (content
.length() > kMaxSystemLogLength
)
220 size_t line_count
= 0;
221 const char* text
= content
.c_str();
222 for (size_t i
= 0; i
< content
.length(); i
++) {
223 if (*(text
+ i
) == '\n') {
225 if (line_count
> kMaxLineCount
)
234 void BugReportUtil::SendReport(
237 , const std::string
& page_url_text
238 , const std::string
& description
239 , ScreenshotDataPtr image_data_ptr
242 #if defined(OS_CHROMEOS)
243 , const std::string
& user_email_text
244 , const char* zipped_logs_data
245 , int zipped_logs_length
246 , const chromeos::system::LogDictionaryType
* const sys_info
249 // Create google feedback protocol buffer objects
250 userfeedback::ExternalExtensionSubmit feedback_data
;
251 // type id set to 0, unused field but needs to be initialized to 0
252 feedback_data
.set_type_id(0);
254 userfeedback::CommonData
* common_data
= feedback_data
.mutable_common_data();
255 userfeedback::WebData
* web_data
= feedback_data
.mutable_web_data();
257 // Set GAIA id to 0. We're not using gaia id's for recording
258 // use feedback - we're using the e-mail field, allows users to
259 // submit feedback from incognito mode and specify any mail id
261 common_data
->set_gaia_id(0);
263 #if defined(OS_CHROMEOS)
264 // Add the user e-mail to the feedback object
265 common_data
->set_user_email(user_email_text
);
268 // Add the description to the feedback object
269 common_data
->set_description(description
);
272 std::string chrome_locale
= g_browser_process
->GetApplicationLocale();
273 common_data
->set_source_description_language(chrome_locale
);
276 web_data
->set_url(page_url_text
);
278 // Add the Chrome version
279 chrome::VersionInfo version_info
;
280 if (version_info
.is_valid()) {
281 std::string chrome_version
= version_info
.Name() + " - " +
282 version_info
.Version() +
283 " (" + version_info
.LastChange() + ")";
284 AddFeedbackData(&feedback_data
, std::string(kChromeVersionTag
),
288 // Add OS version (eg, for WinXP SP2: "5.1.2600 Service Pack 2").
289 std::string os_version
= "";
290 SetOSVersion(&os_version
);
291 AddFeedbackData(&feedback_data
, std::string(kOsVersionTag
), os_version
);
293 // Include the page image if we have one.
294 if (image_data_ptr
.get() && image_data_ptr
->size()) {
295 userfeedback::PostedScreenshot screenshot
;
296 screenshot
.set_mime_type(kPngMimeType
);
297 // Set the dimensions of the screenshot
298 userfeedback::Dimensions dimensions
;
299 dimensions
.set_width(static_cast<float>(png_width
));
300 dimensions
.set_height(static_cast<float>(png_height
));
301 *(screenshot
.mutable_dimensions()) = dimensions
;
303 int image_data_size
= image_data_ptr
->size();
304 char* image_data
= reinterpret_cast<char*>(&(image_data_ptr
->front()));
305 screenshot
.set_binary_content(std::string(image_data
, image_data_size
));
307 // Set the screenshot object in feedback
308 *(feedback_data
.mutable_screenshot()) = screenshot
;
311 #if defined(OS_CHROMEOS)
313 // Add the product specific data
314 for (chromeos::system::LogDictionaryType::const_iterator i
=
315 sys_info
->begin(); i
!= sys_info
->end(); ++i
) {
316 if (!CommandLine::ForCurrentProcess()->HasSwitch(
317 switches::kCompressSystemFeedback
) || ValidFeedbackSize(i
->second
)) {
318 AddFeedbackData(&feedback_data
, i
->first
, i
->second
);
322 // If we have zipped logs, add them here
323 if (zipped_logs_data
&& CommandLine::ForCurrentProcess()->HasSwitch(
324 switches::kCompressSystemFeedback
)) {
325 userfeedback::ProductSpecificBinaryData attachment
;
326 attachment
.set_mime_type(kBZip2MimeType
);
327 attachment
.set_name(kLogsAttachmentName
);
328 attachment
.set_data(std::string(zipped_logs_data
, zipped_logs_length
));
329 *(feedback_data
.add_product_specific_binary_data()) = attachment
;
334 // Set our Chrome specific data
335 userfeedback::ChromeData chrome_data
;
336 #if defined(OS_CHROMEOS)
337 chrome_data
.set_chrome_platform(
338 userfeedback::ChromeData_ChromePlatform_CHROME_OS
);
339 userfeedback::ChromeOsData chrome_os_data
;
340 chrome_os_data
.set_category(
341 (userfeedback::ChromeOsData_ChromeOsCategory
) problem_type
);
342 *(chrome_data
.mutable_chrome_os_data()) = chrome_os_data
;
344 chrome_data
.set_chrome_platform(
345 userfeedback::ChromeData_ChromePlatform_CHROME_BROWSER
);
346 userfeedback::ChromeBrowserData chrome_browser_data
;
347 chrome_browser_data
.set_category(
348 (userfeedback::ChromeBrowserData_ChromeBrowserCategory
) problem_type
);
349 *(chrome_data
.mutable_chrome_browser_data()) = chrome_browser_data
;
352 *(feedback_data
.mutable_chrome_data()) = chrome_data
;
354 // Serialize our report to a string pointer we can pass around
355 std::string
* post_body
= new std::string
;
356 feedback_data
.SerializeToString(post_body
);
358 // We have the body of our POST, so send it off to the server with 0 delay
359 DispatchFeedback(profile
, post_body
, 0);
362 #if defined(ENABLE_SAFE_BROWSING)
364 void BugReportUtil::ReportPhishing(TabContents
* currentTab
,
365 const std::string
& phishing_url
) {
366 currentTab
->GetController().LoadURL(
367 safe_browsing_util::GeneratePhishingReportUrl(
368 kReportPhishingUrl
, phishing_url
,
369 false /* not client-side detection */),
371 content::PAGE_TRANSITION_LINK
,
376 static std::vector
<unsigned char>* screenshot_png
= NULL
;
377 static gfx::Rect
* screenshot_size
= NULL
;
380 std::vector
<unsigned char>* BugReportUtil::GetScreenshotPng() {
381 if (screenshot_png
== NULL
)
382 screenshot_png
= new std::vector
<unsigned char>;
383 return screenshot_png
;
387 void BugReportUtil::ClearScreenshotPng() {
389 screenshot_png
->clear();
393 gfx::Rect
& BugReportUtil::GetScreenshotSize() {
394 if (screenshot_size
== NULL
)
395 screenshot_size
= new gfx::Rect();
396 return *screenshot_size
;
400 void BugReportUtil::SetScreenshotSize(const gfx::Rect
& rect
) {
401 gfx::Rect
& screen_size
= GetScreenshotSize();