Logo Search packages:      
Sourcecode: chromium-browser version File versions  Download package

bug_report_view.cc

// Copyright (c) 2010 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/views/bug_report_view.h"

#include "app/combobox_model.h"
#include "app/l10n_util.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/file_version_info.h"
#include "base/path_service.h"
#include "base/utf_string_conversions.h"
#include "base/waitable_event.h"
#include "chrome/app/chrome_version_info.h"
#include "chrome/browser/bug_report_util.h"
#include "chrome/browser/pref_service.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/safe_browsing/safe_browsing_util.h"
#include "chrome/browser/tab_contents/navigation_controller.h"
#include "chrome/browser/tab_contents/navigation_entry.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/net/url_fetcher.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"
#include "net/base/escape.h"
#include "unicode/locid.h"
#include "views/controls/button/checkbox.h"
#include "views/controls/label.h"
#include "views/grid_layout.h"
#include "views/standard_layout.h"
#include "views/widget/widget.h"
#include "views/window/client_view.h"
#include "views/window/window.h"

#if defined(OS_LINUX)
#include "app/x11_util.h"
#else
#include "app/win_util.h"
#endif

#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/login/user_manager.h"
#endif

using views::ColumnSet;
using views::GridLayout;

// Report a bug data version.
static const int kBugReportVersion = 1;
static const int kScreenImageRadioGroup = 2;
static const char kScreenshotsRelativePath[] = "/Screenshots";
static const char kScreenshotPattern[] = "*.png";

// Number of lines description field can display at one time.
static const int kDescriptionLines = 5;

class BugReportComboBoxModel : public ComboboxModel {
 public:
  BugReportComboBoxModel() {}

  // ComboboxModel interface.
  virtual int GetItemCount() {
    return BugReportUtil::OTHER_PROBLEM + 1;
  }

  virtual std::wstring GetItemAt(int index) {
    return GetItemAtIndex(index);
  }

  static std::wstring GetItemAtIndex(int index) {
#if defined(OS_CHROMEOS)
    switch (index) {
      case BugReportUtil::CONNECTIVITY_ISSUE:
        return l10n_util::GetString(IDS_BUGREPORT_CONNECTIVITY_ISSUE);
      case BugReportUtil::SYNC_ISSUE:
        return l10n_util::GetString(IDS_BUGREPORT_SYNC_ISSUE);
      case BugReportUtil::CRASH_ISSUE:
        return l10n_util::GetString(IDS_BUGREPORT_CRASH_ISSUE);
      case BugReportUtil::PAGE_FORMATTING:
        return l10n_util::GetString(IDS_BUGREPORT_PAGE_FORMATTING);
      case BugReportUtil::EXTENSION_ISSUE:
        return l10n_util::GetString(IDS_BUGREPORT_EXTENSION_ISSUE);
      case BugReportUtil::SUSPEND_ISSUE:
        return l10n_util::GetString(IDS_BUGREPORT_SUSPEND_ISSUE);
      case BugReportUtil::PHISHING_PAGE:
        return l10n_util::GetString(IDS_BUGREPORT_PHISHING_PAGE);
      case BugReportUtil::OTHER_PROBLEM:
        return l10n_util::GetString(IDS_BUGREPORT_OTHER_PROBLEM);
      default:
        NOTREACHED();
        return std::wstring();
    }
#else
    switch (index) {
      case BugReportUtil::PAGE_WONT_LOAD:
        return l10n_util::GetString(IDS_BUGREPORT_PAGE_WONT_LOAD);
      case BugReportUtil::PAGE_LOOKS_ODD:
        return l10n_util::GetString(IDS_BUGREPORT_PAGE_LOOKS_ODD);
      case BugReportUtil::PHISHING_PAGE:
        return l10n_util::GetString(IDS_BUGREPORT_PHISHING_PAGE);
      case BugReportUtil::CANT_SIGN_IN:
        return l10n_util::GetString(IDS_BUGREPORT_CANT_SIGN_IN);
      case BugReportUtil::CHROME_MISBEHAVES:
        return l10n_util::GetString(IDS_BUGREPORT_CHROME_MISBEHAVES);
      case BugReportUtil::SOMETHING_MISSING:
        return l10n_util::GetString(IDS_BUGREPORT_SOMETHING_MISSING);
      case BugReportUtil::BROWSER_CRASH:
        return l10n_util::GetString(IDS_BUGREPORT_BROWSER_CRASH);
      case BugReportUtil::OTHER_PROBLEM:
        return l10n_util::GetString(IDS_BUGREPORT_OTHER_PROBLEM);
      default:
        NOTREACHED();
        return std::wstring();
    }
#endif
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(BugReportComboBoxModel);
};

namespace {

#if defined(OS_CHROMEOS)
class LastScreenshotTask : public Task {
 public:
  LastScreenshotTask(std::string* image_str,
                     base::WaitableEvent* task_waitable)
      : image_str_(image_str),
        task_waitable_(task_waitable) {
  }

