| OLD | NEW |
| (Empty) |
| 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. | |
| 4 | |
| 5 #include "chrome/browser/feedback/feedback_util.h" | |
| 6 | |
| 7 #include <sstream> | |
| 8 #include <string> | |
| 9 #include <vector> | |
| 10 | |
| 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 "chrome/common/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" | |
| 51 #include "url/gurl.h" | |
| 52 | |
| 53 namespace { | |
| 54 | |
| 55 GURL GetTargetTabUrl(int session_id, int index) { | |
| 56 Browser* browser = chrome::FindBrowserWithID(session_id); | |
| 57 // Sanity checks. | |
| 58 if (!browser || index >= browser->tab_strip_model()->count()) | |
| 59 return GURL(); | |
| 60 | |
| 61 if (index >= 0) { | |
| 62 content::WebContents* target_tab = | |
| 63 browser->tab_strip_model()->GetWebContentsAt(index); | |
| 64 if (target_tab) | |
| 65 return target_tab->GetURL(); | |
| 66 } | |
| 67 | |
| 68 return GURL(); | |
| 69 } | |
| 70 | |
| 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"; | |
| 75 | |
| 76 #if defined(OS_CHROMEOS) | |
| 77 const int kChromeOSProductId = 208; | |
| 78 #else | |
| 79 const int kChromeBrowserProductId = 237; | |
| 80 #endif | |
| 81 | |
| 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; | |
| 92 } | |
| 93 | |
| 94 // Adds data as an attachment to feedback_data if the data is non-empty. | |
| 95 void AddAttachment(userfeedback::ExtensionSubmit* feedback_data, | |
| 96 const char* name, | |
| 97 std::string* data) { | |
| 98 if (data == NULL || data->empty()) | |
| 99 return; | |
| 100 | |
| 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); | |
| 106 } | |
| 107 | |
| 108 } // namespace | |
| 109 | |
| 110 namespace chrome { | |
| 111 | |
| 112 const char kAppLauncherCategoryTag[] = "AppLauncher"; | |
| 113 | |
| 114 void ShowFeedbackPage(Browser* browser, | |
| 115 const std::string& description_template, | |
| 116 const std::string& category_tag) { | |
| 117 GURL page_url; | |
| 118 if (browser) { | |
| 119 page_url = GetTargetTabUrl(browser->session_id().id(), | |
| 120 browser->tab_strip_model()->active_index()); | |
| 121 } | |
| 122 | |
| 123 Profile* profile = NULL; | |
| 124 if (browser) { | |
| 125 profile = browser->profile(); | |
| 126 } else { | |
| 127 profile = ProfileManager::GetLastUsedProfileAllowedByPolicy(); | |
| 128 } | |
| 129 if (!profile) { | |
| 130 LOG(ERROR) << "Cannot invoke feedback: No profile found!"; | |
| 131 return; | |
| 132 } | |
| 133 | |
| 134 // We do not want to launch on an OTR profile. | |
| 135 profile = profile->GetOriginalProfile(); | |
| 136 DCHECK(profile); | |
| 137 | |
| 138 extensions::FeedbackPrivateAPI* api = | |
| 139 extensions::FeedbackPrivateAPI::GetFactoryInstance()->Get(profile); | |
| 140 | |
| 141 api->RequestFeedback(description_template, | |
| 142 category_tag, | |
| 143 page_url); | |
| 144 } | |
| 145 | |
| 146 } // namespace chrome | |
| 147 | |
| 148 namespace feedback_util { | |
| 149 | |
| 150 void SendReport(scoped_refptr<FeedbackData> data) { | |
| 151 if (!data.get()) { | |
| 152 LOG(ERROR) << "SendReport called with NULL data!"; | |
| 153 NOTREACHED(); | |
| 154 return; | |
| 155 } | |
| 156 | |
| 157 userfeedback::ExtensionSubmit feedback_data; | |
| 158 // Unused field, needs to be 0 though. | |
| 159 feedback_data.set_type_id(0); | |
| 160 | |
| 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()); | |
| 166 | |
| 167 std::string chrome_locale = g_browser_process->GetApplicationLocale(); | |
| 168 common_data->set_source_description_language(chrome_locale); | |
| 169 | |
| 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()); | |
| 173 | |
| 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); | |
| 180 } | |
| 181 | |
| 182 AddAttachment(&feedback_data, kLogsAttachmentName, data->compressed_logs()); | |
| 183 } | |
| 184 | |
| 185 if (data->histograms()) { | |
| 186 AddAttachment(&feedback_data, | |
| 187 kHistogramsAttachmentName, | |
| 188 data->compressed_histograms()); | |
| 189 } | |
| 190 | |
| 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()); | |
| 197 } | |
| 198 | |
| 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); | |
| 204 | |
| 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); | |
| 210 | |
| 211 *(screenshot.mutable_dimensions()) = dimensions; | |
| 212 screenshot.set_binary_content(*data->image()); | |
| 213 | |
| 214 *(feedback_data.mutable_screenshot()) = screenshot; | |
| 215 } | |
| 216 | |
| 217 if (data->category_tag().size()) | |
| 218 feedback_data.set_bucket(data->category_tag()); | |
| 219 | |
| 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); | |
| 230 #else | |
| 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); | |
| 238 #endif | |
| 239 | |
| 240 *(feedback_data.mutable_chrome_data()) = chrome_data; | |
| 241 | |
| 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); | |
| 246 | |
| 247 feedback::FeedbackUploader *uploader = | |
| 248 feedback::FeedbackUploaderFactory::GetForBrowserContext(data->profile()); | |
| 249 uploader->QueueReport(post_body); | |
| 250 } | |
| 251 | |
| 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; | |
| 256 | |
| 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)) | |
| 260 return false; | |
| 261 if (base::WriteFile(temp_path.Append(filename), data.c_str(), data.size()) == | |
| 262 -1) | |
| 263 return false; | |
| 264 | |
| 265 bool succeed = base::CreateTemporaryFile(&zip_file) && | |
| 266 zip::Zip(temp_path, zip_file, false) && | |
| 267 base::ReadFileToString(zip_file, compressed_logs); | |
| 268 | |
| 269 base::DeleteFile(temp_path, true); | |
| 270 base::DeleteFile(zip_file, false); | |
| 271 | |
| 272 return succeed; | |
| 273 } | |
| 274 | |
| 275 } // namespace feedback_util | |
| OLD | NEW |