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.
5 #include "chrome/browser/feedback/feedback_util.h"
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_switches.h"
35 #include "chrome/common/chrome_version_info.h"
36 #include "chrome/common/metrics/metrics_log_manager.h"
37 #include "content/public/browser/browser_thread.h"
38 #include "content/public/browser/navigation_controller.h"
39 #include "content/public/browser/web_contents.h"
40 #include "content/public/common/content_client.h"
41 #include "grit/generated_resources.h"
42 #include "grit/locale_settings.h"
43 #include "grit/theme_resources.h"
44 #include "net/base/load_flags.h"
45 #include "net/http/http_request_headers.h"
46 #include "net/url_request/url_fetcher.h"
47 #include "net/url_request/url_fetcher_delegate.h"
48 #include "net/url_request/url_request_status.h"
49 #include "third_party/icu/source/common/unicode/locid.h"
50 #include "third_party/zlib/google/zip.h"
51 #include "ui/base/l10n/l10n_util.h"
56 GURL
GetTargetTabUrl(int session_id
, int index
) {
57 Browser
* browser
= chrome::FindBrowserWithID(session_id
);
59 if (!browser
|| index
>= browser
->tab_strip_model()->count())
63 content::WebContents
* target_tab
=
64 browser
->tab_strip_model()->GetWebContentsAt(index
);
66 return target_tab
->GetURL();
72 const char kPngMimeType
[] = "image/png";
73 const char kArbitraryMimeType
[] = "application/octet-stream";
74 const char kHistogramsAttachmentName
[] = "histograms.zip";
75 const char kLogsAttachmentName
[] = "system_logs.zip";
77 #if defined(OS_CHROMEOS)
78 const int kChromeOSProductId
= 208;
80 const int kChromeBrowserProductId
= 237;
83 void AddFeedbackData(userfeedback::ExtensionSubmit
* feedback_data
,
84 const std::string
& key
, const std::string
& value
) {
85 // Don't bother with empty keys or values
86 if (key
== "" || value
== "") return;
87 // Create log_value object and add it to the web_data object
88 userfeedback::ProductSpecificData log_value
;
89 log_value
.set_key(key
);
90 log_value
.set_value(value
);
91 userfeedback::WebData
* web_data
= feedback_data
->mutable_web_data();
92 *(web_data
->add_product_specific_data()) = log_value
;
95 // Adds data as an attachment to feedback_data if the data is non-empty.
96 void AddAttachment(userfeedback::ExtensionSubmit
* feedback_data
,
99 if (data
== NULL
|| data
->empty())
102 userfeedback::ProductSpecificBinaryData
* attachment
=
103 feedback_data
->add_product_specific_binary_data();
104 attachment
->set_mime_type(kArbitraryMimeType
);
105 attachment
->set_name(name
);
106 attachment
->set_data(*data
);
113 const char kAppLauncherCategoryTag
[] = "AppLauncher";
115 void ShowFeedbackPage(Browser
* browser
,
116 const std::string
& description_template
,
117 const std::string
& category_tag
) {
120 page_url
= GetTargetTabUrl(browser
->session_id().id(),
121 browser
->tab_strip_model()->active_index());
124 Profile
* profile
= NULL
;
126 profile
= browser
->profile();
128 profile
= ProfileManager::GetLastUsedProfileAllowedByPolicy();
131 LOG(ERROR
) << "Cannot invoke feedback: No profile found!";
135 extensions::FeedbackPrivateAPI
* api
=
136 extensions::FeedbackPrivateAPI::GetFactoryInstance()->GetForProfile(
139 api
->RequestFeedback(description_template
,
144 } // namespace chrome
146 namespace feedback_util
{
148 void SendReport(scoped_refptr
<FeedbackData
> data
) {
150 LOG(ERROR
) << "SendReport called with NULL data!";
155 userfeedback::ExtensionSubmit feedback_data
;
156 // Unused field, needs to be 0 though.
157 feedback_data
.set_type_id(0);
159 userfeedback::CommonData
* common_data
= feedback_data
.mutable_common_data();
160 // We're not using gaia ids, we're using the e-mail field instead.
161 common_data
->set_gaia_id(0);
162 common_data
->set_user_email(data
->user_email());
163 common_data
->set_description(data
->description());
165 std::string chrome_locale
= g_browser_process
->GetApplicationLocale();
166 common_data
->set_source_description_language(chrome_locale
);
168 userfeedback::WebData
* web_data
= feedback_data
.mutable_web_data();
169 web_data
->set_url(data
->page_url());
170 web_data
->mutable_navigator()->set_user_agent(content::GetUserAgent(GURL()));
172 gfx::Rect screen_size
;
173 if (data
->sys_info()) {
174 for (FeedbackData::SystemLogsMap::const_iterator i
=
175 data
->sys_info()->begin(); i
!= data
->sys_info()->end(); ++i
) {
176 if (FeedbackData::BelowCompressionThreshold(i
->second
))
177 AddFeedbackData(&feedback_data
, i
->first
, i
->second
);
180 AddAttachment(&feedback_data
, kLogsAttachmentName
, data
->compressed_logs());
183 if (data
->histograms()) {
184 AddAttachment(&feedback_data
,
185 kHistogramsAttachmentName
,
186 data
->compressed_histograms());
189 if (!data
->attached_filename().empty()) {
190 // We need to use the UTF8Unsafe methods here to accomodate Windows, which
191 // uses wide strings to store filepaths.
192 std::string name
= base::FilePath::FromUTF8Unsafe(
193 data
->attached_filename()).BaseName().AsUTF8Unsafe();
194 AddAttachment(&feedback_data
, name
.c_str(), data
->attached_filedata());
197 // NOTE: Screenshot needs to be processed after system info since we'll get
198 // the screenshot dimensions from system info.
199 if (data
->image() && data
->image()->size()) {
200 userfeedback::PostedScreenshot screenshot
;
201 screenshot
.set_mime_type(kPngMimeType
);
203 // Set that we 'have' dimensions of the screenshot. These dimensions are
204 // ignored by the server but are a 'required' field in the protobuf.
205 userfeedback::Dimensions dimensions
;
206 dimensions
.set_width(0.0);
207 dimensions
.set_height(0.0);
209 *(screenshot
.mutable_dimensions()) = dimensions
;
210 screenshot
.set_binary_content(*data
->image());
212 *(feedback_data
.mutable_screenshot()) = screenshot
;
215 if (data
->category_tag().size())
216 feedback_data
.set_bucket(data
->category_tag());
218 // Set whether we're reporting from ChromeOS or Chrome on another platform.
219 userfeedback::ChromeData chrome_data
;
220 #if defined(OS_CHROMEOS)
221 chrome_data
.set_chrome_platform(
222 userfeedback::ChromeData_ChromePlatform_CHROME_OS
);
223 userfeedback::ChromeOsData chrome_os_data
;
224 chrome_os_data
.set_category(
225 userfeedback::ChromeOsData_ChromeOsCategory_OTHER
);
226 *(chrome_data
.mutable_chrome_os_data()) = chrome_os_data
;
227 feedback_data
.set_product_id(kChromeOSProductId
);
229 chrome_data
.set_chrome_platform(
230 userfeedback::ChromeData_ChromePlatform_CHROME_BROWSER
);
231 userfeedback::ChromeBrowserData chrome_browser_data
;
232 chrome_browser_data
.set_category(
233 userfeedback::ChromeBrowserData_ChromeBrowserCategory_OTHER
);
234 *(chrome_data
.mutable_chrome_browser_data()) = chrome_browser_data
;
235 feedback_data
.set_product_id(kChromeBrowserProductId
);
238 *(feedback_data
.mutable_chrome_data()) = chrome_data
;
240 // This pointer will eventually get deleted by the PostCleanup class, after
241 // we've either managed to successfully upload the report or died trying.
242 scoped_ptr
<std::string
> post_body(new std::string
);
243 feedback_data
.SerializeToString(post_body
.get());
245 feedback::FeedbackUploader
*uploader
=
246 feedback::FeedbackUploaderFactory::GetForBrowserContext(data
->profile());
247 uploader
->QueueReport(post_body
.Pass());
250 bool ZipString(const base::FilePath
& filename
,
251 const std::string
& data
, std::string
* compressed_logs
) {
252 base::FilePath temp_path
;
253 base::FilePath zip_file
;
255 // Create a temporary directory, put the logs into a file in it. Create
256 // another temporary file to receive the zip file in.
257 if (!base::CreateNewTempDirectory(base::FilePath::StringType(), &temp_path
))
259 if (file_util::WriteFile(temp_path
.Append(filename
),
260 data
.c_str(), data
.size()) == -1)
263 bool succeed
= base::CreateTemporaryFile(&zip_file
) &&
264 zip::Zip(temp_path
, zip_file
, false) &&
265 base::ReadFileToString(zip_file
, compressed_logs
);
267 base::DeleteFile(temp_path
, true);
268 base::DeleteFile(zip_file
, false);
273 } // namespace feedback_util