 private:
  void Run() {
    FilePath fileshelf_path;
    // TODO(rkc): Change this to use FilePath.Append() once the cros
    // issue with with it is fixed
    if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS,
                          &fileshelf_path)) {
      *image_str_ = "";
      task_waitable_->Signal();
    }

    FilePath screenshots_path(fileshelf_path.value() +
                              std::string(kScreenshotsRelativePath));
    file_util::FileEnumerator screenshots(screenshots_path, false,
                                          file_util::FileEnumerator::FILES,
                                          std::string(kScreenshotPattern));
    FilePath screenshot = screenshots.Next();
    FilePath latest("");
    time_t last_mtime = 0;
    while (!screenshot.empty()) {
      file_util::FileEnumerator::FindInfo info;
      screenshots.GetFindInfo(&info);
      if (info.stat.st_mtime > last_mtime) {
        last_mtime = info.stat.st_mtime;
        latest = screenshot;
      }
      screenshot = screenshots.Next();
    }

    if (!file_util::ReadFileToString(latest, image_str_))
      *image_str_ = "";
    task_waitable_->Signal();
  }
 private:
  std::string* image_str_;
  base::WaitableEvent* task_waitable_;
};
#endif

bool GetLastScreenshot(std::string* image_str) {
#if defined(OS_CHROMEOS)
  base::WaitableEvent task_waitable(true, false);
  ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE,
                         new LastScreenshotTask(image_str, &task_waitable));
  task_waitable.Wait();
  if (*image_str == "")
    return false;
  else
    return true;
#else
  return false;
#endif
}

} // namespace

namespace browser {

// Global "display this dialog" function declared in browser_dialogs.h.
void ShowBugReportView(views::Window* parent,
                       Profile* profile,
                       TabContents* tab) {
  BugReportView* view = new BugReportView(profile, tab);

  // Get the size of the parent window to capture screenshot dimensions
  gfx::Rect screen_size = parent->GetBounds();
  view->set_screen_size(screen_size);

  // Grab an exact snapshot of the window that the user is seeing (i.e. as
  // rendered--do not re-render, and include windowed plugins).
  std::vector<unsigned char> *screenshot_png = new std::vector<unsigned char>;

#if defined(OS_LINUX)
  x11_util::GrabWindowSnapshot(parent->GetNativeWindow(), screenshot_png);
#else
  win_util::GrabWindowSnapshot(parent->GetNativeWindow(), screenshot_png);
#endif

  // The BugReportView takes ownership of the image data, and will dispose of
  // it in its destructor
  view->set_captured_image(screenshot_png);

#if defined(OS_CHROMEOS)
  // Get last screenshot taken
  std::string image_str;
  bool have_last_image = false;
  if (GetLastScreenshot(&image_str)) {
    // reuse screenshot_png; previous pointer now owned by BugReportView
    screenshot_png = new std::vector<unsigned char>(image_str.begin(),
                                                    image_str.end());
    have_last_image = true;
  } else {
    // else set it to be an empty vector
    screenshot_png = new std::vector<unsigned char>;
  }
  view->set_last_image(screenshot_png);

  // Create and show the dialog
  views::Window::CreateChromeWindow(parent->GetNativeWindow(), gfx::Rect(),
                                    view)->Show();
  if (!have_last_image)
    view->DisableLastImageRadio();
#endif
}

}  // namespace browser

