Home | History | Annotate | Download | only in webui
      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("&"), &params)) {
    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