Commit 4837225e authored by rkc@chromium.org's avatar rkc@chromium.org

Add feedback uploader to upload reports in a sequenced manner with retries.

This is the first part of the CL that will enable re-sending feedback reports that were sent when the user was offline, across sessions.

R=jcivelli@chromium.org, zork@chromium.org
BUG=249853

Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=241962

Review URL: https://codereview.chromium.org/116863002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@242069 0039d316-1c4b-4281-b951-d872f2087c98
parent 43aa7800
// Copyright 2013 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/feedback/feedback_uploader.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/task_runner_util.h"
#include "base/threading/sequenced_worker_pool.h"
#include "chrome/common/chrome_switches.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "net/base/load_flags.h"
#include "net/url_request/url_fetcher.h"
#include "url/gurl.h"
using content::BrowserThread;
namespace feedback {
namespace {
const char kFeedbackPostUrl[] =
"https://www.google.com/tools/feedback/chrome/__submit";
const char kProtBufMimeType[] = "application/x-protobuf";
const int64 kRetryDelayMinutes = 60;
} // namespace
struct FeedbackReport {
FeedbackReport(const base::Time& upload_at, scoped_ptr<std::string> data)
: upload_at(upload_at), data(data.Pass()) {}
FeedbackReport(const FeedbackReport& report) {
upload_at = report.upload_at;
data = report.data.Pass();
}
FeedbackReport& operator=(const FeedbackReport& report) {
upload_at = report.upload_at;
data = report.data.Pass();
return *this;
}
base::Time upload_at; // Upload this report at or after this time.
mutable scoped_ptr<std::string> data;
};
bool FeedbackUploader::ReportsUploadTimeComparator::operator()(
const FeedbackReport& a, const FeedbackReport& b) const {
return a.upload_at > b.upload_at;
}
FeedbackUploader::FeedbackUploader(content::BrowserContext* context)
: context_(context),
retry_delay_(base::TimeDelta::FromMinutes(kRetryDelayMinutes)) {
CHECK(context_);
dispatch_callback_ = base::Bind(&FeedbackUploader::DispatchReport,
AsWeakPtr());
}
FeedbackUploader::~FeedbackUploader() {
}
void FeedbackUploader::QueueReport(scoped_ptr<std::string> data) {
reports_queue_.push(FeedbackReport(base::Time::Now(), data.Pass()));
UpdateUploadTimer();
}
void FeedbackUploader::DispatchReport(scoped_ptr<std::string> data) {
GURL post_url;
if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kFeedbackServer))
post_url = GURL(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kFeedbackServer));
else
post_url = GURL(kFeedbackPostUrl);
// Save the report data pointer since the report.Pass() in the next statement
// will invalidate the scoper.
std::string* data_ptr = data.get();
net::URLFetcher* fetcher = net::URLFetcher::Create(
post_url, net::URLFetcher::POST,
new FeedbackUploaderDelegate(
data.Pass(),
base::Bind(&FeedbackUploader::UpdateUploadTimer, AsWeakPtr()),
base::Bind(&FeedbackUploader::RetryReport, AsWeakPtr())));
fetcher->SetUploadData(std::string(kProtBufMimeType), *data_ptr);
fetcher->SetRequestContext(context_->GetRequestContext());
fetcher->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES |
net::LOAD_DO_NOT_SEND_COOKIES);
fetcher->Start();
}
void FeedbackUploader::UpdateUploadTimer() {
if (reports_queue_.empty())
return;
const FeedbackReport& report = reports_queue_.top();
base::Time now = base::Time::Now();
if (report.upload_at <= now) {
scoped_ptr<std::string> data = report.data.Pass();
reports_queue_.pop();
dispatch_callback_.Run(data.Pass());
} else {
// Stop the old timer and start an updated one.
if (upload_timer_.IsRunning())
upload_timer_.Stop();
upload_timer_.Start(
FROM_HERE, report.upload_at - now, this,
&FeedbackUploader::UpdateUploadTimer);
}
}
void FeedbackUploader::RetryReport(scoped_ptr<std::string> data) {
reports_queue_.push(
FeedbackReport(base::Time::Now() + retry_delay_, data.Pass()));
UpdateUploadTimer();
}
void FeedbackUploader::setup_for_test(
const ReportDataCallback& dispatch_callback,
const base::TimeDelta& retry_delay) {
dispatch_callback_ = dispatch_callback;
retry_delay_ = retry_delay;
}
} // namespace feedback
// Copyright 2013 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.
#ifndef CHROME_BROWSER_FEEDBACK_FEEDBACK_UPLOADER_H_
#define CHROME_BROWSER_FEEDBACK_FEEDBACK_UPLOADER_H_
#include <queue>
#include <string>
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chrome/browser/feedback/feedback_uploader_delegate.h"
#include "components/browser_context_keyed_service/browser_context_keyed_service.h"
namespace content {
class BrowserContext;
}
namespace feedback {
struct FeedbackReport;
// FeedbackUploader is used to add a feedback report to the queue of reports
// being uploaded. In case uploading a report fails, it is written to disk and
// tried again when it's turn comes up next in the queue.
class FeedbackUploader : public BrowserContextKeyedService,
public base::SupportsWeakPtr<FeedbackUploader> {
public:
explicit FeedbackUploader(content::BrowserContext* context);
virtual ~FeedbackUploader();
void QueueReport(scoped_ptr<std::string> data);
private:
friend class FeedbackUploaderTest;
struct ReportsUploadTimeComparator {
bool operator()(const FeedbackReport& a, const FeedbackReport& b) const;
};
// Dispatches the report to be uploaded.
void DispatchReport(scoped_ptr<std::string> data);
// Update our timer for uploading the next report.
void UpdateUploadTimer();
// Requeue this report with a delay.
void RetryReport(scoped_ptr<std::string> data);
void setup_for_test(const ReportDataCallback& dispatch_callback,
const base::TimeDelta& retry_delay);
// Browser context this uploader was created for.
content::BrowserContext* context_;
// Timer to upload the next report at.
base::OneShotTimer<FeedbackUploader> upload_timer_;
// Priority queue of reports prioritized by the time the report is supposed
// to be uploaded at.
std::priority_queue<FeedbackReport,
std::vector<FeedbackReport>,
ReportsUploadTimeComparator> reports_queue_;
std::vector<FeedbackReport> loaded_reports_;
ReportDataCallback dispatch_callback_;
base::TimeDelta retry_delay_;
DISALLOW_COPY_AND_ASSIGN(FeedbackUploader);
};
} // namespace feedback
#endif // CHROME_BROWSER_FEEDBACK_FEEDBACK_UPLOADER_H_
// Copyright 2013 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/feedback/feedback_uploader_delegate.h"
#include <sstream>
#include "base/logging.h"
#include "net/url_request/url_fetcher.h"
#include "url/gurl.h"
namespace feedback {
namespace {
const int kHttpPostSuccessNoContent = 204;
const int kHttpPostFailNoConnection = -1;
const int kHttpPostFailClientError = 400;
const int kHttpPostFailServerError = 500;
} // namespace
FeedbackUploaderDelegate::FeedbackUploaderDelegate(
scoped_ptr<std::string> post_body,
const base::Closure& success_callback,
const ReportDataCallback& error_callback)
: post_body_(post_body.Pass()),
success_callback_(success_callback),
error_callback_(error_callback) {
}
FeedbackUploaderDelegate::~FeedbackUploaderDelegate() {}
void FeedbackUploaderDelegate::OnURLFetchComplete(
const net::URLFetcher* source) {
scoped_ptr<const net::URLFetcher> source_scoper(source);
std::stringstream error_stream;
int response_code = source->GetResponseCode();
if (response_code == kHttpPostSuccessNoContent) {
error_stream << "Success";
success_callback_.Run();
} else {
// Process the error for debug output
if (response_code == kHttpPostFailNoConnection) {
error_stream << "No connection to server.";
} else if ((response_code > kHttpPostFailClientError) &&
(response_code < kHttpPostFailServerError)) {
error_stream << "Client error: HTTP response code " << response_code;
} else if (response_code > kHttpPostFailServerError) {
error_stream << "Server error: HTTP response code " << response_code;
} else {
error_stream << "Unknown error: HTTP response code " << response_code;
}
error_callback_.Run(post_body_.Pass());
}
LOG(WARNING) << "FEEDBACK: Submission to feedback server ("
<< source->GetURL() << ") status: " << error_stream.str();
// This instance won't be used for anything else, delete us.
delete this;
}
} // namespace feedback
// Copyright 2013 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.
#ifndef CHROME_BROWSER_FEEDBACK_FEEDBACK_UPLOADER_DELEGATE_H_
#define CHROME_BROWSER_FEEDBACK_FEEDBACK_UPLOADER_DELEGATE_H_
#include <string>
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/memory/scoped_ptr.h"
#include "net/url_request/url_fetcher_delegate.h"
namespace feedback {
typedef base::Callback<void(scoped_ptr<std::string>)> ReportDataCallback;
// FeedbackUploaderDelegate is a simple http uploader for a feedback report. On
// succes or failure, it deletes itself, but on failure it also notifies the
// error callback specified when constructing the class instance.
class FeedbackUploaderDelegate : public net::URLFetcherDelegate {
public:
FeedbackUploaderDelegate(scoped_ptr<std::string> post_body,
const base::Closure& success_callback,
const ReportDataCallback& error_callback);
virtual ~FeedbackUploaderDelegate();
private:
// Overridden from net::URLFetcherDelegate.
virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
scoped_ptr<std::string> post_body_;
base::Closure success_callback_;
ReportDataCallback error_callback_;
DISALLOW_COPY_AND_ASSIGN(FeedbackUploaderDelegate);
};
} // namespace feedback
#endif // CHROME_BROWSER_FEEDBACK_FEEDBACK_UPLOADER_DELEGATE_H_
// Copyright 2013 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/feedback/feedback_uploader_factory.h"
#include "base/memory/singleton.h"
#include "chrome/browser/feedback/feedback_uploader.h"
#include "components/browser_context_keyed_service/browser_context_dependency_manager.h"
namespace feedback {
// static
FeedbackUploaderFactory* FeedbackUploaderFactory::GetInstance() {
return Singleton<FeedbackUploaderFactory>::get();
}
// static
FeedbackUploader* FeedbackUploaderFactory::GetForBrowserContext(
content::BrowserContext* context) {
return static_cast<FeedbackUploader*>(
GetInstance()->GetServiceForBrowserContext(context, true));
}
FeedbackUploaderFactory::FeedbackUploaderFactory()
: BrowserContextKeyedServiceFactory(
"feedback::FeedbackUploader",
BrowserContextDependencyManager::GetInstance()) {}
FeedbackUploaderFactory::~FeedbackUploaderFactory() {}
BrowserContextKeyedService* FeedbackUploaderFactory::BuildServiceInstanceFor(
content::BrowserContext* context) const {
return new FeedbackUploader(context);
}
} // namespace feedback
// Copyright 2013 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.
#ifndef CHROME_BROWSER_FEEDBACK_FEEDBACK_UPLOADER_FACTORY_H_
#define CHROME_BROWSER_FEEDBACK_FEEDBACK_UPLOADER_FACTORY_H_
#include "components/browser_context_keyed_service/browser_context_keyed_service_factory.h"
template<typename T> struct DefaultSingletonTraits;
namespace content {
class BrowserContext;
}
namespace feedback {
class FeedbackUploader;
// Singleton that owns the FeedbackUploaders and associates them with profiles;
class FeedbackUploaderFactory : public BrowserContextKeyedServiceFactory {
public:
// Returns singleton instance of FeedbackUploaderFactory.
static FeedbackUploaderFactory* GetInstance();
// Returns the Feedback Uploader associated with |context|.
static FeedbackUploader* GetForBrowserContext(
content::BrowserContext* context);
private:
friend struct DefaultSingletonTraits<FeedbackUploaderFactory>;
FeedbackUploaderFactory();
virtual ~FeedbackUploaderFactory();
// BrowserContextKeyedServiceFactory overrides:
virtual BrowserContextKeyedService* BuildServiceInstanceFor(
content::BrowserContext* context) const OVERRIDE;
DISALLOW_COPY_AND_ASSIGN(FeedbackUploaderFactory);
};
} // namespace feedback
#endif // CHROME_BROWSER_FEEDBACK_FEEDBACK_UPLOADER_FACTORY_H_
// Copyright 2013 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/feedback/feedback_uploader.h"
#include "base/bind.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "chrome/browser/feedback/feedback_uploader_factory.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/test/test_browser_thread.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
const char kReportOne[] = "one";
const char kReportTwo[] = "two";
const char kReportThree[] = "three";
const char kReportFour[] = "four";
const char kReportFive[] = "five";
const base::TimeDelta kRetryDelayForTest =
base::TimeDelta::FromMilliseconds(100);
BrowserContextKeyedService* CreateFeedbackUploaderService(
content::BrowserContext* context) {
return new feedback::FeedbackUploader(Profile::FromBrowserContext(context));
}
} // namespace
namespace feedback {
class FeedbackUploaderTest : public testing::Test {
protected:
FeedbackUploaderTest()
: ui_thread_(content::BrowserThread::UI, &message_loop_),
profile_(new TestingProfile()),
expected_reports_(0) {
FeedbackUploaderFactory::GetInstance()->SetTestingFactory(
profile_.get(), &CreateFeedbackUploaderService);
uploader_ = FeedbackUploaderFactory::GetForBrowserContext(profile_.get());
uploader_->setup_for_test(
base::Bind(&FeedbackUploaderTest::MockDispatchReport,
base::Unretained(this)),
kRetryDelayForTest);
}
virtual ~FeedbackUploaderTest() {
FeedbackUploaderFactory::GetInstance()->SetTestingFactory(
profile_.get(), NULL);
}
void QueueReport(const std::string& data) {
uploader_->QueueReport(make_scoped_ptr(new std::string(data)));
}
void ReportFailure(const std::string& data) {
uploader_->RetryReport(make_scoped_ptr(new std::string(data)));
}
void MockDispatchReport(scoped_ptr<std::string> report_data) {
dispatched_reports_.push_back(*report_data.get());
// Dispatch will always update the timer, whether successful or not,
// simulate the same behavior.
uploader_->UpdateUploadTimer();
if (dispatched_reports_.size() >= expected_reports_) {
if (run_loop_.get())
run_loop_->Quit();
}
}
void RunMessageLoop() {
run_loop_.reset(new base::RunLoop());
run_loop_->Run();
}
base::MessageLoop message_loop_;
scoped_ptr<base::RunLoop> run_loop_;
content::TestBrowserThread ui_thread_;
scoped_ptr<TestingProfile> profile_;
FeedbackUploader* uploader_;
std::vector<std::string> dispatched_reports_;
size_t expected_reports_;
};
TEST_F(FeedbackUploaderTest, QueueMultiple) {
dispatched_reports_.clear();
QueueReport(kReportOne);
QueueReport(kReportTwo);
QueueReport(kReportThree);
QueueReport(kReportFour);
EXPECT_EQ(dispatched_reports_.size(), 4u);
EXPECT_EQ(dispatched_reports_[0], kReportOne);
EXPECT_EQ(dispatched_reports_[1], kReportTwo);
EXPECT_EQ(dispatched_reports_[2], kReportThree);
EXPECT_EQ(dispatched_reports_[3], kReportFour);
}
TEST_F(FeedbackUploaderTest, QueueMultipleWithFailures) {
dispatched_reports_.clear();
QueueReport(kReportOne);
QueueReport(kReportTwo);
QueueReport(kReportThree);
QueueReport(kReportFour);
ReportFailure(kReportThree);
ReportFailure(kReportTwo);
QueueReport(kReportFive);
expected_reports_ = 7;
RunMessageLoop();
EXPECT_EQ(dispatched_reports_.size(), 7u);
EXPECT_EQ(dispatched_reports_[0], kReportOne);
EXPECT_EQ(dispatched_reports_[1], kReportTwo);
EXPECT_EQ(dispatched_reports_[2], kReportThree);
EXPECT_EQ(dispatched_reports_[3], kReportFour);
EXPECT_EQ(dispatched_reports_[4], kReportFive);
EXPECT_EQ(dispatched_reports_[5], kReportThree);
EXPECT_EQ(dispatched_reports_[6], kReportTwo);
}
} // namespace feedback
...@@ -21,6 +21,8 @@ ...@@ -21,6 +21,8 @@
#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process.h"
#include "chrome/browser/extensions/api/feedback_private/feedback_private_api.h" #include "chrome/browser/extensions/api/feedback_private/feedback_private_api.h"
#include "chrome/browser/feedback/feedback_data.h" #include "chrome/browser/feedback/feedback_data.h"
#include "chrome/browser/feedback/feedback_uploader.h"
#include "chrome/browser/feedback/feedback_uploader_factory.h"
#include "chrome/browser/metrics/variations/variations_http_header_provider.h" #include "chrome/browser/metrics/variations/variations_http_header_provider.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profile_manager.h"
...@@ -51,8 +53,6 @@ ...@@ -51,8 +53,6 @@
namespace { namespace {
void DispatchFeedback(Profile* profile, std::string* post_body, int64 delay);
GURL GetTargetTabUrl(int session_id, int index) { GURL GetTargetTabUrl(int session_id, int index) {
Browser* browser = chrome::FindBrowserWithID(session_id); Browser* browser = chrome::FindBrowserWithID(session_id);
// Sanity checks. // Sanity checks.
...@@ -69,22 +69,7 @@ GURL GetTargetTabUrl(int session_id, int index) { ...@@ -69,22 +69,7 @@ GURL GetTargetTabUrl(int session_id, int index) {
return GURL(); return GURL();
} }
// URL to post bug reports to.
const char kFeedbackPostUrl[] =
"https://www.google.com/tools/feedback/chrome/__submit";
const char kProtBufMimeType[] = "application/x-protobuf";
const char kPngMimeType[] = "image/png"; const char kPngMimeType[] = "image/png";
const int kHttpPostSuccessNoContent = 204;
const int kHttpPostFailNoConnection = -1;
const int kHttpPostFailClientError = 400;
const int kHttpPostFailServerError = 500;
const int64 kInitialRetryDelay = 900000; // 15 minutes
const int64 kRetryDelayIncreaseFactor = 2;
const int64 kRetryDelayLimit = 14400000; // 4 hours
const char kArbitraryMimeType[] = "application/octet-stream"; const char kArbitraryMimeType[] = "application/octet-stream";
const char kHistogramsAttachmentName[] = "histograms.zip"; const char kHistogramsAttachmentName[] = "histograms.zip";
const char kLogsAttachmentName[] = "system_logs.zip"; const char kLogsAttachmentName[] = "system_logs.zip";
...@@ -95,111 +80,6 @@ const int kChromeOSProductId = 208; ...@@ -95,111 +80,6 @@ const int kChromeOSProductId = 208;
const int kChromeBrowserProductId = 237; const int kChromeBrowserProductId = 237;
#endif #endif
// Simple net::URLFetcherDelegate to clean up URLFetcher on completion.
class PostCleanup : public net::URLFetcherDelegate {
public:
PostCleanup(Profile* profile,
std::string* post_body,
int64 previous_delay) : profile_(profile),
post_body_(post_body),
previous_delay_(previous_delay) { }
// Overridden from net::URLFetcherDelegate.
virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
protected:
virtual ~PostCleanup() {}
private:
Profile* profile_;
std::string* post_body_;
int64 previous_delay_;
DISALLOW_COPY_AND_ASSIGN(PostCleanup);
};
// Don't use the data parameter, instead use the pointer we pass into every
// post cleanup object - that pointer will be deleted and deleted only on a
// successful post to the feedback server.
void PostCleanup::OnURLFetchComplete(
const net::URLFetcher* source) {
std::stringstream error_stream;
int response_code = source->GetResponseCode();
if (response_code == kHttpPostSuccessNoContent) {
// We've sent our report, delete the report data
delete post_body_;
error_stream << "Success";
} else {
// Uh oh, feedback failed, send it off to retry
if (previous_delay_) {
if (previous_delay_ < kRetryDelayLimit)
previous_delay_ *= kRetryDelayIncreaseFactor;
} else {
previous_delay_ = kInitialRetryDelay;
}
DispatchFeedback(profile_, post_body_, previous_delay_);
// Process the error for debug output
if (response_code == kHttpPostFailNoConnection) {
error_stream << "No connection to server.";
} else if ((response_code > kHttpPostFailClientError) &&
(response_code < kHttpPostFailServerError)) {
error_stream << "Client error: HTTP response code " << response_code;
} else if (response_code > kHttpPostFailServerError) {
error_stream << "Server error: HTTP response code " << response_code;
} else {
error_stream << "Unknown error: HTTP response code " << response_code;
}
}
LOG(WARNING) << "FEEDBACK: Submission to feedback server (" <<
source->GetURL() << ") status: " << error_stream.str();
// Delete the URLFetcher.
delete source;
// And then delete ourselves.
delete this;
}
void SendFeedback(Profile* profile,
std::string* post_body,
int64 previous_delay) {
DCHECK(post_body);
GURL post_url;
if (CommandLine::ForCurrentProcess()->
HasSwitch(switches::kFeedbackServer))
post_url = GURL(CommandLine::ForCurrentProcess()->
GetSwitchValueASCII(switches::kFeedbackServer));
else
post_url = GURL(kFeedbackPostUrl);
net::URLFetcher* fetcher = net::URLFetcher::Create(
post_url, net::URLFetcher::POST,
new PostCleanup(profile, post_body, previous_delay));
fetcher->SetRequestContext(profile->GetRequestContext());
fetcher->SetLoadFlags(
net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES);
net::HttpRequestHeaders headers;
chrome_variations::VariationsHttpHeaderProvider::GetInstance()->AppendHeaders(
fetcher->GetOriginalURL(), profile->IsOffTheRecord(), false, &headers);
fetcher->SetExtraRequestHeaders(headers.ToString());
fetcher->SetUploadData(std::string(kProtBufMimeType), *post_body);
fetcher->Start();
}
void DispatchFeedback(Profile* profile, std::string* post_body, int64 delay) {
DCHECK(post_body);
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&SendFeedback, profile, post_body, delay),
base::TimeDelta::FromMilliseconds(delay));
}
void AddFeedbackData(userfeedback::ExtensionSubmit* feedback_data, void AddFeedbackData(userfeedback::ExtensionSubmit* feedback_data,
const std::string& key, const std::string& value) { const std::string& key, const std::string& value) {
// Don't bother with empty keys or values // Don't bother with empty keys or values
...@@ -359,10 +239,12 @@ void SendReport(scoped_refptr<FeedbackData> data) { ...@@ -359,10 +239,12 @@ void SendReport(scoped_refptr<FeedbackData> data) {
// This pointer will eventually get deleted by the PostCleanup class, after // This pointer will eventually get deleted by the PostCleanup class, after
// we've either managed to successfully upload the report or died trying. // we've either managed to successfully upload the report or died trying.
std::string* post_body = new std::string; scoped_ptr<std::string> post_body(new std::string);
feedback_data.SerializeToString(post_body); feedback_data.SerializeToString(post_body.get());
DispatchFeedback(data->profile(), post_body, 0); feedback::FeedbackUploader *uploader =
feedback::FeedbackUploaderFactory::GetForBrowserContext(data->profile());
uploader->QueueReport(post_body.Pass());
} }
bool ZipString(const base::FilePath& filename, bool ZipString(const base::FilePath& filename,
......
...@@ -350,6 +350,12 @@ ...@@ -350,6 +350,12 @@
'browser/feedback/feedback_data.h', 'browser/feedback/feedback_data.h',
'browser/feedback/feedback_util.cc', 'browser/feedback/feedback_util.cc',
'browser/feedback/feedback_util.h', 'browser/feedback/feedback_util.h',
'browser/feedback/feedback_uploader.cc',
'browser/feedback/feedback_uploader.h',
'browser/feedback/feedback_uploader_delegate.cc',
'browser/feedback/feedback_uploader_delegate.h',
'browser/feedback/feedback_uploader_factory.cc',
'browser/feedback/feedback_uploader_factory.h',
'browser/feedback/system_logs/about_system_logs_fetcher.cc', 'browser/feedback/system_logs/about_system_logs_fetcher.cc',
'browser/feedback/system_logs/about_system_logs_fetcher.h', 'browser/feedback/system_logs/about_system_logs_fetcher.h',
'browser/feedback/system_logs/scrubbed_system_logs_fetcher.cc', 'browser/feedback/system_logs/scrubbed_system_logs_fetcher.cc',
......
...@@ -941,6 +941,7 @@ ...@@ -941,6 +941,7 @@
'browser/extensions/webstore_installer_unittest.cc', 'browser/extensions/webstore_installer_unittest.cc',
'browser/external_protocol/external_protocol_handler_unittest.cc', 'browser/external_protocol/external_protocol_handler_unittest.cc',
'browser/favicon/favicon_handler_unittest.cc', 'browser/favicon/favicon_handler_unittest.cc',
'browser/feedback/feedback_uploader_unittest.cc',
'browser/file_select_helper_unittest.cc', 'browser/file_select_helper_unittest.cc',
'browser/first_run/first_run_unittest.cc', 'browser/first_run/first_run_unittest.cc',
'browser/geolocation/chrome_geolocation_permission_context_unittest.cc', 'browser/geolocation/chrome_geolocation_permission_context_unittest.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment