| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/feedback/feedback_util.h" | 5 #include "chrome/browser/feedback/feedback_util.h" |
| 6 | 6 |
| 7 #include <sstream> | 7 #include <sstream> |
| 8 #include <string> | 8 #include <string> |
| 9 #include <vector> | 9 #include <vector> |
| 10 | 10 |
| 11 #include "base/bind.h" | 11 #include "base/bind.h" |
| 12 #include "base/command_line.h" | 12 #include "base/command_line.h" |
| 13 #include "base/file_util.h" | 13 #include "base/file_util.h" |
| 14 #include "base/file_version_info.h" | 14 #include "base/file_version_info.h" |
| 15 #include "base/memory/singleton.h" | 15 #include "base/memory/singleton.h" |
| 16 #include "base/message_loop/message_loop.h" | 16 #include "base/message_loop/message_loop.h" |
| 17 #include "base/strings/string_number_conversions.h" | 17 #include "base/strings/string_number_conversions.h" |
| 18 #include "base/strings/stringprintf.h" | 18 #include "base/strings/stringprintf.h" |
| 19 #include "base/strings/utf_string_conversions.h" | 19 #include "base/strings/utf_string_conversions.h" |
| 20 #include "base/win/windows_version.h" | 20 #include "base/win/windows_version.h" |
| 21 #include "chrome/browser/browser_process.h" | 21 #include "chrome/browser/browser_process.h" |
| 22 #include "chrome/browser/extensions/api/feedback_private/feedback_private_api.h" | 22 #include "chrome/browser/extensions/api/feedback_private/feedback_private_api.h" |
| 23 #include "chrome/browser/feedback/feedback_data.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" |
| 24 #include "chrome/browser/metrics/variations/variations_http_header_provider.h" | 26 #include "chrome/browser/metrics/variations/variations_http_header_provider.h" |
| 25 #include "chrome/browser/profiles/profile.h" | 27 #include "chrome/browser/profiles/profile.h" |
| 26 #include "chrome/browser/profiles/profile_manager.h" | 28 #include "chrome/browser/profiles/profile_manager.h" |
| 27 #include "chrome/browser/safe_browsing/safe_browsing_util.h" | 29 #include "chrome/browser/safe_browsing/safe_browsing_util.h" |
| 28 #include "chrome/browser/ui/browser_finder.h" | 30 #include "chrome/browser/ui/browser_finder.h" |
| 29 #include "chrome/browser/ui/browser_list.h" | 31 #include "chrome/browser/ui/browser_list.h" |
| 30 #include "chrome/browser/ui/browser_window.h" | 32 #include "chrome/browser/ui/browser_window.h" |
| 31 #include "chrome/browser/ui/tabs/tab_strip_model.h" | 33 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| 32 #include "chrome/common/chrome_switches.h" | 34 #include "chrome/common/chrome_switches.h" |
| 33 #include "chrome/common/chrome_version_info.h" | 35 #include "chrome/common/chrome_version_info.h" |
| (...skipping 16 matching lines...) Expand all Loading... |
| 50 #include "url/gurl.h" | 52 #include "url/gurl.h" |
| 51 | 53 |
| 52 #if defined(OS_CHROMEOS) | 54 #if defined(OS_CHROMEOS) |
| 53 #include "ash/shell.h" | 55 #include "ash/shell.h" |
| 54 #include "ui/aura/root_window.h" | 56 #include "ui/aura/root_window.h" |
| 55 #include "ui/aura/window.h" | 57 #include "ui/aura/window.h" |
| 56 #endif | 58 #endif |
| 57 | 59 |
| 58 namespace { | 60 namespace { |
| 59 | 61 |
| 60 void DispatchFeedback(Profile* profile, std::string* post_body, int64 delay); | |
| 61 | |
| 62 GURL GetTargetTabUrl(int session_id, int index) { | 62 GURL GetTargetTabUrl(int session_id, int index) { |
| 63 Browser* browser = chrome::FindBrowserWithID(session_id); | 63 Browser* browser = chrome::FindBrowserWithID(session_id); |
| 64 // Sanity checks. | 64 // Sanity checks. |
| 65 if (!browser || index >= browser->tab_strip_model()->count()) | 65 if (!browser || index >= browser->tab_strip_model()->count()) |
| 66 return GURL(); | 66 return GURL(); |
| 67 | 67 |
| 68 if (index >= 0) { | 68 if (index >= 0) { |
| 69 content::WebContents* target_tab = | 69 content::WebContents* target_tab = |
| 70 browser->tab_strip_model()->GetWebContentsAt(index); | 70 browser->tab_strip_model()->GetWebContentsAt(index); |
| 71 if (target_tab) | 71 if (target_tab) |
| 72 return target_tab->GetURL(); | 72 return target_tab->GetURL(); |
| 73 } | 73 } |
| 74 | 74 |
| 75 return GURL(); | 75 return GURL(); |
| 76 } | 76 } |
| 77 | 77 |
| 78 // URL to post bug reports to. | |
| 79 const char kFeedbackPostUrl[] = | |
| 80 "https://www.google.com/tools/feedback/chrome/__submit"; | |
| 81 | |
| 82 const char kProtBufMimeType[] = "application/x-protobuf"; | |
| 83 const char kPngMimeType[] = "image/png"; | 78 const char kPngMimeType[] = "image/png"; |
| 84 | |
| 85 const int kHttpPostSuccessNoContent = 204; | |
| 86 const int kHttpPostFailNoConnection = -1; | |
| 87 const int kHttpPostFailClientError = 400; | |
| 88 const int kHttpPostFailServerError = 500; | |
| 89 | |
| 90 const int64 kInitialRetryDelay = 900000; // 15 minutes | |
| 91 const int64 kRetryDelayIncreaseFactor = 2; | |
| 92 const int64 kRetryDelayLimit = 14400000; // 4 hours | |
| 93 | |
| 94 const char kArbitraryMimeType[] = "application/octet-stream"; | 79 const char kArbitraryMimeType[] = "application/octet-stream"; |
| 95 const char kHistogramsAttachmentName[] = "histograms.zip"; | 80 const char kHistogramsAttachmentName[] = "histograms.zip"; |
| 96 const char kLogsAttachmentName[] = "system_logs.zip"; | 81 const char kLogsAttachmentName[] = "system_logs.zip"; |
| 97 | 82 |
| 98 #if defined(OS_CHROMEOS) | 83 #if defined(OS_CHROMEOS) |
| 99 const int kChromeOSProductId = 208; | 84 const int kChromeOSProductId = 208; |
| 100 #else | 85 #else |
| 101 const int kChromeBrowserProductId = 237; | 86 const int kChromeBrowserProductId = 237; |
| 102 #endif | 87 #endif |
| 103 | 88 |
| 104 // Simple net::URLFetcherDelegate to clean up URLFetcher on completion. | |
| 105 class PostCleanup : public net::URLFetcherDelegate { | |
| 106 public: | |
| 107 PostCleanup(Profile* profile, | |
| 108 std::string* post_body, | |
| 109 int64 previous_delay) : profile_(profile), | |
| 110 post_body_(post_body), | |
| 111 previous_delay_(previous_delay) { } | |
| 112 // Overridden from net::URLFetcherDelegate. | |
| 113 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE; | |
| 114 | |
| 115 protected: | |
| 116 virtual ~PostCleanup() {} | |
| 117 | |
| 118 private: | |
| 119 Profile* profile_; | |
| 120 std::string* post_body_; | |
| 121 int64 previous_delay_; | |
| 122 | |
| 123 DISALLOW_COPY_AND_ASSIGN(PostCleanup); | |
| 124 }; | |
| 125 | |
| 126 // Don't use the data parameter, instead use the pointer we pass into every | |
| 127 // post cleanup object - that pointer will be deleted and deleted only on a | |
| 128 // successful post to the feedback server. | |
| 129 void PostCleanup::OnURLFetchComplete( | |
| 130 const net::URLFetcher* source) { | |
| 131 std::stringstream error_stream; | |
| 132 int response_code = source->GetResponseCode(); | |
| 133 if (response_code == kHttpPostSuccessNoContent) { | |
| 134 // We've sent our report, delete the report data | |
| 135 delete post_body_; | |
| 136 | |
| 137 error_stream << "Success"; | |
| 138 } else { | |
| 139 // Uh oh, feedback failed, send it off to retry | |
| 140 if (previous_delay_) { | |
| 141 if (previous_delay_ < kRetryDelayLimit) | |
| 142 previous_delay_ *= kRetryDelayIncreaseFactor; | |
| 143 } else { | |
| 144 previous_delay_ = kInitialRetryDelay; | |
| 145 } | |
| 146 DispatchFeedback(profile_, post_body_, previous_delay_); | |
| 147 | |
| 148 // Process the error for debug output | |
| 149 if (response_code == kHttpPostFailNoConnection) { | |
| 150 error_stream << "No connection to server."; | |
| 151 } else if ((response_code > kHttpPostFailClientError) && | |
| 152 (response_code < kHttpPostFailServerError)) { | |
| 153 error_stream << "Client error: HTTP response code " << response_code; | |
| 154 } else if (response_code > kHttpPostFailServerError) { | |
| 155 error_stream << "Server error: HTTP response code " << response_code; | |
| 156 } else { | |
| 157 error_stream << "Unknown error: HTTP response code " << response_code; | |
| 158 } | |
| 159 } | |
| 160 | |
| 161 LOG(WARNING) << "FEEDBACK: Submission to feedback server (" << | |
| 162 source->GetURL() << ") status: " << error_stream.str(); | |
| 163 | |
| 164 // Delete the URLFetcher. | |
| 165 delete source; | |
| 166 // And then delete ourselves. | |
| 167 delete this; | |
| 168 } | |
| 169 | |
| 170 void SendFeedback(Profile* profile, | |
| 171 std::string* post_body, | |
| 172 int64 previous_delay) { | |
| 173 DCHECK(post_body); | |
| 174 | |
| 175 GURL post_url; | |
| 176 if (CommandLine::ForCurrentProcess()-> | |
| 177 HasSwitch(switches::kFeedbackServer)) | |
| 178 post_url = GURL(CommandLine::ForCurrentProcess()-> | |
| 179 GetSwitchValueASCII(switches::kFeedbackServer)); | |
| 180 else | |
| 181 post_url = GURL(kFeedbackPostUrl); | |
| 182 | |
| 183 net::URLFetcher* fetcher = net::URLFetcher::Create( | |
| 184 post_url, net::URLFetcher::POST, | |
| 185 new PostCleanup(profile, post_body, previous_delay)); | |
| 186 fetcher->SetRequestContext(profile->GetRequestContext()); | |
| 187 fetcher->SetLoadFlags( | |
| 188 net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES); | |
| 189 | |
| 190 net::HttpRequestHeaders headers; | |
| 191 chrome_variations::VariationsHttpHeaderProvider::GetInstance()->AppendHeaders( | |
| 192 fetcher->GetOriginalURL(), profile->IsOffTheRecord(), false, &headers); | |
| 193 fetcher->SetExtraRequestHeaders(headers.ToString()); | |
| 194 | |
| 195 fetcher->SetUploadData(std::string(kProtBufMimeType), *post_body); | |
| 196 fetcher->Start(); | |
| 197 } | |
| 198 | |
| 199 void DispatchFeedback(Profile* profile, std::string* post_body, int64 delay) { | |
| 200 DCHECK(post_body); | |
| 201 | |
| 202 base::MessageLoop::current()->PostDelayedTask( | |
| 203 FROM_HERE, | |
| 204 base::Bind(&SendFeedback, profile, post_body, delay), | |
| 205 base::TimeDelta::FromMilliseconds(delay)); | |
| 206 } | |
| 207 | |
| 208 | |
| 209 void AddFeedbackData(userfeedback::ExtensionSubmit* feedback_data, | 89 void AddFeedbackData(userfeedback::ExtensionSubmit* feedback_data, |
| 210 const std::string& key, const std::string& value) { | 90 const std::string& key, const std::string& value) { |
| 211 // Don't bother with empty keys or values | 91 // Don't bother with empty keys or values |
| 212 if (key == "" || value == "") return; | 92 if (key == "" || value == "") return; |
| 213 // Create log_value object and add it to the web_data object | 93 // Create log_value object and add it to the web_data object |
| 214 userfeedback::ProductSpecificData log_value; | 94 userfeedback::ProductSpecificData log_value; |
| 215 log_value.set_key(key); | 95 log_value.set_key(key); |
| 216 log_value.set_value(value); | 96 log_value.set_value(value); |
| 217 userfeedback::WebData* web_data = feedback_data->mutable_web_data(); | 97 userfeedback::WebData* web_data = feedback_data->mutable_web_data(); |
| 218 *(web_data->add_product_specific_data()) = log_value; | 98 *(web_data->add_product_specific_data()) = log_value; |
| (...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 358 chrome_browser_data.set_category( | 238 chrome_browser_data.set_category( |
| 359 userfeedback::ChromeBrowserData_ChromeBrowserCategory_OTHER); | 239 userfeedback::ChromeBrowserData_ChromeBrowserCategory_OTHER); |
| 360 *(chrome_data.mutable_chrome_browser_data()) = chrome_browser_data; | 240 *(chrome_data.mutable_chrome_browser_data()) = chrome_browser_data; |
| 361 feedback_data.set_product_id(kChromeBrowserProductId); | 241 feedback_data.set_product_id(kChromeBrowserProductId); |
| 362 #endif | 242 #endif |
| 363 | 243 |
| 364 *(feedback_data.mutable_chrome_data()) = chrome_data; | 244 *(feedback_data.mutable_chrome_data()) = chrome_data; |
| 365 | 245 |
| 366 // This pointer will eventually get deleted by the PostCleanup class, after | 246 // This pointer will eventually get deleted by the PostCleanup class, after |
| 367 // we've either managed to successfully upload the report or died trying. | 247 // we've either managed to successfully upload the report or died trying. |
| 368 std::string* post_body = new std::string; | 248 scoped_ptr<std::string> post_body(new std::string); |
| 369 feedback_data.SerializeToString(post_body); | 249 feedback_data.SerializeToString(post_body.get()); |
| 370 | 250 |
| 371 DispatchFeedback(data->profile(), post_body, 0); | 251 feedback::FeedbackUploader *uploader = |
| 252 feedback::FeedbackUploaderFactory::GetForBrowserContext(data->profile()); |
| 253 uploader->QueueReport(post_body.Pass()); |
| 372 } | 254 } |
| 373 | 255 |
| 374 bool ZipString(const base::FilePath& filename, | 256 bool ZipString(const base::FilePath& filename, |
| 375 const std::string& data, std::string* compressed_logs) { | 257 const std::string& data, std::string* compressed_logs) { |
| 376 base::FilePath temp_path; | 258 base::FilePath temp_path; |
| 377 base::FilePath zip_file; | 259 base::FilePath zip_file; |
| 378 | 260 |
| 379 // Create a temporary directory, put the logs into a file in it. Create | 261 // Create a temporary directory, put the logs into a file in it. Create |
| 380 // another temporary file to receive the zip file in. | 262 // another temporary file to receive the zip file in. |
| 381 if (!base::CreateNewTempDirectory(base::FilePath::StringType(), &temp_path)) | 263 if (!base::CreateNewTempDirectory(base::FilePath::StringType(), &temp_path)) |
| 382 return false; | 264 return false; |
| 383 if (file_util::WriteFile(temp_path.Append(filename), | 265 if (file_util::WriteFile(temp_path.Append(filename), |
| 384 data.c_str(), data.size()) == -1) | 266 data.c_str(), data.size()) == -1) |
| 385 return false; | 267 return false; |
| 386 | 268 |
| 387 bool succeed = base::CreateTemporaryFile(&zip_file) && | 269 bool succeed = base::CreateTemporaryFile(&zip_file) && |
| 388 zip::Zip(temp_path, zip_file, false) && | 270 zip::Zip(temp_path, zip_file, false) && |
| 389 base::ReadFileToString(zip_file, compressed_logs); | 271 base::ReadFileToString(zip_file, compressed_logs); |
| 390 | 272 |
| 391 base::DeleteFile(temp_path, true); | 273 base::DeleteFile(temp_path, true); |
| 392 base::DeleteFile(zip_file, false); | 274 base::DeleteFile(zip_file, false); |
| 393 | 275 |
| 394 return succeed; | 276 return succeed; |
| 395 } | 277 } |
| 396 | 278 |
| 397 } // namespace feedback_util | 279 } // namespace feedback_util |
| OLD | NEW |