// BugReportView - create and submit a bug report from the user.
// This is separate from crash reporting, which is handled by Breakpad.
BugReportView::BugReportView(Profile* profile, TabContents* tab)
    : include_page_source_checkbox_(NULL),
      include_page_image_checkbox_(NULL),
      profile_(profile),
      tab_(tab),
      problem_type_(0) {
  DCHECK(profile);
  SetupControl();

  // We want to use the URL of the current committed entry (the current URL may
  // actually be the pending one).
  if (tab->controller().GetActiveEntry()) {
    page_url_text_->SetText(UTF8ToUTF16(
        tab->controller().GetActiveEntry()->url().spec()));
  }

#if defined(OS_CHROMEOS)
  // Get and set the gaia e-mail
  chromeos::UserManager* manager = chromeos::UserManager::Get();
  if (!manager) {
    user_email_text_->SetText(UTF8ToUTF16(std::string("")));
  } else {
    const std::string& email = manager->logged_in_user().email();
    user_email_text_->SetText(UTF8ToUTF16(email));
    if (!email.empty())
      user_email_text_->SetEnabled(false);
  }
#endif

  // Retrieve the application version info.
  scoped_ptr<FileVersionInfo> version_info(
      chrome_app::GetChromeVersionInfo());
  if (version_info.get()) {
    version_ = version_info->product_name() + L" - " +
        version_info->file_version() +
        L" (" + version_info->last_change() + L")";
  }


  FilePath tmpfilename;

#if defined(OS_CHROMEOS)
  chromeos::SyslogsLibrary* syslogs_lib =
      chromeos::CrosLibrary::Get()->GetSyslogsLibrary();
  if (syslogs_lib) {
    sys_info_.reset(syslogs_lib->GetSyslogs(&tmpfilename));
  }
#endif
}

BugReportView::~BugReportView() {
}

void BugReportView::SetupControl() {
  bug_type_model_.reset(new BugReportComboBoxModel);

  // Adds all controls.
  bug_type_label_ = new views::Label(
      l10n_util::GetString(IDS_BUGREPORT_BUG_TYPE));
  bug_type_combo_ = new views::Combobox(bug_type_model_.get());
  bug_type_combo_->set_listener(this);
  bug_type_combo_->SetAccessibleName(bug_type_label_->GetText());

  page_title_label_ = new views::Label(
      l10n_util::GetString(IDS_BUGREPORT_REPORT_PAGE_TITLE));
  page_title_text_ = new views::Label(UTF16ToWideHack(tab_->GetTitle()));
  page_url_label_ = new views::Label(
      l10n_util::GetString(IDS_BUGREPORT_REPORT_URL_LABEL));
  // page_url_text_'s text (if any) is filled in after dialog creation.
  page_url_text_ = new views::Textfield;
  page_url_text_->SetController(this);
  page_url_text_->SetAccessibleName(page_url_label_->GetText());

#if defined(OS_CHROMEOS)
  user_email_label_ = new views::Label(
      l10n_util::GetString(IDS_BUGREPORT_USER_EMAIL_LABEL));
  // user_email_text_'s text (if any) is filled in after dialog creation.
  user_email_text_ = new views::Textfield;
  user_email_text_->SetController(this);
  user_email_text_->SetAccessibleName(user_email_label_->GetText());
#endif

  description_label_ = new views::Label(
      l10n_util::GetString(IDS_BUGREPORT_DESCRIPTION_LABEL));
  description_text_ =
      new views::Textfield(views::Textfield::STYLE_MULTILINE);
  description_text_->SetHeightInLines(kDescriptionLines);
  description_text_->SetAccessibleName(description_label_->GetText());

  include_page_source_checkbox_ = new views::Checkbox(
      l10n_util::GetString(IDS_BUGREPORT_INCLUDE_PAGE_SOURCE_CHKBOX));
  include_page_source_checkbox_->SetChecked(true);

#if defined(OS_CHROMEOS)
  include_last_screen_image_radio_ = new views::RadioButton(
      l10n_util::GetString(IDS_BUGREPORT_INCLUDE_LAST_SCREEN_IMAGE),
      kScreenImageRadioGroup);

  include_new_screen_image_radio_ = new views::RadioButton(
      l10n_util::GetString(IDS_BUGREPORT_INCLUDE_NEW_SCREEN_IMAGE),
      kScreenImageRadioGroup);

  include_no_screen_image_radio_ = new views::RadioButton(
      l10n_util::GetString(IDS_BUGREPORT_INCLUDE_NO_SCREEN_IMAGE),
      kScreenImageRadioGroup);

  system_information_url_control_ = new views::Link(
      l10n_util::GetString(IDS_BUGREPORT_SYSTEM_INFORMATION_URL_TEXT));
  system_information_url_control_->SetController(this);

  include_new_screen_image_radio_->SetChecked(true);
#endif
  include_page_image_checkbox_ = new views::Checkbox(
      l10n_util::GetString(IDS_BUGREPORT_INCLUDE_PAGE_IMAGE_CHKBOX));
  include_page_image_checkbox_->SetChecked(true);

  // Arranges controls by using GridLayout.
  const int column_set_id = 0;
  GridLayout* layout = CreatePanelGridLayout(this);
  SetLayoutManager(layout);
  ColumnSet* column_set = layout->AddColumnSet(column_set_id);
  column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 0,
                        GridLayout::USE_PREF, 0, 0);
  column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing * 2);
  column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
                        GridLayout::USE_PREF, 0, 0);

  // Page Title and text.
  layout->StartRow(0, column_set_id);
  layout->AddView(page_title_label_);
  layout->AddView(page_title_text_, 1, 1, GridLayout::LEADING,
                  GridLayout::FILL);
  layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);

  // Bug type and combo box.
  layout->StartRow(0, column_set_id);
  layout->AddView(bug_type_label_, 1, 1, GridLayout::LEADING, GridLayout::FILL);
  layout->AddView(bug_type_combo_);
  layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);

  // Page URL and text field.
  layout->StartRow(0, column_set_id);
  layout->AddView(page_url_label_);
  layout->AddView(page_url_text_);
  layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);

  // Description label and text field.
  layout->StartRow(0, column_set_id);
  layout->AddView(description_label_, 1, 1, GridLayout::LEADING,
                  GridLayout::LEADING);
  layout->AddView(description_text_, 1, 1, GridLayout::FILL,
                  GridLayout::LEADING);
