| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/webui/feedback_ui.h" |
| |
| #include <algorithm> |
| #include <vector> |
| |
| #include "base/base64.h" |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/files/file_enumerator.h" |
| #include "base/logging.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/prefs/pref_service.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/time/time.h" |
| #include "base/values.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/download/download_prefs.h" |
| #include "chrome/browser/feedback/feedback_data.h" |
| #include "chrome/browser/feedback/feedback_util.h" |
| #include "chrome/browser/feedback/tracing_manager.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/browser/signin/signin_manager.h" |
| #include "chrome/browser/signin/signin_manager_factory.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_finder.h" |
| #include "chrome/browser/ui/browser_window.h" |
| #include "chrome/browser/ui/chrome_pages.h" |
| #include "chrome/browser/ui/singleton_tabs.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/browser/ui/webui/screenshot_source.h" |
| #include "chrome/browser/ui/window_snapshot/window_snapshot.h" |
| #include "chrome/common/cancelable_task_tracker.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/url_constants.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/navigation_controller.h" |
| #include "content/public/browser/navigation_entry.h" |
| #include "content/public/browser/url_data_source.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/browser/web_ui.h" |
| #include "content/public/browser/web_ui_data_source.h" |
| #include "content/public/browser/web_ui_message_handler.h" |
| #include "grit/browser_resources.h" |
| #include "grit/chromium_strings.h" |
| #include "grit/generated_resources.h" |
| #include "grit/locale_settings.h" |
| #include "net/base/escape.h" |
| #include "ui/base/resource/resource_bundle.h" |
| |
| #if defined(OS_CHROMEOS) |
| #include "ash/shell.h" |
| #include "ash/shell_delegate.h" |
| #include "base/file_util.h" |
| #include "base/path_service.h" |
| #include "chrome/browser/chromeos/drive/drive.pb.h" |
| #include "chrome/browser/chromeos/drive/file_system_interface.h" |
| #include "chrome/browser/chromeos/drive/file_system_util.h" |
| #include "chrome/browser/chromeos/login/user_manager.h" |
| #include "chrome/browser/chromeos/system_logs/system_logs_fetcher_base.h" |
| #include "ui/aura/root_window.h" |
| #include "ui/aura/window.h" |
| #endif |
| |
| using content::BrowserThread; |
| using content::WebContents; |
| using content::WebUIMessageHandler; |
| using ui::WebDialogUI; |
| |
| namespace { |
| |
| const char kCategoryTagParameter[] = "categoryTag="; |
| const char kDescriptionParameter[] = "description="; |
| const char kSessionIDParameter[] = "session_id="; |
| const char kTabIndexParameter[] = "tab_index="; |
| const char kCustomPageUrlParameter[] = "customPageUrl="; |
| |
| #if defined(OS_CHROMEOS) |
| |
| const char kTimestampParameter[] = "timestamp="; |
| const char kTraceIdParameter[] = "traceId="; |
| |
| const char kPerformanceCategoryTag[] = "Performance"; |
| |
| const size_t kMaxSavedScreenshots = 2; |
| size_t kMaxNumScanFiles = 1000; |
| |
| // Compare two screenshot filepaths, which include the screenshot timestamp |
| // in the format of screenshot-yyyymmdd-hhmmss.png. Return true if |filepath1| |
| // is more recent |filepath2|. |
| bool ScreenshotTimestampComp(const std::string& filepath1, |
| const std::string& filepath2) { |
| return filepath1 > filepath2; |
| } |
| |
| std::string GetUserEmail() { |
| chromeos::UserManager* manager = chromeos::UserManager::Get(); |
| if (!manager) |
| return std::string(); |
| else |
| return manager->GetLoggedInUser()->display_email(); |
| } |
| |
| bool ScreenshotDriveTimestampComp(const drive::ResourceEntry& entry1, |
| const drive::ResourceEntry& entry2) { |
| return entry1.file_info().last_modified() > |
| entry2.file_info().last_modified(); |
| } |
| |
| void ReadDirectoryCallback(size_t max_saved, |
| std::vector<std::string>* saved_screenshots, |
| base::Closure callback, |
| drive::FileError error, |
| scoped_ptr<drive::ResourceEntryVector> entries) { |
| if (error != drive::FILE_ERROR_OK) { |
| callback.Run(); |
| return; |
| } |
| |
| size_t max_scan = std::min(kMaxNumScanFiles, entries->size()); |
| std::vector<drive::ResourceEntry> screenshot_entries; |
| for (size_t i = 0; i < max_scan; ++i) { |
| const drive::ResourceEntry& entry = (*entries)[i]; |
| if (StartsWithASCII(entry.base_name(), |
| ScreenshotSource::kScreenshotPrefix, true) && |
| EndsWith(entry.base_name(), |
| ScreenshotSource::kScreenshotSuffix, true)) { |
| screenshot_entries.push_back(entry); |
| } |
| } |
| |
| size_t sort_size = std::min(max_saved, screenshot_entries.size()); |
| std::partial_sort(screenshot_entries.begin(), |
| screenshot_entries.begin() + sort_size, |
| screenshot_entries.end(), |
| ScreenshotDriveTimestampComp); |
| for (size_t i = 0; i < sort_size; ++i) { |
| const drive::ResourceEntry& entry = screenshot_entries[i]; |
| saved_screenshots->push_back( |
| std::string(ScreenshotSource::kScreenshotUrlRoot) + |
| std::string(ScreenshotSource::kScreenshotSaved) + |
| entry.base_name()); |
| } |
| callback.Run(); |
| } |
| |
| #else |
| |
| std::string GetUserEmail() { |
| Profile* profile = ProfileManager::GetLastUsedProfile(); |
| if (!profile) |
| return std::string(); |
| |
| SigninManager* signin = SigninManagerFactory::GetForProfile(profile); |
| if (!signin) |
| return std::string(); |
| |
| return signin->GetAuthenticatedUsername(); |
| } |
| |
| #endif // OS_CHROMEOS |
| |
| // Returns the index of the feedback tab if already open, -1 otherwise |
| int GetIndexOfFeedbackTab(Browser* browser) { |
| GURL feedback_url(chrome::kChromeUIFeedbackURL); |
| for (int i = 0; i < browser->tab_strip_model()->count(); ++i) { |
| WebContents* tab = browser->tab_strip_model()->GetWebContentsAt(i); |
| if (tab && tab->GetURL().GetWithEmptyPath() == feedback_url) |
| return i; |
| } |
| |
| return -1; |
| } |
| |
| } // namespace |
| |
| namespace chrome { |
| |
| void ShowFeedbackPage(Browser* browser, |
| const std::string& description_template, |
| const std::string& category_tag) { |
| #if defined(OS_CHROMEOS) |
| // Grab the timestamp before we do anything else - this is crucial to help |
| // diagnose some hardware issues. |
| base::Time now = base::Time::Now(); |
| std::string timestamp = base::DoubleToString(now.ToDoubleT()); |
| #endif |
| |
| // First check if we're already open (we cannot depend on ShowSingletonTab |
| // for this functionality since we need to make *sure* we never get |
| // instantiated again while we are open - with singleton tabs, that can |
| // happen). |
| int feedback_tab_index = GetIndexOfFeedbackTab(browser); |
| if (feedback_tab_index >= 0) { |
| // Do not refresh screenshot, do not create a new tab. |
| browser->tab_strip_model()->ActivateTabAt(feedback_tab_index, true); |
| return; |
| } |
| |
| if (category_tag != kAppLauncherCategoryTag) { |
| std::vector<unsigned char>* last_screenshot_png = |
| FeedbackUtil::GetScreenshotPng(); |
| last_screenshot_png->clear(); |
| |
| gfx::NativeWindow native_window; |
| gfx::Rect snapshot_bounds; |
| |
| #if defined(OS_CHROMEOS) |
| // For ChromeOS, don't use the browser window but the root window |
| // instead to grab the screenshot. We want everything on the screen, not |
| // just the current browser. |
| native_window = ash::Shell::GetPrimaryRootWindow(); |
| snapshot_bounds = gfx::Rect(native_window->bounds()); |
| #else |
| native_window = browser->window()->GetNativeWindow(); |
| snapshot_bounds = gfx::Rect(browser->window()->GetBounds().size()); |
| #endif |
| bool success = chrome::GrabWindowSnapshotForUser(native_window, |
| last_screenshot_png, |
| snapshot_bounds); |
| FeedbackUtil::SetScreenshotSize(success ? snapshot_bounds : gfx::Rect()); |
| } |
| std::string feedback_url = std::string(chrome::kChromeUIFeedbackURL) + "?" + |
| kSessionIDParameter + base::IntToString(browser->session_id().id()) + |
| "&" + kTabIndexParameter + |
| base::IntToString(browser->tab_strip_model()->active_index()) + |
| "&" + kDescriptionParameter + |
| net::EscapeUrlEncodedData(description_template, false) + "&" + |
| kCategoryTagParameter + net::EscapeUrlEncodedData(category_tag, false); |
| |
| #if defined(OS_CHROMEOS) |
| feedback_url = feedback_url + "&" + kTimestampParameter + |
| net::EscapeUrlEncodedData(timestamp, false); |
| |
| // The manager is only available if tracing is enabled. |
| if (TracingManager* manager = TracingManager::Get()) { |
| int trace_id = manager->RequestTrace(); |
| feedback_url = feedback_url + "&" + kTraceIdParameter + |
| base::IntToString(trace_id); |
| } |
| #endif |
| chrome::ShowSingletonTab(browser, GURL(feedback_url)); |
| } |
| |
| } // namespace chrome |
| |
| // The handler for Javascript messages related to the "bug report" dialog |
| class FeedbackHandler : public WebUIMessageHandler, |
| public base::SupportsWeakPtr<FeedbackHandler> { |
| public: |
| explicit FeedbackHandler(content::WebContents* tab); |
| virtual ~FeedbackHandler(); |
| |
| // Init work after Attach. Returns true on success. |
| bool Init(); |
| |
| // WebUIMessageHandler implementation. |
| virtual void RegisterMessages() OVERRIDE; |
| |
| private: |
| void HandleGetDialogDefaults(const ListValue* args); |
| void HandleRefreshCurrentScreenshot(const ListValue* args); |
| #if defined(OS_CHROMEOS) |
| void HandleRefreshSavedScreenshots(const ListValue* args); |
| void RefreshSavedScreenshotsCallback( |
| std::vector<std::string>* saved_screenshots); |
| void GetMostRecentScreenshotsDrive( |
| const base::FilePath& filepath, |
| std::vector<std::string>* saved_screenshots, |
| size_t max_saved, base::Closure callback); |
| void StartSyslogsCollection(); |
| #endif |
| void HandleSendReport(const ListValue* args); |
| void HandleCancel(const ListValue* args); |
| void HandleOpenSystemTab(const ListValue* args); |
| |
| void SetupScreenshotsSource(); |
| void ClobberScreenshotsSource(); |
| |
| void CloseFeedbackTab(); |
| |
| WebContents* tab_; |
| ScreenshotSource* screenshot_source_; |
| |
| scoped_refptr<FeedbackData> feedback_data_; |
| std::string target_tab_url_; |
| std::string category_tag_; |
| #if defined(OS_CHROMEOS) |
| // Timestamp of when the feedback request was initiated. |
| std::string timestamp_; |
| #endif |
| |
| DISALLOW_COPY_AND_ASSIGN(FeedbackHandler); |
| }; |
| |
| content::WebUIDataSource* CreateFeedbackUIHTMLSource(bool successful_init) { |
| content::WebUIDataSource* source = |
| content::WebUIDataSource::Create(chrome::kChromeUIFeedbackHost); |
| source->SetUseJsonJSFormatV2(); |
| |
| source->AddLocalizedString("title", IDS_FEEDBACK_TITLE); |
| source->AddLocalizedString("page-title", IDS_FEEDBACK_REPORT_PAGE_TITLE); |
| source->AddLocalizedString("page-url", IDS_FEEDBACK_REPORT_URL_LABEL); |
| source->AddLocalizedString("description", IDS_FEEDBACK_DESCRIPTION_LABEL); |
| source->AddLocalizedString("current-screenshot", |
| IDS_FEEDBACK_SCREENSHOT_LABEL); |
| source->AddLocalizedString("saved-screenshot", |
| IDS_FEEDBACK_SAVED_SCREENSHOT_LABEL); |
| source->AddLocalizedString("user-email", IDS_FEEDBACK_USER_EMAIL_LABEL); |
| |
| #if defined(OS_CHROMEOS) |
| source->AddLocalizedString("performance-trace", |
| IDS_FEEDBACK_INCLUDE_PERFORMANCE_TRACE_CHECKBOX); |
| source->AddLocalizedString("sysinfo", |
| IDS_FEEDBACK_INCLUDE_SYSTEM_INFORMATION_CHKBOX); |
| source->AddLocalizedString("currentscreenshots", |
| IDS_FEEDBACK_CURRENT_SCREENSHOTS); |
| source->AddLocalizedString("savedscreenshots", |
| IDS_FEEDBACK_SAVED_SCREENSHOTS); |
| source->AddLocalizedString("choose-different-screenshot", |
| IDS_FEEDBACK_CHOOSE_DIFFERENT_SCREENSHOT); |
| source->AddLocalizedString("choose-original-screenshot", |
| IDS_FEEDBACK_CHOOSE_ORIGINAL_SCREENSHOT); |
| source->AddLocalizedString("attach-file-label", |
| IDS_FEEDBACK_ATTACH_FILE_LABEL); |
| source->AddLocalizedString("attach-file-note", |
| IDS_FEEDBACK_ATTACH_FILE_NOTE); |
| source->AddLocalizedString("attach-file-to-big", |
| IDS_FEEDBACK_ATTACH_FILE_TO_BIG); |
| source->AddLocalizedString("reading-file", IDS_FEEDBACK_READING_FILE); |
| #else |
| source->AddLocalizedString("currentscreenshots", |
| IDS_FEEDBACK_INCLUDE_NEW_SCREEN_IMAGE); |
| #endif |
| source->AddLocalizedString("noscreenshot", |
| IDS_FEEDBACK_INCLUDE_NO_SCREENSHOT); |
| |
| source->AddLocalizedString("send-report", IDS_FEEDBACK_SEND_REPORT); |
| source->AddLocalizedString("cancel", IDS_CANCEL); |
| |
| source->AddLocalizedString("no-description", IDS_FEEDBACK_NO_DESCRIPTION); |
| source->AddLocalizedString("no-saved-screenshots", |
| IDS_FEEDBACK_NO_SAVED_SCREENSHOTS_HELP); |
| source->AddLocalizedString("privacy-note", IDS_FEEDBACK_PRIVACY_NOTE); |
| source->AddLocalizedString("launcher-title", IDS_FEEDBACK_LAUNCHER_TITLE); |
| source->AddLocalizedString("launcher-description", |
| IDS_FEEDBACK_LAUNCHER_DESCRIPTION_LABEL); |
| |
| source->SetJsonPath("strings.js"); |
| source->AddResourcePath("feedback.js", IDR_FEEDBACK_JS); |
| source->SetDefaultResource( |
| successful_init ? IDR_FEEDBACK_HTML : IDR_FEEDBACK_HTML_INVALID); |
| |
| return source; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // FeedbackHandler |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| FeedbackHandler::FeedbackHandler(WebContents* tab) |
| : tab_(tab), |
| screenshot_source_(NULL), |
| feedback_data_(NULL) { |
| DCHECK(tab); |
| } |
| |
| FeedbackHandler::~FeedbackHandler() { |
| // Make sure we don't leave any screenshot data around. |
| FeedbackUtil::ClearScreenshotPng(); |
| } |
| |
| void FeedbackHandler::ClobberScreenshotsSource() { |
| // Re-create our screenshots data source (this clobbers the last source) |
| // setting the screenshot to NULL, effectively disabling the source |
| // TODO(rkc): Once there is a method to 'remove' a source, change this code |
| Profile* profile = Profile::FromBrowserContext(tab_->GetBrowserContext()); |
| content::URLDataSource::Add(profile, new ScreenshotSource(NULL, profile)); |
| |
| FeedbackUtil::ClearScreenshotPng(); |
| } |
| |
| void FeedbackHandler::SetupScreenshotsSource() { |
| Profile* profile = Profile::FromBrowserContext(tab_->GetBrowserContext()); |
| screenshot_source_ = |
| new ScreenshotSource(FeedbackUtil::GetScreenshotPng(), profile); |
| // Add the source to the data manager. |
| content::URLDataSource::Add(profile, screenshot_source_); |
| } |
| |
| bool FeedbackHandler::Init() { |
| std::string page_url; |
| if (tab_->GetController().GetActiveEntry()) { |
| page_url = tab_->GetController().GetActiveEntry()->GetURL().spec(); |
| } |
| |
| url_parse::Parsed parts; |
| ParseStandardURL(page_url.c_str(), page_url.length(), &parts); |
| |
| size_t params_start = page_url.find("?"); |
| std::string query = page_url.substr(params_start + 1); |
| |
| int session_id = -1; |
| int index = -1; |
| |
| std::vector<std::string> params; |
| std::string custom_page_url; |
| if (Tokenize(query, std::string("&"), ¶ms)) { |
| for (std::vector<std::string>::iterator it = params.begin(); |
| it != params.end(); ++it) { |
| std::string query_str = *it; |
| if (StartsWithASCII(query_str, std::string(kSessionIDParameter), true)) { |
| ReplaceFirstSubstringAfterOffset( |
| &query_str, 0, kSessionIDParameter, std::string()); |
| if (!base::StringToInt(query_str, &session_id)) |
| return false; |
| } else if (StartsWithASCII(*it, std::string(kTabIndexParameter), true)) { |
| ReplaceFirstSubstringAfterOffset( |
| &query_str, 0, kTabIndexParameter, std::string()); |
| if (!base::StringToInt(query_str, &index)) |
| return false; |
| } else if (StartsWithASCII(*it, std::string(kCustomPageUrlParameter), |
| true)) { |
| ReplaceFirstSubstringAfterOffset( |
| &query_str, 0, kCustomPageUrlParameter, std::string()); |
| custom_page_url = query_str; |
| } else if (StartsWithASCII(*it, std::string(kCategoryTagParameter), |
| true)) { |
| ReplaceFirstSubstringAfterOffset( |
| &query_str, 0, kCategoryTagParameter, std::string()); |
| category_tag_ = query_str; |
| #if defined(OS_CHROMEOS) |
| } else if (StartsWithASCII(*it, std::string(kTimestampParameter), true)) { |
| ReplaceFirstSubstringAfterOffset( |
| &query_str, 0, kTimestampParameter, ""); |
| timestamp_ = query_str; |
| #endif |
| } |
| } |
| } |
| |
| // If we don't have a page url specified, get it from the tab index. |
| if (custom_page_url.empty()) { |
| if (session_id == -1) |
| return false; |
| |
| Browser* browser = chrome::FindBrowserWithID(session_id); |
| // Sanity checks. |
| if (!browser || index >= browser->tab_strip_model()->count()) |
| return false; |
| |
| if (index >= 0) { |
| WebContents* target_tab = |
| browser->tab_strip_model()->GetWebContentsAt(index); |
| if (target_tab) |
| target_tab_url_ = target_tab->GetURL().spec(); |
| } |
| |
| // Note: We don't need to setup a screenshot source if we're using a |
| // custom page URL since we were invoked from JS/an extension, in which |
| // case we don't actually have a screenshot anyway. |
| SetupScreenshotsSource(); |
| } else { |
| target_tab_url_ = custom_page_url; |
| } |
| |
| return true; |
| } |
| |
| void FeedbackHandler::RegisterMessages() { |
| web_ui()->RegisterMessageCallback("getDialogDefaults", |
| base::Bind(&FeedbackHandler::HandleGetDialogDefaults, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback("refreshCurrentScreenshot", |
| base::Bind(&FeedbackHandler::HandleRefreshCurrentScreenshot, |
| base::Unretained(this))); |
| #if defined(OS_CHROMEOS) |
| web_ui()->RegisterMessageCallback("refreshSavedScreenshots", |
| base::Bind(&FeedbackHandler::HandleRefreshSavedScreenshots, |
| base::Unretained(this))); |
| #endif |
| web_ui()->RegisterMessageCallback("sendReport", |
| base::Bind(&FeedbackHandler::HandleSendReport, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback("cancel", |
| base::Bind(&FeedbackHandler::HandleCancel, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback("openSystemTab", |
| base::Bind(&FeedbackHandler::HandleOpenSystemTab, |
| base::Unretained(this))); |
| } |
| |
| void FeedbackHandler::HandleGetDialogDefaults(const ListValue*) { |
| feedback_data_ = new FeedbackData(); |
| |
| // Send back values which the dialog js needs initially. |
| DictionaryValue dialog_defaults; |
| |
| if (category_tag_ == chrome::kAppLauncherCategoryTag) |
| dialog_defaults.SetBoolean("launcherFeedback", true); |
| |
| // Current url. |
| dialog_defaults.SetString("currentUrl", target_tab_url_); |
| |
| // Are screenshots disabled? |
| dialog_defaults.SetBoolean( |
| "disableScreenshots", |
| g_browser_process->local_state()->GetBoolean(prefs::kDisableScreenshots)); |
| |
| // User e-mail |
| std::string user_email = GetUserEmail(); |
| dialog_defaults.SetString("userEmail", user_email); |
| |
| // Set email checkbox to checked by default for cros, unchecked for Chrome. |
| dialog_defaults.SetBoolean( |
| "emailCheckboxDefault", |
| #if defined(OS_CHROMEOS) |
| true); |
| #else |
| false); |
| #endif |
| |
| |
| #if defined(OS_CHROMEOS) |
| feedback_data_->StartSyslogsCollection(); |
| |
| // On ChromeOS if the user's email is blank, it means we don't |
| // have a logged in user, hence don't use saved screenshots. |
| dialog_defaults.SetBoolean("useSaved", !user_email.empty()); |
| #endif |
| |
| web_ui()->CallJavascriptFunction("setupDialogDefaults", dialog_defaults); |
| } |
| |
| void FeedbackHandler::HandleRefreshCurrentScreenshot(const ListValue*) { |
| std::string current_screenshot( |
| std::string(ScreenshotSource::kScreenshotUrlRoot) + |
| std::string(ScreenshotSource::kScreenshotCurrent)); |
| StringValue screenshot(current_screenshot); |
| web_ui()->CallJavascriptFunction("setupCurrentScreenshot", screenshot); |
| } |
| |
| #if defined(OS_CHROMEOS) |
| void FeedbackHandler::HandleRefreshSavedScreenshots(const ListValue*) { |
| std::vector<std::string>* saved_screenshots = new std::vector<std::string>; |
| base::FilePath filepath = DownloadPrefs::FromBrowserContext( |
| tab_->GetBrowserContext())->DownloadPath(); |
| base::Closure refresh_callback = base::Bind( |
| &FeedbackHandler::RefreshSavedScreenshotsCallback, |
| AsWeakPtr(), base::Owned(saved_screenshots)); |
| if (drive::util::IsUnderDriveMountPoint(filepath)) { |
| GetMostRecentScreenshotsDrive( |
| filepath, saved_screenshots, kMaxSavedScreenshots, refresh_callback); |
| } else { |
| BrowserThread::PostTaskAndReply( |
| BrowserThread::FILE, FROM_HERE, |
| base::Bind(&FeedbackUI::GetMostRecentScreenshots, filepath, |
| base::Unretained(saved_screenshots), kMaxSavedScreenshots), |
| refresh_callback); |
| } |
| } |
| |
| void FeedbackHandler::RefreshSavedScreenshotsCallback( |
| std::vector<std::string>* saved_screenshots) { |
| ListValue screenshots_list; |
| for (size_t i = 0; i < saved_screenshots->size(); ++i) |
| screenshots_list.Append(new StringValue((*saved_screenshots)[i])); |
| web_ui()->CallJavascriptFunction("setupSavedScreenshots", screenshots_list); |
| } |
| |
| void FeedbackHandler::GetMostRecentScreenshotsDrive( |
| const base::FilePath& filepath, std::vector<std::string>* saved_screenshots, |
| size_t max_saved, base::Closure callback) { |
| drive::FileSystemInterface* file_system = |
| drive::util::GetFileSystemByProfile(Profile::FromWebUI(web_ui())); |
| if (!file_system) { |
| callback.Run(); |
| return; |
| } |
| |
| file_system->ReadDirectoryByPath( |
| drive::util::ExtractDrivePath(filepath), |
| base::Bind(&ReadDirectoryCallback, max_saved, saved_screenshots, |
| callback)); |
| } |
| #endif |
| |
| |
| void FeedbackHandler::HandleSendReport(const ListValue* list_value) { |
| if (!feedback_data_.get()) { |
| LOG(ERROR) << "Bug report hasn't been intialized yet."; |
| return; |
| } |
| |
| ListValue::const_iterator i = list_value->begin(); |
| std::string page_url; |
| (*i++)->GetAsString(&page_url); |
| std::string category_tag; |
| (*i++)->GetAsString(&category_tag); |
| std::string description; |
| (*i++)->GetAsString(&description); |
| std::string user_email; |
| (*i++)->GetAsString(&user_email); |
| std::string screenshot_path; |
| (*i++)->GetAsString(&screenshot_path); |
| screenshot_path.erase(0, strlen(ScreenshotSource::kScreenshotUrlRoot)); |
| |
| // Get the image to send in the report. |
| ScreenshotDataPtr image_ptr; |
| if (!screenshot_path.empty() && screenshot_source_) |
| image_ptr = screenshot_source_->GetCachedScreenshot(screenshot_path); |
| |
| #if defined(OS_CHROMEOS) |
| std::string sys_info_checkbox; |
| (*i++)->GetAsString(&sys_info_checkbox); |
| bool send_sys_info = (sys_info_checkbox == "true"); |
| |
| std::string trace_id_str; |
| (*i++)->GetAsString(&trace_id_str); |
| int trace_id = 0; |
| base::StringToInt(trace_id_str, &trace_id); |
| if (trace_id) |
| category_tag = kPerformanceCategoryTag; |
| |
| std::string attached_filename; |
| scoped_ptr<std::string> attached_filedata; |
| // If we have an attached file, we'll still have more data in the list. |
| if (i != list_value->end()) { |
| (*i++)->GetAsString(&attached_filename); |
| if (base::FilePath::IsSeparator(attached_filename[0])) { |
| // We have an attached filepath, not filename, hence we need read this |
| // this file in chrome. We won't have any file data, skip over it. |
| i++; |
| } else { |
| std::string encoded_filedata; |
| attached_filedata.reset(new std::string); |
| (*i++)->GetAsString(&encoded_filedata); |
| if (!base::Base64Decode( |
| base::StringPiece(encoded_filedata), attached_filedata.get())) { |
| LOG(ERROR) << "Unable to attach file: " << attached_filename; |
| // Clear the filename so feedback_util doesn't try to attach the file. |
| attached_filename = ""; |
| } |
| } |
| } |
| #endif |
| |
| // TODO(rkc): We are not setting the category tag here since this |
| // functionality is broken on the feedback server side. Fix this once the |
| // issue is resolved. |
| feedback_data_->set_category_tag(category_tag); |
| feedback_data_->set_description(description); |
| feedback_data_->set_image(image_ptr); |
| feedback_data_->set_page_url(page_url); |
| feedback_data_->set_profile(Profile::FromWebUI(web_ui())); |
| feedback_data_->set_user_email(user_email); |
| #if defined(OS_CHROMEOS) |
| feedback_data_->set_attached_filedata(attached_filedata.Pass()); |
| feedback_data_->set_attached_filename(attached_filename); |
| feedback_data_->set_send_sys_info(send_sys_info); |
| feedback_data_->set_timestamp(timestamp_); |
| feedback_data_->set_trace_id(trace_id); |
| #endif |
| |
| // Signal the feedback object that the data from the feedback page has been |
| // filled - the object will manage sending of the actual report. |
| feedback_data_->FeedbackPageDataComplete(); |
| |
| CloseFeedbackTab(); |
| } |
| |
| void FeedbackHandler::HandleCancel(const ListValue*) { |
| CloseFeedbackTab(); |
| } |
| |
| void FeedbackHandler::HandleOpenSystemTab(const ListValue* args) { |
| #if defined(OS_CHROMEOS) |
| web_ui()->GetWebContents()->GetDelegate()->OpenURLFromTab( |
| web_ui()->GetWebContents(), |
| content::OpenURLParams(GURL(chrome::kChromeUISystemInfoURL), |
| content::Referrer(), |
| NEW_FOREGROUND_TAB, |
| content::PAGE_TRANSITION_LINK, |
| false)); |
| #endif |
| } |
| |
| void FeedbackHandler::CloseFeedbackTab() { |
| ClobberScreenshotsSource(); |
| tab_->GetDelegate()->CloseContents(tab_); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // FeedbackUI |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| FeedbackUI::FeedbackUI(content::WebUI* web_ui) |
| : WebDialogUI(web_ui) { |
| FeedbackHandler* handler = new FeedbackHandler(web_ui->GetWebContents()); |
| web_ui->AddMessageHandler(handler); |
| |
| // The handler's init will determine whether we show the error html page. |
| content::WebUIDataSource* html_source = |
| CreateFeedbackUIHTMLSource(handler->Init()); |
| |
| // Set up the chrome://feedback/ source. |
| Profile* profile = Profile::FromWebUI(web_ui); |
| content::WebUIDataSource::Add(profile, html_source); |
| } |
| |
| #if defined(OS_CHROMEOS) |
| // static |
| void FeedbackUI::GetMostRecentScreenshots( |
| const base::FilePath& filepath, |
| std::vector<std::string>* saved_screenshots, |
| size_t max_saved) { |
| std::string pattern = |
| std::string(ScreenshotSource::kScreenshotPrefix) + "*" + |
| ScreenshotSource::kScreenshotSuffix; |
| base::FileEnumerator screenshots(filepath, false, |
| base::FileEnumerator::FILES, pattern); |
| base::FilePath screenshot = screenshots.Next(); |
| |
| std::vector<std::string> screenshot_filepaths; |
| while (!screenshot.empty()) { |
| screenshot_filepaths.push_back(screenshot.BaseName().value()); |
| screenshot = screenshots.Next(); |
| } |
| |
| size_t sort_size = std::min(max_saved, screenshot_filepaths.size()); |
| std::partial_sort(screenshot_filepaths.begin(), |
| screenshot_filepaths.begin() + sort_size, |
| screenshot_filepaths.end(), |
| ScreenshotTimestampComp); |
| for (size_t i = 0; i < sort_size; ++i) |
| saved_screenshots->push_back( |
| std::string(ScreenshotSource::kScreenshotUrlRoot) + |
| std::string(ScreenshotSource::kScreenshotSaved) + |
| screenshot_filepaths[i]); |
| } |
| #endif |