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