// Copyright (c) 2011 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/bug_report_ui.h" #include <algorithm> #include <string> #include <vector> #include "base/callback.h" #include "base/logging.h" #include "base/memory/singleton.h" #include "base/memory/weak_ptr.h" #include "base/message_loop.h" #include "base/string_number_conversions.h" #include "base/string_piece.h" #include "base/utf_string_conversions.h" #include "base/values.h" #include "chrome/browser/bug_report_data.h" #include "chrome/browser/bug_report_util.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/webui/screenshot_source.h" #include "chrome/browser/ui/window_snapshot/window_snapshot.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/jstemplate_builder.h" #include "chrome/common/url_constants.h" #include "content/browser/browser_thread.h" #include "content/browser/tab_contents/tab_contents.h" #include "grit/browser_resources.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "grit/locale_settings.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/rect.h" #if defined(OS_CHROMEOS) #include "base/file_util.h" #include "base/path_service.h" #include "base/synchronization/waitable_event.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/cros/syslogs_library.h" #include "chrome/browser/chromeos/login/user_manager.h" #endif namespace { const char kScreenshotBaseUrl[] = "chrome://screenshots/"; const char kCurrentScreenshotUrl[] = "chrome://screenshots/current"; #if defined(OS_CHROMEOS) const char kSavedScreenshotsUrl[] = "chrome://screenshots/saved/"; const char kScreenshotPattern[] = "*.png"; const char kScreenshotsRelativePath[] = "/Screenshots"; const size_t kMaxSavedScreenshots = 2; #endif #if defined(OS_CHROMEOS) void GetSavedScreenshots(std::vector<std::string>* saved_screenshots, base::WaitableEvent* done) { saved_screenshots->clear(); FilePath fileshelf_path; if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &fileshelf_path)) { done->Signal(); return; } // TODO(rkc): Change this to use FilePath.Append() once the cros // issue with it is fixed 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(); while (!screenshot.empty()) { saved_screenshots->push_back(std::string(kSavedScreenshotsUrl) + screenshot.BaseName().value()); if (saved_screenshots->size() >= kMaxSavedScreenshots) break; screenshot = screenshots.Next(); } done->Signal(); } // This fuction posts a task to the file thread to create/list all the current // and saved screenshots. void GetScreenshotUrls(std::vector<std::string>* saved_screenshots) { base::WaitableEvent done(true, false); BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, NewRunnableFunction(&GetSavedScreenshots, saved_screenshots, &done)); done.Wait(); } std::string GetUserEmail() { chromeos::UserManager* manager = chromeos::UserManager::Get(); if (!manager) return std::string(); else return manager->logged_in_user().email(); } #endif // Returns the index of the feedback tab if already open, -1 otherwise int GetIndexOfFeedbackTab(Browser* browser) { GURL bug_report_url(chrome::kChromeUIBugReportURL); for (int i = 0; i < browser->tab_count(); ++i) { TabContents* tab = browser->GetTabContentsAt(i); if (tab && tab->GetURL().GetWithEmptyPath() == bug_report_url) return i; } return -1; } } // namespace namespace browser { // TODO(rkc): Eventually find a better way to do this std::vector<unsigned char>* last_screenshot_png = 0; gfx::Rect screen_size; void RefreshLastScreenshot(Browser* browser) { if (last_screenshot_png) last_screenshot_png->clear(); else last_screenshot_png = new std::vector<unsigned char>; gfx::NativeWindow native_window = browser->window()->GetNativeHandle(); screen_size = browser::GrabWindowSnapshot(native_window, last_screenshot_png); } void ShowHtmlBugReportView(Browser* browser) { // 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->ActivateTabAt(feedback_tab_index, true); return; } RefreshLastScreenshot(browser); std::string bug_report_url = std::string(chrome::kChromeUIBugReportURL) + "#" + base::IntToString(browser->active_index()); browser->ShowSingletonTab(GURL(bug_report_url)); } } // namespace browser class BugReportUIHTMLSource : public ChromeURLDataManager::DataSource { public: explicit BugReportUIHTMLSource(base::StringPiece html); // Called when the network layer has requested a resource underneath // the path we registered. virtual void StartDataRequest(const std::string& path, bool is_incognito, int request_id); virtual std::string GetMimeType(const std::string&) const { return "text/html"; } private: base::StringPiece bug_report_html_; ~BugReportUIHTMLSource() {} DISALLOW_COPY_AND_ASSIGN(BugReportUIHTMLSource); }; // The handler for Javascript messages related to the "bug report" dialog class BugReportHandler : public WebUIMessageHandler, public base::SupportsWeakPtr<BugReportHandler> { public: explicit BugReportHandler(TabContents* tab); virtual ~BugReportHandler(); // Init work after Attach. base::StringPiece Init(); // WebUIMessageHandler implementation. virtual WebUIMessageHandler* Attach(WebUI* web_ui); virtual void RegisterMessages(); private: void HandleGetDialogDefaults(const ListValue* args); void HandleRefreshCurrentScreenshot(const ListValue* args); #if defined(OS_CHROMEOS) void HandleRefreshSavedScreenshots(const ListValue* args); #endif void HandleSendReport(const ListValue* args); void HandleCancel(const ListValue* args); void HandleOpenSystemTab(const ListValue* args); void SetupScreenshotsSource(); void ClobberScreenshotsSource(); void CancelFeedbackCollection(); void CloseFeedbackTab(); TabContents* tab_; ScreenshotSource* screenshot_source_; BugReportData* bug_report_; std::string target_tab_url_; #if defined(OS_CHROMEOS) // Variables to track SyslogsLibrary::RequestSyslogs callback. chromeos::SyslogsLibrary::Handle syslogs_handle_; CancelableRequestConsumer syslogs_consumer_; #endif DISALLOW_COPY_AND_ASSIGN(BugReportHandler); }; //////////////////////////////////////////////////////////////////////////////// // // BugReportHTMLSource // //////////////////////////////////////////////////////////////////////////////// BugReportUIHTMLSource::BugReportUIHTMLSource(base::StringPiece html) : DataSource(chrome::kChromeUIBugReportHost, MessageLoop::current()) { bug_report_html_ = html; } void BugReportUIHTMLSource::StartDataRequest(const std::string& path, bool is_incognito, int request_id) { DictionaryValue localized_strings; localized_strings.SetString(std::string("title"), l10n_util::GetStringUTF8(IDS_BUGREPORT_TITLE)); localized_strings.SetString(std::string("page-title"), l10n_util::GetStringUTF8(IDS_BUGREPORT_REPORT_PAGE_TITLE)); localized_strings.SetString(std::string("issue-with"), l10n_util::GetStringUTF8(IDS_BUGREPORT_ISSUE_WITH)); localized_strings.SetString(std::string("page-url"), l10n_util::GetStringUTF8(IDS_BUGREPORT_REPORT_URL_LABEL)); localized_strings.SetString(std::string("description"), l10n_util::GetStringUTF8(IDS_BUGREPORT_DESCRIPTION_LABEL)); localized_strings.SetString(std::string("current-screenshot"), l10n_util::GetStringUTF8(IDS_BUGREPORT_SCREENSHOT_LABEL)); localized_strings.SetString(std::string("saved-screenshot"), l10n_util::GetStringUTF8(IDS_BUGREPORT_SAVED_SCREENSHOT_LABEL)); #if defined(OS_CHROMEOS) localized_strings.SetString(std::string("user-email"), l10n_util::GetStringUTF8(IDS_BUGREPORT_USER_EMAIL_LABEL)); localized_strings.SetString(std::string("sysinfo"), l10n_util::GetStringUTF8( IDS_BUGREPORT_INCLUDE_SYSTEM_INFORMATION_CHKBOX)); localized_strings.SetString(std::string("currentscreenshots"), l10n_util::GetStringUTF8(IDS_BUGREPORT_CURRENT_SCREENSHOTS)); localized_strings.SetString(std::string("savedscreenshots"), l10n_util::GetStringUTF8(IDS_BUGREPORT_SAVED_SCREENSHOTS)); localized_strings.SetString(std::string("choose-different-screenshot"), l10n_util::GetStringUTF8( IDS_BUGREPORT_CHOOSE_DIFFERENT_SCREENSHOT)); localized_strings.SetString(std::string("choose-original-screenshot"), l10n_util::GetStringUTF8( IDS_BUGREPORT_CHOOSE_ORIGINAL_SCREENSHOT)); #else localized_strings.SetString(std::string("currentscreenshots"), l10n_util::GetStringUTF8(IDS_BUGREPORT_INCLUDE_NEW_SCREEN_IMAGE)); #endif localized_strings.SetString(std::string("noscreenshot"), l10n_util::GetStringUTF8(IDS_BUGREPORT_INCLUDE_NO_SCREENSHOT)); localized_strings.SetString(std::string("send-report"), l10n_util::GetStringUTF8(IDS_BUGREPORT_SEND_REPORT)); localized_strings.SetString(std::string("cancel"), l10n_util::GetStringUTF8(IDS_CANCEL)); // Option strings for the "issue with" drop-down. localized_strings.SetString(std::string("issue-choose"), l10n_util::GetStringUTF8(IDS_BUGREPORT_CHOOSE_ISSUE)); localized_strings.SetString(std::string("no-issue-selected"), l10n_util::GetStringUTF8(IDS_BUGREPORT_NO_ISSUE_SELECTED)); localized_strings.SetString(std::string("no-description"), l10n_util::GetStringUTF8(IDS_BUGREPORT_NO_DESCRIPTION)); localized_strings.SetString(std::string("no-saved-screenshots"), l10n_util::GetStringUTF8(IDS_BUGREPORT_NO_SAVED_SCREENSHOTS_HELP)); localized_strings.SetString(std::string("privacy-note"), l10n_util::GetStringUTF8(IDS_BUGREPORT_PRIVACY_NOTE)); // TODO(rkc): Find some way to ensure this order of dropdowns is in sync // with the order in the userfeedback ChromeData proto buffer #if defined(OS_CHROMEOS) // Dropdown for ChromeOS: // // Connectivity // Sync // Crash // Page Formatting // Extensions or Apps // Standby or Resume // Phishing Page // General Feedback/Other localized_strings.SetString(std::string("issue-connectivity"), l10n_util::GetStringUTF8(IDS_BUGREPORT_CONNECTIVITY)); localized_strings.SetString(std::string("issue-sync"), l10n_util::GetStringUTF8(IDS_BUGREPORT_SYNC)); localized_strings.SetString(std::string("issue-crashes"), l10n_util::GetStringUTF8(IDS_BUGREPORT_CRASHES)); localized_strings.SetString(std::string("issue-page-formatting"), l10n_util::GetStringUTF8(IDS_BUGREPORT_PAGE_FORMATTING)); localized_strings.SetString(std::string("issue-extensions"), l10n_util::GetStringUTF8(IDS_BUGREPORT_EXTENSIONS)); localized_strings.SetString(std::string("issue-standby"), l10n_util::GetStringUTF8(IDS_BUGREPORT_STANDBY_RESUME)); localized_strings.SetString(std::string("issue-phishing"), l10n_util::GetStringUTF8(IDS_BUGREPORT_PHISHING_PAGE)); localized_strings.SetString(std::string("issue-other"), l10n_util::GetStringUTF8(IDS_BUGREPORT_GENERAL)); #else // Dropdown for Chrome: // // Page formatting or layout // Pages not loading // Plug-ins (e.g. Adobe Flash Player, Quicktime, etc) // Tabs or windows // Synced preferences // Crashes // Extensions or apps // Phishing // Other localized_strings.SetString(std::string("issue-page-formatting"), l10n_util::GetStringUTF8(IDS_BUGREPORT_PAGE_FORMATTING)); localized_strings.SetString(std::string("issue-page-load"), l10n_util::GetStringUTF8(IDS_BUGREPORT_PAGE_LOAD)); localized_strings.SetString(std::string("issue-plugins"), l10n_util::GetStringUTF8(IDS_BUGREPORT_PLUGINS)); localized_strings.SetString(std::string("issue-tabs"), l10n_util::GetStringUTF8(IDS_BUGREPORT_TABS)); localized_strings.SetString(std::string("issue-sync"), l10n_util::GetStringUTF8(IDS_BUGREPORT_SYNC)); localized_strings.SetString(std::string("issue-crashes"), l10n_util::GetStringUTF8(IDS_BUGREPORT_CRASHES)); localized_strings.SetString(std::string("issue-extensions"), l10n_util::GetStringUTF8(IDS_BUGREPORT_EXTENSIONS)); localized_strings.SetString(std::string("issue-phishing"), l10n_util::GetStringUTF8(IDS_BUGREPORT_PHISHING_PAGE)); localized_strings.SetString(std::string("issue-other"), l10n_util::GetStringUTF8(IDS_BUGREPORT_OTHER)); #endif SetFontAndTextDirection(&localized_strings); const std::string full_html = jstemplate_builder::GetI18nTemplateHtml( bug_report_html_, &localized_strings); scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes); html_bytes->data.resize(full_html.size()); std::copy(full_html.begin(), full_html.end(), html_bytes->data.begin()); SendResponse(request_id, html_bytes); } //////////////////////////////////////////////////////////////////////////////// // // BugReportData // //////////////////////////////////////////////////////////////////////////////// void BugReportData::SendReport() { #if defined(OS_CHROMEOS) // In case we already got the syslogs and sent the report, leave if (sent_report_) return; // Set send_report_ so that no one else processes SendReport sent_report_ = true; #endif int image_data_size = image_.size(); char* image_data = image_data_size ? reinterpret_cast<char*>(&(image_.front())) : NULL; BugReportUtil::SendReport(profile_ , problem_type_ , page_url_ , description_ , image_data , image_data_size , browser::screen_size.width() , browser::screen_size.height() #if defined(OS_CHROMEOS) , user_email_ , zip_content_ ? zip_content_->c_str() : NULL , zip_content_ ? zip_content_->length() : 0 , send_sys_info_ ? sys_info_ : NULL #endif ); #if defined(OS_CHROMEOS) if (sys_info_) { delete sys_info_; sys_info_ = NULL; } if (zip_content_) { delete zip_content_; zip_content_ = NULL; } #endif // Once the report has been sent, this object has no purpose in life, delete // ourselves. delete this; } //////////////////////////////////////////////////////////////////////////////// // // BugReportHandler // //////////////////////////////////////////////////////////////////////////////// BugReportHandler::BugReportHandler(TabContents* tab) : tab_(tab), screenshot_source_(NULL), bug_report_(NULL) #if defined(OS_CHROMEOS) , syslogs_handle_(0) #endif { } BugReportHandler::~BugReportHandler() { // Just in case we didn't send off bug_report_ to SendReport if (bug_report_) { // If we're deleting the report object, cancel feedback collection first CancelFeedbackCollection(); delete bug_report_; } } void BugReportHandler::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 tab_->profile()->GetChromeURLDataManager()->AddDataSource( new ScreenshotSource(NULL)); // clobber last screenshot if (browser::last_screenshot_png) browser::last_screenshot_png->clear(); } void BugReportHandler::SetupScreenshotsSource() { // If we don't already have a screenshot source object created, create one. if (!screenshot_source_) screenshot_source_ = new ScreenshotSource( browser::last_screenshot_png); // Add the source to the data manager. tab_->profile()->GetChromeURLDataManager()->AddDataSource(screenshot_source_); } WebUIMessageHandler* BugReportHandler::Attach(WebUI* web_ui) { SetupScreenshotsSource(); return WebUIMessageHandler::Attach(web_ui); } base::StringPiece BugReportHandler::Init() { std::string page_url; if (tab_->controller().GetActiveEntry()) { page_url = tab_->controller().GetActiveEntry()->url().spec(); } std::string params = page_url.substr(strlen(chrome::kChromeUIBugReportURL)); // Erase the # - the first character. if (params.length()) params.erase(params.begin(), params.begin() + 1); int index = 0; if (!base::StringToInt(params, &index)) { return base::StringPiece( ResourceBundle::GetSharedInstance().GetRawDataResource( IDR_BUGREPORT_HTML_INVALID)); } Browser* browser = BrowserList::GetLastActive(); // Sanity checks. if (((index == 0) && (params != "0")) || !browser || index >= browser->tab_count()) { return base::StringPiece( ResourceBundle::GetSharedInstance().GetRawDataResource( IDR_BUGREPORT_HTML_INVALID)); } TabContents* target_tab = browser->GetTabContentsAt(index); if (target_tab) { target_tab_url_ = target_tab->GetURL().spec(); } // Setup the screenshot source after we've verified input is legit. SetupScreenshotsSource(); return base::StringPiece( ResourceBundle::GetSharedInstance().GetRawDataResource( IDR_BUGREPORT_HTML)); } void BugReportHandler::RegisterMessages() { web_ui_->RegisterMessageCallback("getDialogDefaults", NewCallback(this, &BugReportHandler::HandleGetDialogDefaults)); web_ui_->RegisterMessageCallback("refreshCurrentScreenshot", NewCallback(this, &BugReportHandler::HandleRefreshCurrentScreenshot)); #if defined(OS_CHROMEOS) web_ui_->RegisterMessageCallback("refreshSavedScreenshots", NewCallback(this, &BugReportHandler::HandleRefreshSavedScreenshots)); #endif web_ui_->RegisterMessageCallback("sendReport", NewCallback(this, &BugReportHandler::HandleSendReport)); web_ui_->RegisterMessageCallback("cancel", NewCallback(this, &BugReportHandler::HandleCancel)); web_ui_->RegisterMessageCallback("openSystemTab", NewCallback(this, &BugReportHandler::HandleOpenSystemTab)); } void BugReportHandler::HandleGetDialogDefaults(const ListValue*) { bug_report_ = new BugReportData(); // send back values which the dialog js needs initially ListValue dialog_defaults; // 0: current url if (target_tab_url_.length()) dialog_defaults.Append(new StringValue(target_tab_url_)); else dialog_defaults.Append(new StringValue("")); #if defined(OS_CHROMEOS) // 1: about:system dialog_defaults.Append(new StringValue(chrome::kChromeUISystemInfoURL)); // Trigger the request for system information here. chromeos::SyslogsLibrary* syslogs_lib = chromeos::CrosLibrary::Get()->GetSyslogsLibrary(); if (syslogs_lib) { syslogs_handle_ = syslogs_lib->RequestSyslogs( true, true, &syslogs_consumer_, NewCallback(bug_report_, &BugReportData::SyslogsComplete)); } // 2: user e-mail dialog_defaults.Append(new StringValue(GetUserEmail())); #endif web_ui_->CallJavascriptFunction("setupDialogDefaults", dialog_defaults); } void BugReportHandler::HandleRefreshCurrentScreenshot(const ListValue*) { std::string current_screenshot(kCurrentScreenshotUrl); StringValue screenshot(current_screenshot); web_ui_->CallJavascriptFunction("setupCurrentScreenshot", screenshot); } #if defined(OS_CHROMEOS) void BugReportHandler::HandleRefreshSavedScreenshots(const ListValue*) { std::vector<std::string> saved_screenshots; GetScreenshotUrls(&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); } #endif void BugReportHandler::HandleSendReport(const ListValue* list_value) { if (!bug_report_) { LOG(ERROR) << "Bug report hasn't been intialized yet."; return; } ListValue::const_iterator i = list_value->begin(); if (i == list_value->end()) { LOG(ERROR) << "Incorrect data passed to sendReport."; return; } // #0 - Problem type. int problem_type; std::string problem_type_str; (*i)->GetAsString(&problem_type_str); if (!base::StringToInt(problem_type_str, &problem_type)) { LOG(ERROR) << "Incorrect data passed to sendReport."; return; } if (++i == list_value->end()) { LOG(ERROR) << "Incorrect data passed to sendReport."; return; } // #1 - Page url. std::string page_url; (*i)->GetAsString(&page_url); if (++i == list_value->end()) { LOG(ERROR) << "Incorrect data passed to sendReport."; return; } // #2 - Description. std::string description; (*i)->GetAsString(&description); if (++i == list_value->end()) { LOG(ERROR) << "Incorrect data passed to sendReport."; return; } // #3 - Screenshot to send. std::string screenshot_path; (*i)->GetAsString(&screenshot_path); screenshot_path.erase(0, strlen(kScreenshotBaseUrl)); // Get the image to send in the report. std::vector<unsigned char> image; if (!screenshot_path.empty()) image = screenshot_source_->GetScreenshot(screenshot_path); #if defined(OS_CHROMEOS) if (++i == list_value->end()) { LOG(ERROR) << "Incorrect data passed to sendReport."; return; } // #4 - User e-mail std::string user_email; (*i)->GetAsString(&user_email); if (++i == list_value->end()) { LOG(ERROR) << "Incorrect data passed to sendReport."; return; } // #5 - System info checkbox. std::string sys_info_checkbox; (*i)->GetAsString(&sys_info_checkbox); bool send_sys_info = (sys_info_checkbox == "true"); // If we aren't sending the sys_info, cancel the gathering of the syslogs. if (!send_sys_info) CancelFeedbackCollection(); #endif // Update the data in bug_report_ so it can be sent bug_report_->UpdateData(web_ui_->GetProfile() , target_tab_url_ , problem_type , page_url , description , image #if defined(OS_CHROMEOS) , user_email , send_sys_info , false // sent_report #endif ); #if defined(OS_CHROMEOS) // If we don't require sys_info, or we have it, or we never requested it // (because libcros failed to load), then send the report now. // Otherwise, the report will get sent when we receive sys_info. if (!send_sys_info || bug_report_->sys_info() != NULL || syslogs_handle_ == 0) { bug_report_->SendReport(); } #else bug_report_->SendReport(); #endif // Lose the pointer to the BugReportData object; the object will delete itself // from SendReport, whether we called it, or will be called by the log // completion routine. bug_report_ = NULL; // Whether we sent the report, or if it will be sent by the Syslogs complete // function, close our feedback tab anyway, we have no more use for it. CloseFeedbackTab(); } void BugReportHandler::HandleCancel(const ListValue*) { CloseFeedbackTab(); } void BugReportHandler::HandleOpenSystemTab(const ListValue* args) { #if defined(OS_CHROMEOS) BrowserList::GetLastActive()->OpenSystemTabAndActivate(); #endif } void BugReportHandler::CancelFeedbackCollection() { #if defined(OS_CHROMEOS) if (syslogs_handle_ != 0) { chromeos::SyslogsLibrary* syslogs_lib = chromeos::CrosLibrary::Get()->GetSyslogsLibrary(); if (syslogs_lib) syslogs_lib->CancelRequest(syslogs_handle_); } #endif } void BugReportHandler::CloseFeedbackTab() { ClobberScreenshotsSource(); Browser* browser = BrowserList::GetLastActive(); if (browser) { browser->CloseTabContents(tab_); } else { LOG(FATAL) << "Failed to get last active browser."; } } //////////////////////////////////////////////////////////////////////////////// // // BugReportUI // //////////////////////////////////////////////////////////////////////////////// BugReportUI::BugReportUI(TabContents* tab) : HtmlDialogUI(tab) { BugReportHandler* handler = new BugReportHandler(tab); AddMessageHandler((handler)->Attach(this)); // The handler's init will specify which html // resource we'll display to the user BugReportUIHTMLSource* html_source = new BugReportUIHTMLSource(handler->Init()); // Set up the chrome://bugreport/ source. tab->profile()->GetChromeURLDataManager()->AddDataSource(html_source); }