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/ui/webui/feedback_ui.h" 6 7 #include <algorithm> 8 #include <vector> 9 10 #include "base/base64.h" 11 #include "base/bind.h" 12 #include "base/bind_helpers.h" 13 #include "base/files/file_enumerator.h" 14 #include "base/logging.h" 15 #include "base/memory/weak_ptr.h" 16 #include "base/message_loop/message_loop.h" 17 #include "base/prefs/pref_service.h" 18 #include "base/strings/string_number_conversions.h" 19 #include "base/strings/string_util.h" 20 #include "base/strings/utf_string_conversions.h" 21 #include "base/time/time.h" 22 #include "base/values.h" 23 #include "chrome/browser/browser_process.h" 24 #include "chrome/browser/download/download_prefs.h" 25 #include "chrome/browser/feedback/feedback_data.h" 26 #include "chrome/browser/feedback/feedback_util.h" 27 #include "chrome/browser/feedback/tracing_manager.h" 28 #include "chrome/browser/profiles/profile.h" 29 #include "chrome/browser/profiles/profile_manager.h" 30 #include "chrome/browser/signin/signin_manager.h" 31 #include "chrome/browser/signin/signin_manager_factory.h" 32 #include "chrome/browser/ui/browser.h" 33 #include "chrome/browser/ui/browser_finder.h" 34 #include "chrome/browser/ui/browser_window.h" 35 #include "chrome/browser/ui/chrome_pages.h" 36 #include "chrome/browser/ui/singleton_tabs.h" 37 #include "chrome/browser/ui/tabs/tab_strip_model.h" 38 #include "chrome/browser/ui/webui/screenshot_source.h" 39 #include "chrome/browser/ui/window_snapshot/window_snapshot.h" 40 #include "chrome/common/cancelable_task_tracker.h" 41 #include "chrome/common/chrome_paths.h" 42 #include "chrome/common/pref_names.h" 43 #include "chrome/common/url_constants.h" 44 #include "content/public/browser/browser_thread.h" 45 #include "content/public/browser/navigation_controller.h" 46 #include "content/public/browser/navigation_entry.h" 47 #include "content/public/browser/url_data_source.h" 48 #include "content/public/browser/web_contents.h" 49 #include "content/public/browser/web_ui.h" 50 #include "content/public/browser/web_ui_data_source.h" 51 #include "content/public/browser/web_ui_message_handler.h" 52 #include "grit/browser_resources.h" 53 #include "grit/chromium_strings.h" 54 #include "grit/generated_resources.h" 55 #include "grit/locale_settings.h" 56 #include "net/base/escape.h" 57 #include "ui/base/resource/resource_bundle.h" 58 59 #if defined(OS_CHROMEOS) 60 #include "ash/shell.h" 61 #include "ash/shell_delegate.h" 62 #include "base/file_util.h" 63 #include "base/path_service.h" 64 #include "chrome/browser/chromeos/drive/drive.pb.h" 65 #include "chrome/browser/chromeos/drive/drive_integration_service.h" 66 #include "chrome/browser/chromeos/drive/file_system_interface.h" 67 #include "chrome/browser/chromeos/drive/file_system_util.h" 68 #include "chrome/browser/chromeos/login/user_manager.h" 69 #include "chrome/browser/chromeos/system_logs/system_logs_fetcher_base.h" 70 #include "ui/aura/root_window.h" 71 #include "ui/aura/window.h" 72 #endif 73 74 using content::BrowserThread; 75 using content::WebContents; 76 using content::WebUIMessageHandler; 77 using ui::WebDialogUI; 78 79 namespace { 80 81 const char kCategoryTagParameter[] = "categoryTag="; 82 const char kDescriptionParameter[] = "description="; 83 const char kSessionIDParameter[] = "session_id="; 84 const char kTabIndexParameter[] = "tab_index="; 85 const char kCustomPageUrlParameter[] = "customPageUrl="; 86 87 #if defined(OS_CHROMEOS) 88 89 const char kTimestampParameter[] = "timestamp="; 90 const char kTraceIdParameter[] = "traceId="; 91 92 const size_t kMaxSavedScreenshots = 2; 93 size_t kMaxNumScanFiles = 1000; 94 95 // Compare two screenshot filepaths, which include the screenshot timestamp 96 // in the format of screenshot-yyyymmdd-hhmmss.png. Return true if |filepath1| 97 // is more recent |filepath2|. 98 bool ScreenshotTimestampComp(const std::string& filepath1, 99 const std::string& filepath2) { 100 return filepath1 > filepath2; 101 } 102 103 std::string GetUserEmail() { 104 chromeos::UserManager* manager = chromeos::UserManager::Get(); 105 if (!manager) 106 return std::string(); 107 else 108 return manager->GetLoggedInUser()->display_email(); 109 } 110 111 bool ScreenshotDriveTimestampComp(const drive::ResourceEntry& entry1, 112 const drive::ResourceEntry& entry2) { 113 return entry1.file_info().last_modified() > 114 entry2.file_info().last_modified(); 115 } 116 117 void ReadDirectoryCallback(size_t max_saved, 118 std::vector<std::string>* saved_screenshots, 119 base::Closure callback, 120 drive::FileError error, 121 scoped_ptr<drive::ResourceEntryVector> entries) { 122 if (error != drive::FILE_ERROR_OK) { 123 callback.Run(); 124 return; 125 } 126 127 size_t max_scan = std::min(kMaxNumScanFiles, entries->size()); 128 std::vector<drive::ResourceEntry> screenshot_entries; 129 for (size_t i = 0; i < max_scan; ++i) { 130 const drive::ResourceEntry& entry = (*entries)[i]; 131 if (StartsWithASCII(entry.base_name(), 132 ScreenshotSource::kScreenshotPrefix, true) && 133 EndsWith(entry.base_name(), 134 ScreenshotSource::kScreenshotSuffix, true)) { 135 screenshot_entries.push_back(entry); 136 } 137 } 138 139 size_t sort_size = std::min(max_saved, screenshot_entries.size()); 140 std::partial_sort(screenshot_entries.begin(), 141 screenshot_entries.begin() + sort_size, 142 screenshot_entries.end(), 143 ScreenshotDriveTimestampComp); 144 for (size_t i = 0; i < sort_size; ++i) { 145 const drive::ResourceEntry& entry = screenshot_entries[i]; 146 saved_screenshots->push_back( 147 std::string(ScreenshotSource::kScreenshotUrlRoot) + 148 std::string(ScreenshotSource::kScreenshotSaved) + 149 entry.base_name()); 150 } 151 callback.Run(); 152 } 153 154 #else 155 156 std::string GetUserEmail() { 157 Profile* profile = ProfileManager::GetLastUsedProfile(); 158 if (!profile) 159 return std::string(); 160 161 SigninManager* signin = SigninManagerFactory::GetForProfile(profile); 162 if (!signin) 163 return std::string(); 164 165 return signin->GetAuthenticatedUsername(); 166 } 167 168 #endif // OS_CHROMEOS 169 170 // Returns the index of the feedback tab if already open, -1 otherwise 171 int GetIndexOfFeedbackTab(Browser* browser) { 172 GURL feedback_url(chrome::kChromeUIFeedbackURL); 173 for (int i = 0; i < browser->tab_strip_model()->count(); ++i) { 174 WebContents* tab = browser->tab_strip_model()->GetWebContentsAt(i); 175 if (tab && tab->GetURL().GetWithEmptyPath() == feedback_url) 176 return i; 177 } 178 179 return -1; 180 } 181 182 } // namespace 183 184 namespace chrome { 185 186 void ShowFeedbackPage(Browser* browser, 187 const std::string& description_template, 188 const std::string& category_tag) { 189 #if defined(OS_CHROMEOS) 190 // Grab the timestamp before we do anything else - this is crucial to help 191 // diagnose some hardware issues. 192 base::Time now = base::Time::Now(); 193 std::string timestamp = base::DoubleToString(now.ToDoubleT()); 194 #endif 195 196 // First check if we're already open (we cannot depend on ShowSingletonTab 197 // for this functionality since we need to make *sure* we never get 198 // instantiated again while we are open - with singleton tabs, that can 199 // happen). 200 int feedback_tab_index = GetIndexOfFeedbackTab(browser); 201 if (feedback_tab_index >= 0) { 202 // Do not refresh screenshot, do not create a new tab. 203 browser->tab_strip_model()->ActivateTabAt(feedback_tab_index, true); 204 return; 205 } 206 207 if (category_tag != kAppLauncherCategoryTag) { 208 std::vector<unsigned char>* last_screenshot_png = 209 FeedbackUtil::GetScreenshotPng(); 210 last_screenshot_png->clear(); 211 212 gfx::NativeWindow native_window; 213 gfx::Rect snapshot_bounds; 214 215 #if defined(OS_CHROMEOS) 216 // For ChromeOS, don't use the browser window but the root window 217 // instead to grab the screenshot. We want everything on the screen, not 218 // just the current browser. 219 native_window = ash::Shell::GetPrimaryRootWindow(); 220 snapshot_bounds = gfx::Rect(native_window->bounds()); 221 #else 222 native_window = browser->window()->GetNativeWindow(); 223 snapshot_bounds = gfx::Rect(browser->window()->GetBounds().size()); 224 #endif 225 bool success = chrome::GrabWindowSnapshotForUser(native_window, 226 last_screenshot_png, 227 snapshot_bounds); 228 FeedbackUtil::SetScreenshotSize(success ? snapshot_bounds : gfx::Rect()); 229 } 230 std::string feedback_url = std::string(chrome::kChromeUIFeedbackURL) + "?" + 231 kSessionIDParameter + base::IntToString(browser->session_id().id()) + 232 "&" + kTabIndexParameter + 233 base::IntToString(browser->tab_strip_model()->active_index()) + 234 "&" + kDescriptionParameter + 235 net::EscapeUrlEncodedData(description_template, false) + "&" + 236 kCategoryTagParameter + net::EscapeUrlEncodedData(category_tag, false); 237 238 #if defined(OS_CHROMEOS) 239 feedback_url = feedback_url + "&" + kTimestampParameter + 240 net::EscapeUrlEncodedData(timestamp, false); 241 242 // The manager is only available if tracing is enabled. 243 if (TracingManager* manager = TracingManager::Get()) { 244 int trace_id = manager->RequestTrace(); 245 feedback_url = feedback_url + "&" + kTraceIdParameter + 246 base::IntToString(trace_id); 247 } 248 #endif 249 chrome::ShowSingletonTab(browser, GURL(feedback_url)); 250 } 251 252 } // namespace chrome 253 254 // The handler for Javascript messages related to the "bug report" dialog 255 class FeedbackHandler : public WebUIMessageHandler, 256 public base::SupportsWeakPtr<FeedbackHandler> { 257 public: 258 explicit FeedbackHandler(content::WebContents* tab); 259 virtual ~FeedbackHandler(); 260 261 // Init work after Attach. Returns true on success. 262 bool Init(); 263 264 // WebUIMessageHandler implementation. 265 virtual void RegisterMessages() OVERRIDE; 266 267 private: 268 void HandleGetDialogDefaults(const ListValue* args); 269 void HandleRefreshCurrentScreenshot(const ListValue* args); 270 #if defined(OS_CHROMEOS) 271 void HandleRefreshSavedScreenshots(const ListValue* args); 272 void RefreshSavedScreenshotsCallback( 273 std::vector<std::string>* saved_screenshots); 274 void GetMostRecentScreenshotsDrive( 275 const base::FilePath& filepath, 276 std::vector<std::string>* saved_screenshots, 277 size_t max_saved, base::Closure callback); 278 void StartSyslogsCollection(); 279 #endif 280 void HandleSendReport(const ListValue* args); 281 void HandleCancel(const ListValue* args); 282 void HandleOpenSystemTab(const ListValue* args); 283 284 void SetupScreenshotsSource(); 285 void ClobberScreenshotsSource(); 286 287 void CloseFeedbackTab(); 288 289 WebContents* tab_; 290 ScreenshotSource* screenshot_source_; 291 292 scoped_refptr<FeedbackData> feedback_data_; 293 std::string target_tab_url_; 294 std::string category_tag_; 295 #if defined(OS_CHROMEOS) 296 // Timestamp of when the feedback request was initiated. 297 std::string timestamp_; 298 #endif 299 300 DISALLOW_COPY_AND_ASSIGN(FeedbackHandler); 301 }; 302 303 content::WebUIDataSource* CreateFeedbackUIHTMLSource(bool successful_init) { 304 content::WebUIDataSource* source = 305 content::WebUIDataSource::Create(chrome::kChromeUIFeedbackHost); 306 source->SetUseJsonJSFormatV2(); 307 308 source->AddLocalizedString("title", IDS_FEEDBACK_TITLE); 309 source->AddLocalizedString("page-title", IDS_FEEDBACK_REPORT_PAGE_TITLE); 310 source->AddLocalizedString("page-url", IDS_FEEDBACK_REPORT_URL_LABEL); 311 source->AddLocalizedString("description", IDS_FEEDBACK_DESCRIPTION_LABEL); 312 source->AddLocalizedString("current-screenshot", 313 IDS_FEEDBACK_SCREENSHOT_LABEL); 314 source->AddLocalizedString("saved-screenshot", 315 IDS_FEEDBACK_SAVED_SCREENSHOT_LABEL); 316 source->AddLocalizedString("user-email", IDS_FEEDBACK_USER_EMAIL_LABEL); 317 318 #if defined(OS_CHROMEOS) 319 source->AddLocalizedString("performance-trace", 320 IDS_FEEDBACK_INCLUDE_PERFORMANCE_TRACE_CHECKBOX); 321 source->AddLocalizedString("sysinfo", 322 IDS_FEEDBACK_INCLUDE_SYSTEM_INFORMATION_CHKBOX); 323 source->AddLocalizedString("currentscreenshots", 324 IDS_FEEDBACK_CURRENT_SCREENSHOTS); 325 source->AddLocalizedString("savedscreenshots", 326 IDS_FEEDBACK_SAVED_SCREENSHOTS); 327 source->AddLocalizedString("choose-different-screenshot", 328 IDS_FEEDBACK_CHOOSE_DIFFERENT_SCREENSHOT); 329 source->AddLocalizedString("choose-original-screenshot", 330 IDS_FEEDBACK_CHOOSE_ORIGINAL_SCREENSHOT); 331 source->AddLocalizedString("attach-file-label", 332 IDS_FEEDBACK_ATTACH_FILE_LABEL); 333 source->AddLocalizedString("attach-file-note", 334 IDS_FEEDBACK_ATTACH_FILE_NOTE); 335 source->AddLocalizedString("attach-file-to-big", 336 IDS_FEEDBACK_ATTACH_FILE_TO_BIG); 337 source->AddLocalizedString("reading-file", IDS_FEEDBACK_READING_FILE); 338 #else 339 source->AddLocalizedString("currentscreenshots", 340 IDS_FEEDBACK_INCLUDE_NEW_SCREEN_IMAGE); 341 #endif 342 source->AddLocalizedString("noscreenshot", 343 IDS_FEEDBACK_INCLUDE_NO_SCREENSHOT); 344 345 source->AddLocalizedString("send-report", IDS_FEEDBACK_SEND_REPORT); 346 source->AddLocalizedString("cancel", IDS_CANCEL); 347 348 source->AddLocalizedString("no-description", IDS_FEEDBACK_NO_DESCRIPTION); 349 source->AddLocalizedString("no-saved-screenshots", 350 IDS_FEEDBACK_NO_SAVED_SCREENSHOTS_HELP); 351 source->AddLocalizedString("privacy-note", IDS_FEEDBACK_PRIVACY_NOTE); 352 source->AddLocalizedString("launcher-title", IDS_FEEDBACK_LAUNCHER_TITLE); 353 source->AddLocalizedString("launcher-description", 354 IDS_FEEDBACK_LAUNCHER_DESCRIPTION_LABEL); 355 356 source->SetJsonPath("strings.js"); 357 source->AddResourcePath("feedback.js", IDR_FEEDBACK_JS); 358 source->SetDefaultResource( 359 successful_init ? IDR_FEEDBACK_HTML : IDR_FEEDBACK_HTML_INVALID); 360 361 return source; 362 } 363 364 //////////////////////////////////////////////////////////////////////////////// 365 // 366 // FeedbackHandler 367 // 368 //////////////////////////////////////////////////////////////////////////////// 369 FeedbackHandler::FeedbackHandler(WebContents* tab) 370 : tab_(tab), 371 screenshot_source_(NULL), 372 feedback_data_(NULL) { 373 DCHECK(tab); 374 } 375 376 FeedbackHandler::~FeedbackHandler() { 377 // Make sure we don't leave any screenshot data around. 378 FeedbackUtil::ClearScreenshotPng(); 379 } 380 381 void FeedbackHandler::ClobberScreenshotsSource() { 382 // Re-create our screenshots data source (this clobbers the last source) 383 // setting the screenshot to NULL, effectively disabling the source 384 // TODO(rkc): Once there is a method to 'remove' a source, change this code 385 Profile* profile = Profile::FromBrowserContext(tab_->GetBrowserContext()); 386 content::URLDataSource::Add(profile, new ScreenshotSource(NULL, profile)); 387 388 FeedbackUtil::ClearScreenshotPng(); 389 } 390 391 void FeedbackHandler::SetupScreenshotsSource() { 392 Profile* profile = Profile::FromBrowserContext(tab_->GetBrowserContext()); 393 screenshot_source_ = 394 new ScreenshotSource(FeedbackUtil::GetScreenshotPng(), profile); 395 // Add the source to the data manager. 396 content::URLDataSource::Add(profile, screenshot_source_); 397 } 398 399 bool FeedbackHandler::Init() { 400 std::string page_url; 401 if (tab_->GetController().GetActiveEntry()) { 402 page_url = tab_->GetController().GetActiveEntry()->GetURL().spec(); 403 } 404 405 url_parse::Parsed parts; 406 ParseStandardURL(page_url.c_str(), page_url.length(), &parts); 407 408 size_t params_start = page_url.find("?"); 409 std::string query = page_url.substr(params_start + 1); 410 411 int session_id = -1; 412 int index = -1; 413 414 std::vector<std::string> params; 415 std::string custom_page_url; 416 if (Tokenize(query, std::string("&"), ¶ms)) { 417 for (std::vector<std::string>::iterator it = params.begin(); 418 it != params.end(); ++it) { 419 std::string query_str = *it; 420 if (StartsWithASCII(query_str, std::string(kSessionIDParameter), true)) { 421 ReplaceFirstSubstringAfterOffset( 422 &query_str, 0, kSessionIDParameter, std::string()); 423 if (!base::StringToInt(query_str, &session_id)) 424 return false; 425 } else if (StartsWithASCII(*it, std::string(kTabIndexParameter), true)) { 426 ReplaceFirstSubstringAfterOffset( 427 &query_str, 0, kTabIndexParameter, std::string()); 428 if (!base::StringToInt(query_str, &index)) 429 return false; 430 } else if (StartsWithASCII(*it, std::string(kCustomPageUrlParameter), 431 true)) { 432 ReplaceFirstSubstringAfterOffset( 433 &query_str, 0, kCustomPageUrlParameter, std::string()); 434 custom_page_url = query_str; 435 } else if (StartsWithASCII(*it, std::string(kCategoryTagParameter), 436 true)) { 437 ReplaceFirstSubstringAfterOffset( 438 &query_str, 0, kCategoryTagParameter, std::string()); 439 category_tag_ = query_str; 440 #if defined(OS_CHROMEOS) 441 } else if (StartsWithASCII(*it, std::string(kTimestampParameter), true)) { 442 ReplaceFirstSubstringAfterOffset( 443 &query_str, 0, kTimestampParameter, ""); 444 timestamp_ = query_str; 445 #endif 446 } 447 } 448 } 449 450 // If we don't have a page url specified, get it from the tab index. 451 if (custom_page_url.empty()) { 452 if (session_id == -1) 453 return false; 454 455 Browser* browser = chrome::FindBrowserWithID(session_id); 456 // Sanity checks. 457 if (!browser || index >= browser->tab_strip_model()->count()) 458 return false; 459 460 if (index >= 0) { 461 WebContents* target_tab = 462 browser->tab_strip_model()->GetWebContentsAt(index); 463 if (target_tab) 464 target_tab_url_ = target_tab->GetURL().spec(); 465 } 466 467 // Note: We don't need to setup a screenshot source if we're using a 468 // custom page URL since we were invoked from JS/an extension, in which 469 // case we don't actually have a screenshot anyway. 470 SetupScreenshotsSource(); 471 } else { 472 target_tab_url_ = custom_page_url; 473 } 474 475 return true; 476 } 477 478 void FeedbackHandler::RegisterMessages() { 479 web_ui()->RegisterMessageCallback("getDialogDefaults", 480 base::Bind(&FeedbackHandler::HandleGetDialogDefaults, 481 base::Unretained(this))); 482 web_ui()->RegisterMessageCallback("refreshCurrentScreenshot", 483 base::Bind(&FeedbackHandler::HandleRefreshCurrentScreenshot, 484 base::Unretained(this))); 485 #if defined(OS_CHROMEOS) 486 web_ui()->RegisterMessageCallback("refreshSavedScreenshots", 487 base::Bind(&FeedbackHandler::HandleRefreshSavedScreenshots, 488 base::Unretained(this))); 489 #endif 490 web_ui()->RegisterMessageCallback("sendReport", 491 base::Bind(&FeedbackHandler::HandleSendReport, 492 base::Unretained(this))); 493 web_ui()->RegisterMessageCallback("cancel", 494 base::Bind(&FeedbackHandler::HandleCancel, 495 base::Unretained(this))); 496 web_ui()->RegisterMessageCallback("openSystemTab", 497 base::Bind(&FeedbackHandler::HandleOpenSystemTab, 498 base::Unretained(this))); 499 } 500 501 void FeedbackHandler::HandleGetDialogDefaults(const ListValue*) { 502 feedback_data_ = new FeedbackData(); 503 504 // Send back values which the dialog js needs initially. 505 DictionaryValue dialog_defaults; 506 507 if (category_tag_ == chrome::kAppLauncherCategoryTag) 508 dialog_defaults.SetBoolean("launcherFeedback", true); 509 510 // Current url. 511 dialog_defaults.SetString("currentUrl", target_tab_url_); 512 513 // Are screenshots disabled? 514 dialog_defaults.SetBoolean( 515 "disableScreenshots", 516 g_browser_process->local_state()->GetBoolean(prefs::kDisableScreenshots)); 517 518 // User e-mail 519 std::string user_email = GetUserEmail(); 520 dialog_defaults.SetString("userEmail", user_email); 521 522 // Set email checkbox to checked by default for cros, unchecked for Chrome. 523 dialog_defaults.SetBoolean( 524 "emailCheckboxDefault", 525 #if defined(OS_CHROMEOS) 526 true); 527 #else 528 false); 529 #endif 530 531 532 #if defined(OS_CHROMEOS) 533 feedback_data_->StartSyslogsCollection(); 534 535 // On ChromeOS if the user's email is blank, it means we don't 536 // have a logged in user, hence don't use saved screenshots. 537 dialog_defaults.SetBoolean("useSaved", !user_email.empty()); 538 #endif 539 540 web_ui()->CallJavascriptFunction("setupDialogDefaults", dialog_defaults); 541 } 542 543 void FeedbackHandler::HandleRefreshCurrentScreenshot(const ListValue*) { 544 std::string current_screenshot( 545 std::string(ScreenshotSource::kScreenshotUrlRoot) + 546 std::string(ScreenshotSource::kScreenshotCurrent)); 547 StringValue screenshot(current_screenshot); 548 web_ui()->CallJavascriptFunction("setupCurrentScreenshot", screenshot); 549 } 550 551 #if defined(OS_CHROMEOS) 552 void FeedbackHandler::HandleRefreshSavedScreenshots(const ListValue*) { 553 std::vector<std::string>* saved_screenshots = new std::vector<std::string>; 554 base::FilePath filepath = DownloadPrefs::FromBrowserContext( 555 tab_->GetBrowserContext())->DownloadPath(); 556 base::Closure refresh_callback = base::Bind( 557 &FeedbackHandler::RefreshSavedScreenshotsCallback, 558 AsWeakPtr(), base::Owned(saved_screenshots)); 559 if (drive::util::IsUnderDriveMountPoint(filepath)) { 560 GetMostRecentScreenshotsDrive( 561 filepath, saved_screenshots, kMaxSavedScreenshots, refresh_callback); 562 } else { 563 BrowserThread::PostTaskAndReply( 564 BrowserThread::FILE, FROM_HERE, 565 base::Bind(&FeedbackUI::GetMostRecentScreenshots, filepath, 566 base::Unretained(saved_screenshots), kMaxSavedScreenshots), 567 refresh_callback); 568 } 569 } 570 571 void FeedbackHandler::RefreshSavedScreenshotsCallback( 572 std::vector<std::string>* saved_screenshots) { 573 ListValue screenshots_list; 574 for (size_t i = 0; i < saved_screenshots->size(); ++i) 575 screenshots_list.Append(new StringValue((*saved_screenshots)[i])); 576 web_ui()->CallJavascriptFunction("setupSavedScreenshots", screenshots_list); 577 } 578 579 void FeedbackHandler::GetMostRecentScreenshotsDrive( 580 const base::FilePath& filepath, std::vector<std::string>* saved_screenshots, 581 size_t max_saved, base::Closure callback) { 582 drive::FileSystemInterface* file_system = 583 drive::DriveIntegrationServiceFactory::GetForProfile( 584 Profile::FromWebUI(web_ui()))->file_system(); 585 file_system->ReadDirectoryByPath( 586 drive::util::ExtractDrivePath(filepath), 587 base::Bind(&ReadDirectoryCallback, max_saved, saved_screenshots, 588 callback)); 589 } 590 #endif 591 592 593 void FeedbackHandler::HandleSendReport(const ListValue* list_value) { 594 if (!feedback_data_.get()) { 595 LOG(ERROR) << "Bug report hasn't been intialized yet."; 596 return; 597 } 598 599 ListValue::const_iterator i = list_value->begin(); 600 std::string page_url; 601 (*i++)->GetAsString(&page_url); 602 std::string category_tag; 603 (*i++)->GetAsString(&category_tag); 604 std::string description; 605 (*i++)->GetAsString(&description); 606 std::string user_email; 607 (*i++)->GetAsString(&user_email); 608 std::string screenshot_path; 609 (*i++)->GetAsString(&screenshot_path); 610 screenshot_path.erase(0, strlen(ScreenshotSource::kScreenshotUrlRoot)); 611 612 // Get the image to send in the report. 613 ScreenshotDataPtr image_ptr; 614 if (!screenshot_path.empty() && screenshot_source_) 615 image_ptr = screenshot_source_->GetCachedScreenshot(screenshot_path); 616 617 #if defined(OS_CHROMEOS) 618 std::string sys_info_checkbox; 619 (*i++)->GetAsString(&sys_info_checkbox); 620 bool send_sys_info = (sys_info_checkbox == "true"); 621 622 std::string trace_id_str; 623 (*i++)->GetAsString(&trace_id_str); 624 int trace_id = 0; 625 base::StringToInt(trace_id_str, &trace_id); 626 627 std::string attached_filename; 628 scoped_ptr<std::string> attached_filedata; 629 // If we have an attached file, we'll still have more data in the list. 630 if (i != list_value->end()) { 631 (*i++)->GetAsString(&attached_filename); 632 if (base::FilePath::IsSeparator(attached_filename[0])) { 633 // We have an attached filepath, not filename, hence we need read this 634 // this file in chrome. We won't have any file data, skip over it. 635 i++; 636 } else { 637 std::string encoded_filedata; 638 attached_filedata.reset(new std::string); 639 (*i++)->GetAsString(&encoded_filedata); 640 if (!base::Base64Decode( 641 base::StringPiece(encoded_filedata), attached_filedata.get())) { 642 LOG(ERROR) << "Unable to attach file: " << attached_filename; 643 // Clear the filename so feedback_util doesn't try to attach the file. 644 attached_filename = ""; 645 } 646 } 647 } 648 #endif 649 650 // TODO(rkc): We are not setting the category tag here since this 651 // functionality is broken on the feedback server side. Fix this once the 652 // issue is resolved. 653 feedback_data_->set_category_tag(category_tag); 654 feedback_data_->set_description(description); 655 feedback_data_->set_image(image_ptr); 656 feedback_data_->set_page_url(page_url); 657 feedback_data_->set_profile(Profile::FromWebUI(web_ui())); 658 feedback_data_->set_user_email(user_email); 659 #if defined(OS_CHROMEOS) 660 feedback_data_->set_attached_filedata(attached_filedata.Pass()); 661 feedback_data_->set_attached_filename(attached_filename); 662 feedback_data_->set_send_sys_info(send_sys_info); 663 feedback_data_->set_timestamp(timestamp_); 664 feedback_data_->set_trace_id(trace_id); 665 #endif 666 667 // Signal the feedback object that the data from the feedback page has been 668 // filled - the object will manage sending of the actual report. 669 feedback_data_->FeedbackPageDataComplete(); 670 671 CloseFeedbackTab(); 672 } 673 674 void FeedbackHandler::HandleCancel(const ListValue*) { 675 CloseFeedbackTab(); 676 } 677 678 void FeedbackHandler::HandleOpenSystemTab(const ListValue* args) { 679 #if defined(OS_CHROMEOS) 680 web_ui()->GetWebContents()->GetDelegate()->OpenURLFromTab( 681 web_ui()->GetWebContents(), 682 content::OpenURLParams(GURL(chrome::kChromeUISystemInfoURL), 683 content::Referrer(), 684 NEW_FOREGROUND_TAB, 685 content::PAGE_TRANSITION_LINK, 686 false)); 687 #endif 688 } 689 690 void FeedbackHandler::CloseFeedbackTab() { 691 ClobberScreenshotsSource(); 692 tab_->GetDelegate()->CloseContents(tab_); 693 } 694 695 //////////////////////////////////////////////////////////////////////////////// 696 // 697 // FeedbackUI 698 // 699 //////////////////////////////////////////////////////////////////////////////// 700 FeedbackUI::FeedbackUI(content::WebUI* web_ui) 701 : WebDialogUI(web_ui) { 702 FeedbackHandler* handler = new FeedbackHandler(web_ui->GetWebContents()); 703 web_ui->AddMessageHandler(handler); 704 705 // The handler's init will determine whether we show the error html page. 706 content::WebUIDataSource* html_source = 707 CreateFeedbackUIHTMLSource(handler->Init()); 708 709 // Set up the chrome://feedback/ source. 710 Profile* profile = Profile::FromWebUI(web_ui); 711 content::WebUIDataSource::Add(profile, html_source); 712 } 713 714 #if defined(OS_CHROMEOS) 715 // static 716 void FeedbackUI::GetMostRecentScreenshots( 717 const base::FilePath& filepath, 718 std::vector<std::string>* saved_screenshots, 719 size_t max_saved) { 720 std::string pattern = 721 std::string(ScreenshotSource::kScreenshotPrefix) + "*" + 722 ScreenshotSource::kScreenshotSuffix; 723 base::FileEnumerator screenshots(filepath, false, 724 base::FileEnumerator::FILES, pattern); 725 base::FilePath screenshot = screenshots.Next(); 726 727 std::vector<std::string> screenshot_filepaths; 728 while (!screenshot.empty()) { 729 screenshot_filepaths.push_back(screenshot.BaseName().value()); 730 screenshot = screenshots.Next(); 731 } 732 733 size_t sort_size = std::min(max_saved, screenshot_filepaths.size()); 734 std::partial_sort(screenshot_filepaths.begin(), 735 screenshot_filepaths.begin() + sort_size, 736 screenshot_filepaths.end(), 737 ScreenshotTimestampComp); 738 for (size_t i = 0; i < sort_size; ++i) 739 saved_screenshots->push_back( 740 std::string(ScreenshotSource::kScreenshotUrlRoot) + 741 std::string(ScreenshotSource::kScreenshotSaved) + 742 screenshot_filepaths[i]); 743 } 744 #endif 745