#if defined(OS_CHROMEOS)
  layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
  // Page URL and text field.
  layout->StartRow(0, column_set_id);
  layout->AddView(user_email_label_);
  layout->AddView(user_email_text_);
#endif
  layout->AddPaddingRow(0, kUnrelatedControlVerticalSpacing);

  // Checkboxes.
  // The include page source checkbox is hidden until we can make it work.
  // layout->StartRow(0, column_set_id);
  // layout->SkipColumns(1);
  // layout->AddView(include_page_source_checkbox_);
  // layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
  layout->StartRow(0, column_set_id);
  layout->SkipColumns(1);
#if defined(OS_CHROMEOS)
  // Radio boxes to select last screen shot or,

  // new screenshot
  layout->AddView(include_new_screen_image_radio_);
  layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
  // last screenshot taken
  layout->StartRow(0, column_set_id);
  layout->SkipColumns(1);
  layout->AddView(include_last_screen_image_radio_);
  layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
  // no screenshot
  layout->StartRow(0, column_set_id);
  layout->SkipColumns(1);
  layout->AddView(include_no_screen_image_radio_);
  layout->AddPaddingRow(0, kUnrelatedControlVerticalSpacing);

  layout->StartRow(0, column_set_id);
  layout->SkipColumns(1);
  layout->AddView(system_information_url_control_, 1, 1, GridLayout::LEADING,
                  GridLayout::CENTER);
#else
  if (include_page_image_checkbox_) {
    layout->StartRow(0, column_set_id);
    layout->SkipColumns(1);
    layout->AddView(include_page_image_checkbox_);
  }
#endif

  layout->AddPaddingRow(0, kUnrelatedControlVerticalSpacing);
}

gfx::Size BugReportView::GetPreferredSize() {
  gfx::Size size = views::Window::GetLocalizedContentsSize(
      IDS_BUGREPORT_DIALOG_WIDTH_CHARS,
#if defined(OS_CHROMEOS)
      IDS_CHROMEOS_BUGREPORT_DIALOG_HEIGHT_LINES);
#else
      IDS_BUGREPORT_DIALOG_HEIGHT_LINES);
#endif
 return size;
}

void BugReportView::UpdateReportingControls(bool is_phishing_report) {
  // page source, screen/page images, system information
  // are not needed if it's a phishing report

  include_page_source_checkbox_->SetEnabled(!is_phishing_report);
  include_page_source_checkbox_->SetChecked(!is_phishing_report);

#if defined(OS_CHROMEOS)
  include_new_screen_image_radio_->SetEnabled(!is_phishing_report);
  if (!last_image_->empty())
    include_last_screen_image_radio_->SetEnabled(!is_phishing_report);
  include_no_screen_image_radio_->SetEnabled(!is_phishing_report);
#else
  if (include_page_image_checkbox_) {
    include_page_image_checkbox_->SetEnabled(!is_phishing_report);
    include_page_image_checkbox_->SetChecked(!is_phishing_report);
  }
#endif
}

