bug_report_util.cc revision 3345a6884c488ff3a535c2c9acdd33d74b37e311
1// Copyright (c) 2010 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/bug_report_util.h" 6 7#include <sstream> 8#include <string> 9 10#include "app/l10n_util.h" 11#include "base/command_line.h" 12#include "base/file_version_info.h" 13#include "base/file_util.h" 14#include "base/singleton.h" 15#include "base/string_util.h" 16#include "base/utf_string_conversions.h" 17#include "chrome/browser/browser_list.h" 18#include "chrome/browser/browser_process_impl.h" 19#include "chrome/browser/profile.h" 20#include "chrome/browser/safe_browsing/safe_browsing_util.h" 21#include "chrome/browser/tab_contents/tab_contents.h" 22#include "chrome/common/chrome_version_info.h" 23#include "chrome/common/chrome_switches.h" 24#include "chrome/common/net/url_fetcher.h" 25#include "googleurl/src/gurl.h" 26#include "grit/generated_resources.h" 27#include "grit/locale_settings.h" 28#include "grit/theme_resources.h" 29#include "net/url_request/url_request_status.h" 30#include "unicode/locid.h" 31 32#if defined(OS_CHROMEOS) 33#include "chrome/browser/chromeos/notifications/system_notification.h" 34#endif 35 36namespace { 37 38const int kBugReportVersion = 1; 39 40const char kReportPhishingUrl[] = 41 "http://www.google.com/safebrowsing/report_phish/"; 42 43// URL to post bug reports to. 44static char const kBugReportPostUrl[] = 45 "https://www.google.com/tools/feedback/chrome/__submit"; 46 47static char const kProtBufMimeType[] = "application/x-protobuf"; 48static char const kPngMimeType[] = "image/png"; 49 50// Tags we use in product specific data 51static char const kPageTitleTag[] = "PAGE TITLE"; 52static char const kProblemTypeIdTag[] = "PROBLEM TYPE ID"; 53static char const kProblemTypeTag[] = "PROBLEM TYPE"; 54static char const kChromeVersionTag[] = "CHROME VERSION"; 55static char const kOsVersionTag[] = "OS VERSION"; 56 57static char const kNotificationId[] = "feedback.chromeos"; 58 59static int const kHttpPostSuccessNoContent = 204; 60static int const kHttpPostFailNoConnection = -1; 61static int const kHttpPostFailClientError = 400; 62static int const kHttpPostFailServerError = 500; 63 64} // namespace 65 66 67#if defined(OS_CHROMEOS) 68class FeedbackNotification { 69 public: 70 // Previous notification cleanup is handled by scoped_ptr. 71 // Note: notification will show only on one profile at a time. 72 void Show(Profile* profile, const string16& message, bool urgent) { 73 notification_.reset( 74 new chromeos::SystemNotification(profile, kNotificationId, 75 IDR_STATUSBAR_FEEDBACK, 76 l10n_util::GetStringUTF16( 77 IDS_BUGREPORT_NOTIFICATION_TITLE))); 78 notification_->Show(message, urgent, false); 79 } 80 81 private: 82 FeedbackNotification() {} 83 friend struct DefaultSingletonTraits<FeedbackNotification>; 84 85 scoped_ptr<chromeos::SystemNotification> notification_; 86 DISALLOW_COPY_AND_ASSIGN(FeedbackNotification); 87}; 88#endif 89 90// Simple URLFetcher::Delegate to clean up URLFetcher on completion. 91class BugReportUtil::PostCleanup : public URLFetcher::Delegate { 92 public: 93#if defined(OS_CHROMEOS) 94 explicit PostCleanup(Profile* profile); 95#else 96 PostCleanup(); 97#endif 98 // Overridden from URLFetcher::Delegate. 99 virtual void OnURLFetchComplete(const URLFetcher* source, 100 const GURL& url, 101 const URLRequestStatus& status, 102 int response_code, 103 const ResponseCookies& cookies, 104 const std::string& data); 105 106 protected: 107 virtual ~PostCleanup() {} 108 109 private: 110 Profile* profile_; 111 112 DISALLOW_COPY_AND_ASSIGN(PostCleanup); 113}; 114 115#if defined(OS_CHROMEOS) 116 BugReportUtil::PostCleanup::PostCleanup(Profile* profile) 117 : profile_(profile) { 118#else 119 BugReportUtil::PostCleanup::PostCleanup() { 120#endif 121} 122 123void BugReportUtil::PostCleanup::OnURLFetchComplete( 124 const URLFetcher* source, 125 const GURL& url, 126 const URLRequestStatus& status, 127 int response_code, 128 const ResponseCookies& cookies, 129 const std::string& data) { 130 131 std::stringstream error_stream; 132 if (response_code == kHttpPostSuccessNoContent) { 133 error_stream << "Success"; 134 } else if (response_code == kHttpPostFailNoConnection) { 135 error_stream << "No connection to server."; 136 } else if ((response_code > kHttpPostFailClientError) && 137 (response_code < kHttpPostFailServerError)) { 138 error_stream << "Client error: HTTP response code " << response_code; 139 } else if (response_code > kHttpPostFailServerError) { 140 error_stream << "Server error: HTTP response code " << response_code; 141 } else { 142 error_stream << "Unknown error: HTTP response code " << response_code; 143 } 144 145 LOG(WARNING) << "Submission to feedback server (" << url << 146 ") status: " << error_stream.str() << std::endl; 147 148#if defined(OS_CHROMEOS) 149 // Show the notification to the user; this notification will stay active till 150 // either the user closes it, or we display another notification. 151 if (response_code == kHttpPostSuccessNoContent) { 152 Singleton<FeedbackNotification>()->Show(profile_, l10n_util::GetStringUTF16( 153 IDS_BUGREPORT_FEEDBACK_STATUS_SUCCESS), false); 154 } else { 155 Singleton<FeedbackNotification>()->Show(profile_, 156 l10n_util::GetStringFUTF16(IDS_BUGREPORT_FEEDBACK_STATUS_FAIL, 157 ASCIIToUTF16(error_stream.str())), 158 true); 159 } 160#endif 161 162 // Delete the URLFetcher. 163 delete source; 164 // And then delete ourselves. 165 delete this; 166} 167 168// static 169void BugReportUtil::SetOSVersion(std::string *os_version) { 170#if defined(OS_WIN) 171 OSVERSIONINFO osvi; 172 ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); 173 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); 174 175 if (GetVersionEx(&osvi)) { 176 *os_version = StringPrintf("%d.%d.%d %S", 177 osvi.dwMajorVersion, 178 osvi.dwMinorVersion, 179 osvi.dwBuildNumber, 180 osvi.szCSDVersion); 181 } else { 182 *os_version = "unknown"; 183 } 184#elif defined(OS_MACOSX) 185 int32 major; 186 int32 minor; 187 int32 bugFix; 188 base::SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugFix); 189 *os_version = StringPrintf("%d.%d.%d", major, minor, bugFix); 190#else 191 *os_version = "unknown"; 192#endif 193} 194 195// static 196std::string BugReportUtil::feedback_server_(""); 197 198// static 199void BugReportUtil::SetFeedbackServer(const std::string& server) { 200 feedback_server_ = server; 201} 202 203 204// static 205void BugReportUtil::AddFeedbackData( 206 userfeedback::ExternalExtensionSubmit* feedback_data, 207 const std::string& key, const std::string& value) { 208 // We have no reason to log any empty values - gives us no data 209 if (value == "") return; 210 // Create log_value object and add it to the web_data object 211 userfeedback::ProductSpecificData log_value; 212 log_value.set_key(key); 213 log_value.set_value(value); 214 userfeedback::WebData* web_data = feedback_data->mutable_web_data(); 215 *(web_data->add_product_specific_data()) = log_value; 216} 217 218// static 219void BugReportUtil::SendReport(Profile* profile, 220 const std::string& page_title_text, 221 int problem_type, 222 const std::string& page_url_text, 223 const std::string& description, 224 const char* png_data, 225 int png_data_length, 226 int png_width, 227#if defined(OS_CHROMEOS) 228 int png_height, 229 const std::string& user_email_text, 230 const chromeos::LogDictionaryType* const sys_info) { 231#else 232 int png_height) { 233#endif 234 GURL post_url; 235 236 if (CommandLine::ForCurrentProcess()-> 237 HasSwitch(switches::kFeedbackServer)) 238 post_url = GURL(CommandLine::ForCurrentProcess()-> 239 GetSwitchValueASCII(switches::kFeedbackServer)); 240 else 241 post_url = GURL(kBugReportPostUrl); 242 243 // Create google feedback protocol buffer objects 244 userfeedback::ExternalExtensionSubmit feedback_data; 245 // type id set to 0, unused field but needs to be initialized to 0 246 feedback_data.set_type_id(0); 247 248 userfeedback::CommonData* common_data = feedback_data.mutable_common_data(); 249 userfeedback::WebData* web_data = feedback_data.mutable_web_data(); 250 251 // Set GAIA id to 0. We're not using gaia id's for recording 252 // use feedback - we're using the e-mail field, allows users to 253 // submit feedback from incognito mode and specify any mail id 254 // they wish 255 common_data->set_gaia_id(0); 256 257 // Add the page title. 258 AddFeedbackData(&feedback_data, std::string(kPageTitleTag), 259 page_title_text); 260 261#if defined(OS_CHROMEOS) 262 // Add the user e-mail to the feedback object 263 common_data->set_user_email(user_email_text); 264#endif 265 266 // Add the description to the feedback object 267 common_data->set_description(description); 268 269 // Add the language 270 std::string chrome_locale = g_browser_process->GetApplicationLocale(); 271 common_data->set_source_description_language(chrome_locale); 272 273 // Set the url 274 web_data->set_url(page_url_text); 275 276 // Add the Chrome version 277 chrome::VersionInfo version_info; 278 if (version_info.is_valid()) { 279 std::string chrome_version = version_info.Name() + " - " + 280 version_info.Version() + 281 " (" + version_info.LastChange() + ")"; 282 AddFeedbackData(&feedback_data, std::string(kChromeVersionTag), 283 chrome_version); 284 } 285 286 // Add OS version (eg, for WinXP SP2: "5.1.2600 Service Pack 2"). 287 std::string os_version = ""; 288 SetOSVersion(&os_version); 289 AddFeedbackData(&feedback_data, std::string(kOsVersionTag), os_version); 290 291#if defined(OS_CHROMEOS) 292 if (sys_info) { 293 for (chromeos::LogDictionaryType::const_iterator i = sys_info->begin(); 294 i != sys_info->end(); ++i) 295 AddFeedbackData(&feedback_data, i->first, i->second); 296 } 297#endif 298 299 // Include the page image if we have one. 300 if (png_data) { 301 userfeedback::PostedScreenshot screenshot; 302 screenshot.set_mime_type(kPngMimeType); 303 // Set the dimensions of the screenshot 304 userfeedback::Dimensions dimensions; 305 dimensions.set_width(static_cast<float>(png_width)); 306 dimensions.set_height(static_cast<float>(png_height)); 307 *(screenshot.mutable_dimensions()) = dimensions; 308 screenshot.set_binary_content(std::string(png_data, png_data_length)); 309 310 // Set the screenshot object in feedback 311 *(feedback_data.mutable_screenshot()) = screenshot; 312 } 313 314 // Set our Chrome specific data 315 userfeedback::ChromeData chrome_data; 316#if defined(OS_CHROMEOS) 317 chrome_data.set_chrome_platform( 318 userfeedback::ChromeData_ChromePlatform_CHROME_OS); 319 userfeedback::ChromeOsData chrome_os_data; 320 chrome_os_data.set_category( 321 (userfeedback::ChromeOsData_ChromeOsCategory) problem_type); 322 *(chrome_data.mutable_chrome_os_data()) = chrome_os_data; 323#else 324 chrome_data.set_chrome_platform( 325 userfeedback::ChromeData_ChromePlatform_CHROME_BROWSER); 326 userfeedback::ChromeBrowserData chrome_browser_data; 327 chrome_browser_data.set_category( 328 (userfeedback::ChromeBrowserData_ChromeBrowserCategory) problem_type); 329 *(chrome_data.mutable_chrome_browser_data()) = chrome_browser_data; 330#endif 331 332 *(feedback_data.mutable_chrome_data()) = chrome_data; 333 334 // We have the body of our POST, so send it off to the server. 335 URLFetcher* fetcher = new URLFetcher(post_url, URLFetcher::POST, 336#if defined(OS_CHROMEOS) 337 new BugReportUtil::PostCleanup(profile)); 338#else 339 new BugReportUtil::PostCleanup()); 340#endif 341 fetcher->set_request_context(profile->GetRequestContext()); 342 343 std::string post_body; 344 feedback_data.SerializeToString(&post_body); 345 fetcher->set_upload_data(std::string(kProtBufMimeType), post_body); 346 fetcher->Start(); 347} 348 349// static 350void BugReportUtil::ReportPhishing(TabContents* currentTab, 351 const std::string& phishing_url) { 352 currentTab->controller().LoadURL( 353 safe_browsing_util::GeneratePhishingReportUrl( 354 kReportPhishingUrl, phishing_url), 355 GURL(), 356 PageTransition::LINK); 357} 358