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_content_client.h"
35 #include "chrome/common/chrome_switches.h"
36 #include "chrome/common/chrome_version_info.h"
37 #include "components/metrics/metrics_log_manager.h"
38 #include "content/public/browser/navigation_controller.h"
39 #include "content/public/browser/web_contents.h"
40 #include "grit/generated_resources.h"
41 #include "grit/locale_settings.h"
42 #include "grit/theme_resources.h"
43 #include "net/base/load_flags.h"
44 #include "net/http/http_request_headers.h"
45 #include "net/url_request/url_fetcher.h"
46 #include "net/url_request/url_fetcher_delegate.h"
47 #include "net/url_request/url_request_status.h"
48 #include "third_party/icu/source/common/unicode/locid.h"
49 #include "third_party/zlib/google/zip.h"
50 #include "ui/base/l10n/l10n_util.h"
55 GURL
GetTargetTabUrl(int session_id
, int index
) {
56 Browser
* browser
= chrome::FindBrowserWithID(session_id
);
58 if (!browser
|| index
>= browser
->tab_strip_model()->count())
62 content::WebContents
* target_tab
=
63 browser
->tab_strip_model()->GetWebContentsAt(index
);
65 return target_tab
->GetURL();
71 const char kPngMimeType
[] = "image/png";
72 const char kArbitraryMimeType
[] = "application/octet-stream";
73 const char kHistogramsAttachmentName
[] = "histograms.zip";
74 const char kLogsAttachmentName
[] = "system_logs.zip";
76 #if defined(OS_CHROMEOS)
77 const int kChromeOSProductId
= 208;
79 const int kChromeBrowserProductId
= 237;
82 void AddFeedbackData(userfeedback::ExtensionSubmit
* feedback_data
,
83 const std::string
& key
, const std::string
& value
) {
84 // Don't bother with empty keys or values
85 if (key
== "" || value
== "") return;
86 // Create log_value object and add it to the web_data object
87 userfeedback::ProductSpecificData log_value
;
88 log_value
.set_key(key
);
89 log_value
.set_value(value
);
90 userfeedback::WebData
* web_data
= feedback_data
->mutable_web_data();
91 *(web_data
->add_product_specific_data()) = log_value
;
94 // Adds data as an attachment to feedback_data if the data is non-empty.
95 void AddAttachment(userfeedback::ExtensionSubmit
* feedback_data
,
98 if (data
== NULL
|| data
->empty())
101 userfeedback::ProductSpecificBinaryData
* attachment
=
102 feedback_data
->add_product_specific_binary_data();
103 attachment
->set_mime_type(kArbitraryMimeType
);
104 attachment
->set_name(name
);
105 attachment
->set_data(*data
);
112 const char kAppLauncherCategoryTag
[] = "AppLauncher";
114 void ShowFeedbackPage(Browser
* browser
,
115 const std::string
& description_template
,
116 const std::string
& category_tag
) {
119 page_url
= GetTargetTabUrl(browser
->session_id().id(),
120 browser
->tab_strip_model()->active_index());
123 Profile
* profile
= NULL
;
125 profile
= browser
->profile();
127 profile
= ProfileManager::GetLastUsedProfileAllowedByPolicy();
130 LOG(ERROR
) << "Cannot invoke feedback: No profile found!";
134 // We do not want to launch on an OTR profile.
135 profile
= profile
->GetOriginalProfile();
138 extensions::FeedbackPrivateAPI
* api
=
139 extensions::FeedbackPrivateAPI::GetFactoryInstance()->Get(profile
);
141 api
->RequestFeedback(description_template
,
146 } // namespace chrome
148 namespace feedback_util
{
150 void SendReport(scoped_refptr
<FeedbackData
> data
) {
152 LOG(ERROR
) << "SendReport called with NULL data!";
157 userfeedback::ExtensionSubmit feedback_data
;
158 // Unused field, needs to be 0 though.
159 feedback_data
.set_type_id(0);
161 userfeedback::CommonData
* common_data
= feedback_data
.mutable_common_data();
162 // We're not using gaia ids, we're using the e-mail field instead.
163 common_data
->set_gaia_id(0);
164 common_data
->set_user_email(data
->user_email());
165 common_data
->set_description(data
->description());
167 std::string chrome_locale
= g_browser_process
->GetApplicationLocale();
168 common_data
->set_source_description_language(chrome_locale
);
170 userfeedback::WebData
* web_data
= feedback_data
.mutable_web_data();
171 web_data
->set_url(data
->page_url());
172 web_data
->mutable_navigator()->set_user_agent(GetUserAgent());
174 gfx::Rect screen_size
;
175 if (data
->sys_info()) {
176 for (FeedbackData::SystemLogsMap::const_iterator i
=
177 data
->sys_info()->begin(); i
!= data
->sys_info()->end(); ++i
) {
178 if (FeedbackData::BelowCompressionThreshold(i
->second
))
179 AddFeedbackData(&feedback_data
, i
->first
, i
->second
);
182 AddAttachment(&feedback_data
, kLogsAttachmentName
, data
->compressed_logs());
185 if (data
->histograms()) {
186 AddAttachment(&feedback_data
,
187 kHistogramsAttachmentName
,
188 data
->compressed_histograms());
191 if (!data
->attached_filename().empty()) {
192 // We need to use the UTF8Unsafe methods here to accomodate Windows, which
193 // uses wide strings to store filepaths.
194 std::string name
= base::FilePath::FromUTF8Unsafe(
195 data
->attached_filename()).BaseName().AsUTF8Unsafe();
196 AddAttachment(&feedback_data
, name
.c_str(), data
->attached_filedata());
199 // NOTE: Screenshot needs to be processed after system info since we'll get
200 // the screenshot dimensions from system info.
201 if (data
->image() && data
->image()->size()) {
202 userfeedback::PostedScreenshot screenshot
;
203 screenshot
.set_mime_type(kPngMimeType
);
205 // Set that we 'have' dimensions of the screenshot. These dimensions are
206 // ignored by the server but are a 'required' field in the protobuf.
207 userfeedback::Dimensions dimensions
;
208 dimensions
.set_width(0.0);
209 dimensions
.set_height(0.0);
211 *(screenshot
.mutable_dimensions()) = dimensions
;
212 screenshot
.set_binary_content(*data
->image());
214 *(feedback_data
.mutable_screenshot()) = screenshot
;
217 if (data
->category_tag().size())
218 feedback_data
.set_bucket(data
->category_tag());
220 // Set whether we're reporting from ChromeOS or Chrome on another platform.
221 userfeedback::ChromeData chrome_data
;
222 #if defined(OS_CHROMEOS)
223 chrome_data
.set_chrome_platform(
224 userfeedback::ChromeData_ChromePlatform_CHROME_OS
);
225 userfeedback::ChromeOsData chrome_os_data
;
226 chrome_os_data
.set_category(
227 userfeedback::ChromeOsData_ChromeOsCategory_OTHER
);
228 *(chrome_data
.mutable_chrome_os_data()) = chrome_os_data
;
229 feedback_data
.set_product_id(kChromeOSProductId
);
231 chrome_data
.set_chrome_platform(
232 userfeedback::ChromeData_ChromePlatform_CHROME_BROWSER
);
233 userfeedback::ChromeBrowserData chrome_browser_data
;
234 chrome_browser_data
.set_category(
235 userfeedback::ChromeBrowserData_ChromeBrowserCategory_OTHER
);
236 *(chrome_data
.mutable_chrome_browser_data()) = chrome_browser_data
;
237 feedback_data
.set_product_id(kChromeBrowserProductId
);
240 *(feedback_data
.mutable_chrome_data()) = chrome_data
;
242 // This pointer will eventually get deleted by the PostCleanup class, after
243 // we've either managed to successfully upload the report or died trying.
244 std::string post_body
;
245 feedback_data
.SerializeToString(&post_body
);
247 feedback::FeedbackUploader
*uploader
=
248 feedback::FeedbackUploaderFactory::GetForBrowserContext(data
->profile());
249 uploader
->QueueReport(post_body
);
252 bool ZipString(const base::FilePath
& filename
,
253 const std::string
& data
, std::string
* compressed_logs
) {
254 base::FilePath temp_path
;
255 base::FilePath zip_file
;
257 // Create a temporary directory, put the logs into a file in it. Create
258 // another temporary file to receive the zip file in.
259 if (!base::CreateNewTempDirectory(base::FilePath::StringType(), &temp_path
))
261 if (base::WriteFile(temp_path
.Append(filename
), data
.c_str(), data
.size()) ==
265 bool succeed
= base::CreateTemporaryFile(&zip_file
) &&
266 zip::Zip(temp_path
, zip_file
, false) &&
267 base::ReadFileToString(zip_file
, compressed_logs
);
269 base::DeleteFile(temp_path
, true);
270 base::DeleteFile(zip_file
, false);
275 } // namespace feedback_util