void BugReportView::ItemChanged(views::Combobox* combobox,
                                int prev_index,
                                int new_index) {
  if (new_index == prev_index)
    return;

  problem_type_ = new_index;
  bool is_phishing_report = new_index == BugReportUtil::PHISHING_PAGE;

  description_text_->SetEnabled(!is_phishing_report);
  description_text_->SetReadOnly(is_phishing_report);
  if (is_phishing_report) {
    old_report_text_ = UTF16ToWide(description_text_->text());
    description_text_->SetText(string16());
  } else if (!old_report_text_.empty()) {
    description_text_->SetText(WideToUTF16Hack(old_report_text_));
    old_report_text_.clear();
  }

  UpdateReportingControls(is_phishing_report);
  GetDialogClientView()->UpdateDialogButtons();
}

void BugReportView::ContentsChanged(views::Textfield* sender,
                                    const string16& new_contents) {
}

bool BugReportView::HandleKeystroke(views::Textfield* sender,
                                    const views::Textfield::Keystroke& key) {
  return false;
}

std::wstring BugReportView::GetDialogButtonLabel(
    MessageBoxFlags::DialogButton button) const {
  if (button == MessageBoxFlags::DIALOGBUTTON_OK) {
    if (problem_type_ == BugReportUtil::PHISHING_PAGE)
      return l10n_util::GetString(IDS_BUGREPORT_SEND_PHISHING_REPORT);
    else
      return l10n_util::GetString(IDS_BUGREPORT_SEND_REPORT);
  } else {
    return std::wstring();
  }
}

int BugReportView::GetDefaultDialogButton() const {
  return MessageBoxFlags::DIALOGBUTTON_NONE;
}

bool BugReportView::CanResize() const {
  return false;
}

bool BugReportView::CanMaximize() const {
  return false;
}

bool BugReportView::IsAlwaysOnTop() const {
  return false;
}

bool BugReportView::HasAlwaysOnTopMenu() const {
  return false;
}

bool BugReportView::IsModal() const {
  return true;
}

std::wstring BugReportView::GetWindowTitle() const {
  return l10n_util::GetString(IDS_BUGREPORT_TITLE);
}


bool BugReportView::Accept() {
  if (IsDialogButtonEnabled(MessageBoxFlags::DIALOGBUTTON_OK)) {
    if (problem_type_ == BugReportUtil::PHISHING_PAGE) {
      BugReportUtil::ReportPhishing(tab_,
          UTF16ToUTF8(page_url_text_->text()));
    } else {
      char* image_data = NULL;
      size_t image_data_size = 0;
#if defined(OS_CHROMEOS)
      if (include_new_screen_image_radio_->checked() &&
          !captured_image_->empty()) {
        image_data = reinterpret_cast<char *>(&captured_image_->front());
        image_data_size = captured_image_->size();
      } else if (include_last_screen_image_radio_->checked() &&
                 !last_image_->empty()) {
        image_data = reinterpret_cast<char *>(&last_image_->front());
        image_data_size = last_image_->size();
      }
#else
      if (include_page_image_checkbox_->checked() && captured_image_.get() &&
          !captured_image_->empty()) {
        image_data = reinterpret_cast<char *>(&captured_image_->front());
        image_data_size = captured_image_->size();
      }
#endif
#if defined(OS_CHROMEOS)
      BugReportUtil::SendReport(profile_,
          WideToUTF8(page_title_text_->GetText()),
          problem_type_,
          UTF16ToUTF8(page_url_text_->text()),
          UTF16ToUTF8(user_email_text_->text()),
          UTF16ToUTF8(description_text_->text()),
          image_data, image_data_size,
          screen_size_.width(), screen_size_.height(),
          WideToUTF8(bug_type_combo_->model()->GetItemAt(problem_type_)),
          sys_info_.get());
#else
      BugReportUtil::SendReport(profile_,
          WideToUTF8(page_title_text_->GetText()),
          problem_type_,
          UTF16ToUTF8(page_url_text_->text()),
          std::string(),
          UTF16ToUTF8(description_text_->text()),
          image_data, image_data_size,
          screen_size_.width(), screen_size_.height());
#endif
    }
  }
  return true;
}

#if defined(OS_CHROMEOS)
void BugReportView::LinkActivated(views::Link* source,
                                    int event_flags) {
  GURL url;
  if (source == system_information_url_control_) {
    url = GURL(chrome::kAboutSystemURL);
  } else {
    NOTREACHED() << "Unknown link source";
    return;
  }

  Browser* browser = BrowserList::GetLastActive();
  if (browser)
    browser->OpenURL(url, GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK);
}
#endif


views::View* BugReportView::GetContentsView() {
  return this;
}

Generated by  Doxygen 1.6.0   Back to index