summaryrefslogtreecommitdiffstats
path: root/ios
diff options
context:
space:
mode:
authordroger <droger@chromium.org>2015-04-03 02:40:46 -0700
committerCommit bot <commit-bot@chromium.org>2015-04-03 09:41:19 +0000
commitf1ba207ccc66eb006b3e51822879926a08629d56 (patch)
tree4e8c376f0327a1a4e1be52e13300eacdc100050b /ios
parenta014946a9df19ed6296c36e3142d9271d137de24 (diff)
downloadchromium_src-f1ba207ccc66eb006b3e51822879926a08629d56.zip
chromium_src-f1ba207ccc66eb006b3e51822879926a08629d56.tar.gz
chromium_src-f1ba207ccc66eb006b3e51822879926a08629d56.tar.bz2
[iOS] Upstream //ios/chrome/browser/net
Review URL: https://codereview.chromium.org/1052873004 Cr-Commit-Position: refs/heads/master@{#323718}
Diffstat (limited to 'ios')
-rw-r--r--ios/chrome/DEPS2
-rw-r--r--ios/chrome/browser/browsing_data_change_listening.h14
-rw-r--r--ios/chrome/browser/net/OWNERS1
-rw-r--r--ios/chrome/browser/net/chrome_cookie_store_ios_client.h34
-rw-r--r--ios/chrome/browser/net/chrome_cookie_store_ios_client.mm30
-rw-r--r--ios/chrome/browser/net/metrics_network_client.h19
-rw-r--r--ios/chrome/browser/net/metrics_network_client.mm95
-rw-r--r--ios/chrome/browser/net/metrics_network_client_manager.h39
-rw-r--r--ios/chrome/browser/net/metrics_network_client_manager.mm137
-rw-r--r--ios/chrome/browser/net/metrics_network_client_unittest.mm72
-rw-r--r--ios/chrome/browser/net/retryable_url_fetcher.h48
-rw-r--r--ios/chrome/browser/net/retryable_url_fetcher.mm92
-rw-r--r--ios/chrome/browser/net/retryable_url_fetcher_unittest.mm103
-rw-r--r--ios/chrome/ios_chrome.gyp10
-rw-r--r--ios/chrome/ios_chrome_tests.gyp2
15 files changed, 698 insertions, 0 deletions
diff --git a/ios/chrome/DEPS b/ios/chrome/DEPS
index f809f8d..f23eea2 100644
--- a/ios/chrome/DEPS
+++ b/ios/chrome/DEPS
@@ -6,6 +6,7 @@ include_rules = [
"+components/autofill/core/browser",
"+components/autofill/ios/browser",
+ "+components/data_reduction_proxy/core/common",
"+components/dom_distiller/core",
"+components/dom_distiller/ios",
"+components/infobars/core",
@@ -17,6 +18,7 @@ include_rules = [
"+components/translate/ios",
"+components/web_resource",
"+components/webp_transcode",
+ "+ios/net",
"+ios/public/provider/chrome",
"+ios/web/public",
"+net",
diff --git a/ios/chrome/browser/browsing_data_change_listening.h b/ios/chrome/browser/browsing_data_change_listening.h
new file mode 100644
index 0000000..2c0afe8
--- /dev/null
+++ b/ios/chrome/browser/browsing_data_change_listening.h
@@ -0,0 +1,14 @@
+// Copyright 2014 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 IOS_CHROME_BROWSER_BROWSING_DATA_CHANGE_LISTENING_H_
+#define IOS_CHROME_BROWSER_BROWSING_DATA_CHANGE_LISTENING_H_
+
+// Listener for changes in browsing data.
+@protocol BrowsingDataChangeListening
+// Called when cookie storage is changed. Can be called on a background thread.
+- (void)didChangeCookieStorage;
+@end
+
+#endif // IOS_CHROME_BROWSER_BROWSING_DATA_CHANGE_LISTENING_H_
diff --git a/ios/chrome/browser/net/OWNERS b/ios/chrome/browser/net/OWNERS
new file mode 100644
index 0000000..ed94051
--- /dev/null
+++ b/ios/chrome/browser/net/OWNERS
@@ -0,0 +1 @@
+droger@chromium.org
diff --git a/ios/chrome/browser/net/chrome_cookie_store_ios_client.h b/ios/chrome/browser/net/chrome_cookie_store_ios_client.h
new file mode 100644
index 0000000..54ed312
--- /dev/null
+++ b/ios/chrome/browser/net/chrome_cookie_store_ios_client.h
@@ -0,0 +1,34 @@
+// Copyright 2014 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 IOS_CHROME_BROWSER_NET_CHROME_COOKIE_STORE_IOS_CLIENT_H_
+#define IOS_CHROME_BROWSER_NET_CHROME_COOKIE_STORE_IOS_CLIENT_H_
+
+#include "base/macros.h"
+#include "base/threading/thread_checker.h"
+#include "ios/net/cookies/cookie_store_ios_client.h"
+
+@protocol BrowsingDataChangeListening;
+
+// Chrome implementation of net::CookieStoreIOSClient. This class lives on the
+// IOThread.
+class ChromeCookieStoreIOSClient : public net::CookieStoreIOSClient {
+ public:
+ // Creates a CookieStoreIOSClient with a BrowsingDataChangeListening.
+ // |browsing_data_change_listener| cannot be nil.
+ explicit ChromeCookieStoreIOSClient(
+ id<BrowsingDataChangeListening> browsing_data_change_listener);
+
+ // CookieStoreIOSClient implementation.
+ void DidChangeCookieStorage() const override;
+ scoped_refptr<base::SequencedTaskRunner> GetTaskRunner() const override;
+
+ private:
+ base::ThreadChecker thread_checker_;
+ // The listener that is informed of change in browsing data.
+ id<BrowsingDataChangeListening> browsing_data_change_listener_; // Weak.
+ DISALLOW_COPY_AND_ASSIGN(ChromeCookieStoreIOSClient);
+};
+
+#endif // IOS_CHROME_BROWSER_NET_CHROME_COOKIE_STORE_IOS_CLIENT_H_
diff --git a/ios/chrome/browser/net/chrome_cookie_store_ios_client.mm b/ios/chrome/browser/net/chrome_cookie_store_ios_client.mm
new file mode 100644
index 0000000..fb0b8f0
--- /dev/null
+++ b/ios/chrome/browser/net/chrome_cookie_store_ios_client.mm
@@ -0,0 +1,30 @@
+// Copyright 2014 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 "ios/chrome/browser/net/chrome_cookie_store_ios_client.h"
+
+#include "base/logging.h"
+#import "ios/chrome/browser/browsing_data_change_listening.h"
+#include "ios/web/public/web_thread.h"
+
+ChromeCookieStoreIOSClient::ChromeCookieStoreIOSClient(
+ id<BrowsingDataChangeListening> browsing_data_change_listener)
+ : browsing_data_change_listener_(browsing_data_change_listener) {
+ DCHECK(browsing_data_change_listener);
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
+}
+
+void ChromeCookieStoreIOSClient::DidChangeCookieStorage() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ [browsing_data_change_listener_ didChangeCookieStorage];
+}
+
+scoped_refptr<base::SequencedTaskRunner>
+ChromeCookieStoreIOSClient::GetTaskRunner() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ base::SequencedWorkerPool* pool = web::WebThread::GetBlockingPool();
+ return pool->GetSequencedTaskRunner(pool->GetSequenceToken()).get();
+}
diff --git a/ios/chrome/browser/net/metrics_network_client.h b/ios/chrome/browser/net/metrics_network_client.h
new file mode 100644
index 0000000..90b37fb
--- /dev/null
+++ b/ios/chrome/browser/net/metrics_network_client.h
@@ -0,0 +1,19 @@
+// 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 IOS_CHROME_BROWSER_NET_METRICS_NETWORK_CLIENT_H_
+#define IOS_CHROME_BROWSER_NET_METRICS_NETWORK_CLIENT_H_
+
+#import "ios/net/clients/crn_forwarding_network_client.h"
+
+@class MetricsNetworkClientManager;
+
+// MetricsNetworkClient records UMA metrics about the network requests.
+@interface MetricsNetworkClient : CRNForwardingNetworkClient
+
+- (instancetype)initWithManager:(MetricsNetworkClientManager*)manager;
+
+@end
+
+#endif // IOS_CHROME_BROWSER_NET_METRICS_NETWORK_CLIENT_H_
diff --git a/ios/chrome/browser/net/metrics_network_client.mm b/ios/chrome/browser/net/metrics_network_client.mm
new file mode 100644
index 0000000..45f041c
--- /dev/null
+++ b/ios/chrome/browser/net/metrics_network_client.mm
@@ -0,0 +1,95 @@
+// 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.
+
+#import "ios/chrome/browser/net/metrics_network_client.h"
+
+#import "base/ios/weak_nsobject.h"
+#include "base/logging.h"
+#include "base/metrics/sparse_histogram.h"
+#include "base/strings/sys_string_conversions.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
+#import "ios/chrome/browser/net/metrics_network_client_manager.h"
+#include "ios/web/public/url_util.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_response_headers.h"
+#include "net/url_request/url_request.h"
+
+@interface MetricsNetworkClient () {
+ BOOL _histogramUpdated;
+ // Pointer to the load time record for this request. This will be created
+ // and owned by |_manager|, and it will remain valid as long as |_manager| is.
+ PageLoadTimeRecord* _loadTimeRecord;
+ // Pointer to the creating manager, which is owned by a tab. All network
+ // requests for the tab are destroyed before the tab is, so this pointer
+ // will always be valid as long as the owning client is alive.
+ MetricsNetworkClientManager* _manager;
+ // A pointer to the request, kept so it can be referred to later.
+ scoped_refptr<net::HttpResponseHeaders> _nativeHeaders;
+}
+- (void)updateHistogram:(NSInteger)code;
+@end
+
+@implementation MetricsNetworkClient
+
+- (void)updateHistogram:(NSInteger)code {
+ DCHECK(!_histogramUpdated) << "Histogram should not be updated twice.";
+ // The |error| must be in the |net::kErrorDomain|. All those errors are
+ // defined in |net/base/net_error_list.h|, must be negative values that
+ // fits in an |int|. See |net/base/net_errors.h| for more information.
+ DCHECK_LE(code, 0) << "Net error codes should be negative.";
+ DCHECK_GT(code, INT_MIN) << "Net error code should fit in an int.";
+ // On iOS, we cannot distinguish between main frames, images and other
+ // subresources. Consequently, all the codes are aggregated in
+ // |ErrorCodesForMainFrame3|.
+ // The other histograms (such as |ErrorCodesForHTTPSGoogleMainFrame2|,
+ // |ErrorCodesForImages| and |ErrorCodesForSubresources2|) are not filled.
+ UMA_HISTOGRAM_SPARSE_SLOWLY("Net.ErrorCodesForMainFrame3",
+ static_cast<int>(-code));
+ _histogramUpdated = YES;
+}
+
+- (instancetype)initWithManager:(MetricsNetworkClientManager*)manager {
+ if ((self = [super init])) {
+ _manager = manager;
+ }
+ return self;
+}
+
+#pragma mark CRNNetworkClientProtocol methods
+
+- (void)didCreateNativeRequest:(net::URLRequest*)nativeRequest {
+ [super didCreateNativeRequest:nativeRequest];
+ GURL url = web::GURLByRemovingRefFromGURL(nativeRequest->original_url());
+ _loadTimeRecord =
+ [_manager recordForPageLoad:url time:nativeRequest->creation_time()];
+ _nativeHeaders = nativeRequest->response_headers();
+}
+
+- (void)didFailWithNSErrorCode:(NSInteger)nsErrorCode
+ netErrorCode:(int)netErrorCode {
+ [super didFailWithNSErrorCode:nsErrorCode netErrorCode:netErrorCode];
+
+ // Ignore NSURLErrorCancelled errors, which are sometimes created to
+ // silently abort loads.
+
+ if (nsErrorCode != NSURLErrorCancelled) {
+ [self updateHistogram:netErrorCode];
+ }
+}
+
+- (void)didFinishLoading {
+ [super didFinishLoading];
+ [self updateHistogram:net::OK];
+ if (!_nativeHeaders)
+ return;
+
+ if (!_loadTimeRecord)
+ return;
+
+ [_loadTimeRecord
+ setDataProxyUsed:data_reduction_proxy::HasDataReductionProxyViaHeader(
+ _nativeHeaders.get(), NULL)];
+}
+
+@end
diff --git a/ios/chrome/browser/net/metrics_network_client_manager.h b/ios/chrome/browser/net/metrics_network_client_manager.h
new file mode 100644
index 0000000..bc7d2a1
--- /dev/null
+++ b/ios/chrome/browser/net/metrics_network_client_manager.h
@@ -0,0 +1,39 @@
+// Copyright 2014 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 IOS_CHROME_BROWSER_NET_METRICS_NETWORK_CLIENT_MANAGER_H_
+#define IOS_CHROME_BROWSER_NET_METRICS_NETWORK_CLIENT_MANAGER_H_
+
+#import <Foundation/Foundation.h>
+
+#import "base/time/time.h"
+#import "ios/net/clients/crn_forwarding_network_client_factory.h"
+#include "url/gurl.h"
+
+@interface PageLoadTimeRecord : NSObject
+@property(nonatomic, assign) BOOL dataProxyUsed;
+@end
+
+// Factory that creates MetricsNetworkClient instances.
+// Each Tab that cares to report page load metrics should create an instance
+// of this class add add it to its request tracker.
+@interface MetricsNetworkClientManager : CRNForwardingNetworkClientFactory
+
+// Called by the tab when a new page load is about to start, to signal the
+// current page URL.
+- (void)pageLoadStarted:(GURL)url;
+
+// Called by the tab when the page load is complete.
+- (void)pageLoadCompleted;
+
+// Return a page load time record that will be used to record a load of |url|
+// that started at |time|. Returns nil if |url| doesn't match the current page
+// url. The page load record is owned by the target, and the caller should not
+// take ownership of it.
+// This method should only be called on the IO thread.
+- (PageLoadTimeRecord*)recordForPageLoad:(const GURL&)url
+ time:(base::TimeTicks)time;
+@end
+
+#endif // IOS_CHROME_BROWSER_NET_METRICS_NETWORK_CLIENT_MANAGER_H_
diff --git a/ios/chrome/browser/net/metrics_network_client_manager.mm b/ios/chrome/browser/net/metrics_network_client_manager.mm
new file mode 100644
index 0000000..be7afdf
--- /dev/null
+++ b/ios/chrome/browser/net/metrics_network_client_manager.mm
@@ -0,0 +1,137 @@
+// Copyright 2014 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.
+
+#import "ios/chrome/browser/net/metrics_network_client_manager.h"
+
+#import "base/ios/weak_nsobject.h"
+#import "base/location.h"
+#include "base/mac/bind_objc_block.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/metrics/histogram.h"
+#import "ios/chrome/browser/net/metrics_network_client.h"
+#include "ios/web/public/web_thread.h"
+#include "url/gurl.h"
+
+@interface PageLoadTimeRecord ()
+
+@property(nonatomic, readonly) GURL url;
+@property(nonatomic, readonly) base::TimeTicks creationTime;
+@property(nonatomic, assign) BOOL alreadyCounted;
+
+- (instancetype)initWithURL:(const GURL&)url time:(base::TimeTicks)time;
+
+@end
+
+@implementation PageLoadTimeRecord {
+ GURL _url;
+ base::TimeTicks _creationTime;
+ BOOL _alreadyCounted;
+ BOOL _dataProxyUsed;
+}
+
+@synthesize url = _url;
+@synthesize creationTime = _creationTime;
+@synthesize alreadyCounted = _alreadyCounted;
+@synthesize dataProxyUsed = _dataProxyUsed;
+
+- (instancetype)initWithURL:(const GURL&)url time:(base::TimeTicks)time {
+ if ((self = [super init])) {
+ _url = url;
+ _creationTime = time;
+ }
+ return self;
+}
+
+@end
+
+@interface MetricsNetworkClientManager ()
+
+// IO-thread-only methods.
+- (void)handlePageLoadStarted:(const GURL&)url;
+- (void)handlePageLoadCompleted;
+
+@end
+
+@implementation MetricsNetworkClientManager {
+ // Set of page load time objects created. Beyond deallocation and
+ // creation, should only be accessed on the IO thread.
+ base::scoped_nsobject<NSMutableSet> _pageLoadTimes;
+ // Current URL being loaded by the tab that owns this object. Only accessible
+ // on the IO thread.
+ GURL _pageURL;
+}
+
+- (instancetype)init {
+ if ((self = [super init])) {
+ _pageLoadTimes.reset([[NSMutableSet set] retain]);
+ }
+ return self;
+}
+
+- (Class)clientClass {
+ return [MetricsNetworkClient class];
+}
+
+#pragma mark CRWForwardingNetworkClientFactory methods
+
+- (CRNForwardingNetworkClient*)clientHandlingRequest:
+ (const net::URLRequest&)request {
+ return [[[MetricsNetworkClient alloc] initWithManager:self] autorelease];
+}
+
+#pragma mark - public UI-thread methods
+
+- (void)pageLoadStarted:(GURL)url {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
+ web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{
+ [self handlePageLoadStarted:url];
+ }));
+}
+
+- (void)pageLoadCompleted {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
+ web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{
+ [self handlePageLoadCompleted];
+ }));
+}
+
+#pragma mark - public IO-thread methods
+
+- (PageLoadTimeRecord*)recordForPageLoad:(const GURL&)url
+ time:(base::TimeTicks)time {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
+ base::scoped_nsobject<PageLoadTimeRecord> plt;
+ if (!_pageURL.spec().empty() && url == _pageURL) {
+ plt.reset([[PageLoadTimeRecord alloc] initWithURL:url time:time]);
+ [_pageLoadTimes addObject:plt];
+ }
+ return plt.get();
+}
+
+#pragma mark - IO-thread handlers for UI thread methods.
+
+- (void)handlePageLoadStarted:(const GURL&)url {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
+ [_pageLoadTimes removeAllObjects];
+ _pageURL = url;
+}
+
+- (void)handlePageLoadCompleted {
+ DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
+ for (PageLoadTimeRecord* plt in _pageLoadTimes.get()) {
+ if (plt.url == _pageURL && !plt.alreadyCounted) {
+ plt.alreadyCounted = YES;
+ base::TimeDelta elapsed = base::TimeTicks::Now() - plt.creationTime;
+ if (plt.dataProxyUsed) {
+ UMA_HISTOGRAM_MEDIUM_TIMES(
+ "Tabs.iOS_PostRedirectPLT_DataReductionProxy", elapsed);
+ } else {
+ UMA_HISTOGRAM_MEDIUM_TIMES("Tabs.iOS_PostRedirectPLT", elapsed);
+ }
+ break;
+ }
+ }
+}
+
+@end
diff --git a/ios/chrome/browser/net/metrics_network_client_unittest.mm b/ios/chrome/browser/net/metrics_network_client_unittest.mm
new file mode 100644
index 0000000..01c543a
--- /dev/null
+++ b/ios/chrome/browser/net/metrics_network_client_unittest.mm
@@ -0,0 +1,72 @@
+// Copyright 2014 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 "ios/chrome/browser/net/metrics_network_client.h"
+
+#include "base/mac/scoped_nsobject.h"
+#include "base/test/histogram_tester.h"
+#include "net/base/net_errors.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Dummy client to be registered as underlying client for the
+// MetricsNetworkClient.
+@interface MetricsMockClient : CRNForwardingNetworkClient
+@end
+
+@implementation MetricsMockClient
+
+- (void)didFailWithNSErrorCode:(NSInteger)nsErrorCode
+ netErrorCode:(int)netErrorCode {
+}
+
+- (void)didFinishLoading {
+}
+
+@end
+
+namespace {
+
+// Name for the histogram the MetricsNetworkClient has to update.
+const char kHistogramName[] = "Net.ErrorCodesForMainFrame3";
+
+class MetricsNetworkClientTest : public testing::Test {
+ public:
+ MetricsNetworkClientTest()
+ : histogram_tester_(), client_([[MetricsNetworkClient alloc] init]) {
+ // Setup a dummy underlying client to avoid DCHECKs.
+ base::scoped_nsobject<MetricsMockClient> underying_client(
+ [[MetricsMockClient alloc] init]);
+ [client_ setUnderlyingClient:underying_client];
+ }
+
+ // Returns true if there are no samples for "Net.ErrorCodesForMainFrame3".
+ void VerifyNoSamples() {
+ histogram_tester_.ExpectTotalCount(kHistogramName, 0);
+ }
+
+ protected:
+ base::HistogramTester histogram_tester_;
+ base::scoped_nsobject<MetricsNetworkClient> client_;
+};
+
+} // namespace
+
+TEST_F(MetricsNetworkClientTest, HistogramUpdatedOnErrors) {
+ int net_error = net::ERR_FAILED;
+ VerifyNoSamples();
+ // NSURLErrorCancelled errors must not update the histogram.
+ [client_ didFailWithNSErrorCode:NSURLErrorCancelled netErrorCode:net_error];
+ VerifyNoSamples();
+ // Other iOS errors update the histogram.
+ [client_ didFailWithNSErrorCode:NSURLErrorCannotConnectToHost
+ netErrorCode:net_error];
+ // |net_error| is negative, the histogram reports the opposite value.
+ histogram_tester_.ExpectUniqueSample(kHistogramName, -net_error, 1);
+}
+
+TEST_F(MetricsNetworkClientTest, HistogramUpdatedOnSuccess) {
+ VerifyNoSamples();
+ [client_ didFinishLoading];
+ histogram_tester_.ExpectUniqueSample(kHistogramName, -net::OK, 1);
+}
diff --git a/ios/chrome/browser/net/retryable_url_fetcher.h b/ios/chrome/browser/net/retryable_url_fetcher.h
new file mode 100644
index 0000000..cd153980
--- /dev/null
+++ b/ios/chrome/browser/net/retryable_url_fetcher.h
@@ -0,0 +1,48 @@
+// Copyright 2014 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 IOS_CHROME_BROWSER_NET_RETRYABLE_URL_FETCHER_H_
+#define IOS_CHROME_BROWSER_NET_RETRYABLE_URL_FETCHER_H_
+
+#import <Foundation/Foundation.h>
+
+#include "net/base/backoff_entry.h"
+
+namespace net {
+class URLRequestContextGetter;
+} // namespace net
+
+// Delegate protocol for RetryableURLFetcher object.
+@protocol RetryableURLFetcherDelegate<NSObject>
+
+// Returns the HTTP URL for RetryableURLFetcher to fetch.
+- (NSString*)urlToFetch;
+
+// Callback function after URL has been fetched. |response| is the content of
+// the HTTP response. |response| may be nil if the HTTP request failed.
+- (void)processSuccessResponse:(NSString*)response;
+
+@end
+
+@interface RetryableURLFetcher : NSObject
+
+// Designated initializer. |context| and |delegate| must not be nil. If |policy|
+// is not null, it specifies how often to retry the URL fetch on a call to
+// -startFetch. If |policy| is null, there is no retry.
+- (instancetype)
+ initWithRequestContextGetter:(net::URLRequestContextGetter*)context
+ delegate:(id<RetryableURLFetcherDelegate>)delegate
+ backoffPolicy:(const net::BackoffEntry::Policy*)policy;
+
+// Starts fetching URL. Uses the backoff policy specified when the object was
+// initialized.
+- (void)startFetch;
+
+// Returns the number of times that this URL Fetcher failed to receive a
+// success response. Returns 0 if this URL Fetcher was not set up to do retries.
+- (int)failureCount;
+
+@end
+
+#endif // IOS_CHROME_BROWSER_NET_RETRYABLE_URL_FETCHER_H_
diff --git a/ios/chrome/browser/net/retryable_url_fetcher.mm b/ios/chrome/browser/net/retryable_url_fetcher.mm
new file mode 100644
index 0000000..0e6bc60
--- /dev/null
+++ b/ios/chrome/browser/net/retryable_url_fetcher.mm
@@ -0,0 +1,92 @@
+// Copyright 2014 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.
+
+#import "ios/chrome/browser/net/retryable_url_fetcher.h"
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/sys_string_conversions.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "url/gurl.h"
+
+@interface RetryableURLFetcher ()
+- (void)urlFetchDidComplete:(const net::URLFetcher*)fetcher;
+@end
+
+class URLRequestDelegate : public net::URLFetcherDelegate {
+ public:
+ explicit URLRequestDelegate(RetryableURLFetcher* owner) : owner_(owner) {}
+ void OnURLFetchComplete(const net::URLFetcher* source) override {
+ [owner_ urlFetchDidComplete:source];
+ }
+
+ private:
+ RetryableURLFetcher* owner_; // Weak.
+};
+
+@implementation RetryableURLFetcher {
+ scoped_refptr<net::URLRequestContextGetter> requestContextGetter_;
+ scoped_ptr<URLRequestDelegate> fetcherDelegate_;
+ scoped_ptr<net::URLFetcher> fetcher_;
+ scoped_ptr<net::BackoffEntry> backoffEntry_;
+ int retryCount_;
+ id<RetryableURLFetcherDelegate> delegate_; // Weak.
+}
+
+- (instancetype)
+ initWithRequestContextGetter:(net::URLRequestContextGetter*)context
+ delegate:(id<RetryableURLFetcherDelegate>)delegate
+ backoffPolicy:(const net::BackoffEntry::Policy*)policy {
+ self = [super init];
+ if (self) {
+ DCHECK(context);
+ DCHECK(delegate);
+ requestContextGetter_ = context;
+ delegate_ = delegate;
+ if (policy)
+ backoffEntry_.reset(new net::BackoffEntry(policy));
+ }
+ return self;
+}
+
+- (void)startFetch {
+ DCHECK(requestContextGetter_.get());
+ GURL url(base::SysNSStringToUTF8([delegate_ urlToFetch]));
+ if (url.is_valid()) {
+ fetcherDelegate_.reset(new URLRequestDelegate(self));
+ fetcher_.reset(net::URLFetcher::Create(url, net::URLFetcher::GET,
+ fetcherDelegate_.get()));
+ fetcher_->SetRequestContext(requestContextGetter_.get());
+ fetcher_->Start();
+ }
+}
+
+- (int)failureCount {
+ return backoffEntry_ ? backoffEntry_->failure_count() : 0;
+}
+
+- (void)urlFetchDidComplete:(const net::URLFetcher*)fetcher {
+ BOOL success = fetcher->GetResponseCode() == net::HTTP_OK;
+ if (!success && backoffEntry_) {
+ backoffEntry_->InformOfRequest(false);
+ double nextRetry = backoffEntry_->GetTimeUntilRelease().InSecondsF();
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, nextRetry * NSEC_PER_SEC),
+ dispatch_get_main_queue(), ^{
+ [self startFetch];
+ });
+ return;
+ }
+ NSString* response = nil;
+ if (success) {
+ std::string responseString;
+ if (fetcher->GetResponseAsString(&responseString))
+ response = base::SysUTF8ToNSString(responseString);
+ }
+ [delegate_ processSuccessResponse:response];
+}
+
+@end
diff --git a/ios/chrome/browser/net/retryable_url_fetcher_unittest.mm b/ios/chrome/browser/net/retryable_url_fetcher_unittest.mm
new file mode 100644
index 0000000..6f73234
--- /dev/null
+++ b/ios/chrome/browser/net/retryable_url_fetcher_unittest.mm
@@ -0,0 +1,103 @@
+// Copyright 2014 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.
+
+#import "ios/chrome/browser/net/retryable_url_fetcher.h"
+
+#import "base/mac/scoped_nsobject.h"
+#include "ios/web/public/test/test_web_thread.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "net/url_request/url_request_test_util.h"
+#import "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+
+namespace {
+// An arbitrary text string for a fake response.
+NSString* const kFakeResponseString = @"Something interesting here.";
+}
+
+// Delegate object to provide data for RetryableURLFetcher and
+// handles the callback when URL is fetched.
+@interface TestRetryableURLFetcherDelegate
+ : NSObject<RetryableURLFetcherDelegate>
+// Counts the number of times that a successful response has been processed.
+@property(nonatomic, assign) NSUInteger responsesProcessed;
+@end
+
+@implementation TestRetryableURLFetcherDelegate
+@synthesize responsesProcessed;
+
+- (NSString*)urlToFetch {
+ return @"http://www.google.com";
+}
+
+- (void)processSuccessResponse:(NSString*)response {
+ if (response) {
+ EXPECT_NSEQ(kFakeResponseString, response);
+ ++responsesProcessed;
+ }
+}
+
+@end
+
+namespace {
+
+class RetryableURLFetcherTest : public PlatformTest {
+ protected:
+ void SetUp() override {
+ PlatformTest::SetUp();
+ test_delegate_.reset([[TestRetryableURLFetcherDelegate alloc] init]);
+ io_thread_.reset(
+ new web::TestWebThread(web::WebThread::IO, &message_loop_));
+ }
+
+ net::TestURLFetcherFactory factory_;
+ scoped_ptr<web::TestWebThread> io_thread_;
+ base::MessageLoop message_loop_;
+ base::scoped_nsobject<TestRetryableURLFetcherDelegate> test_delegate_;
+};
+
+TEST_F(RetryableURLFetcherTest, TestResponse200) {
+ scoped_refptr<net::URLRequestContextGetter> request_context_getter =
+ new net::TestURLRequestContextGetter(message_loop_.message_loop_proxy());
+ base::scoped_nsobject<RetryableURLFetcher> retryableFetcher(
+ [[RetryableURLFetcher alloc]
+ initWithRequestContextGetter:request_context_getter.get()
+ delegate:test_delegate_.get()
+ backoffPolicy:nil]);
+ [retryableFetcher startFetch];
+
+ // Manually calls the delegate.
+ net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+ DCHECK(fetcher);
+ DCHECK(fetcher->delegate());
+ [test_delegate_ setResponsesProcessed:0U];
+ fetcher->set_response_code(200);
+ fetcher->SetResponseString([kFakeResponseString UTF8String]);
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+ EXPECT_EQ(1U, [test_delegate_ responsesProcessed]);
+}
+
+TEST_F(RetryableURLFetcherTest, TestResponse404) {
+ scoped_refptr<net::URLRequestContextGetter> request_context_getter =
+ new net::TestURLRequestContextGetter(message_loop_.message_loop_proxy());
+ base::scoped_nsobject<RetryableURLFetcher> retryableFetcher(
+ [[RetryableURLFetcher alloc]
+ initWithRequestContextGetter:request_context_getter.get()
+ delegate:test_delegate_.get()
+ backoffPolicy:nil]);
+ [retryableFetcher startFetch];
+
+ // Manually calls the delegate.
+ net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+ DCHECK(fetcher);
+ DCHECK(fetcher->delegate());
+ [test_delegate_ setResponsesProcessed:0U];
+ fetcher->set_response_code(404);
+ fetcher->SetResponseString("");
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+ EXPECT_EQ(0U, [test_delegate_ responsesProcessed]);
+}
+
+} // namespace
diff --git a/ios/chrome/ios_chrome.gyp b/ios/chrome/ios_chrome.gyp
index f2e8122..6a221ec 100644
--- a/ios/chrome/ios_chrome.gyp
+++ b/ios/chrome/ios_chrome.gyp
@@ -17,6 +17,7 @@
'../../base/base.gyp:base',
'../../components/components.gyp:autofill_core_browser',
'../../components/components.gyp:autofill_ios_browser',
+ '../../components/components.gyp:data_reduction_proxy_core_common',
'../../components/components.gyp:dom_distiller_core',
'../../components/components.gyp:dom_distiller_ios',
'../../components/components.gyp:infobars_core',
@@ -73,6 +74,7 @@
'browser/autofill/form_suggestion_view_client.h',
'browser/browser_state/browser_state_otr_helper.cc',
'browser/browser_state/browser_state_otr_helper.h',
+ 'browser/browsing_data_change_listening.h',
'browser/chrome_switches.cc',
'browser/chrome_switches.h',
'browser/chrome_url_constants.cc',
@@ -103,8 +105,16 @@
'browser/infobars/infobar_manager_impl.h',
'browser/infobars/infobar_utils.h',
'browser/infobars/infobar_utils.mm',
+ 'browser/net/chrome_cookie_store_ios_client.h',
+ 'browser/net/chrome_cookie_store_ios_client.mm',
'browser/net/image_fetcher.h',
'browser/net/image_fetcher.mm',
+ 'browser/net/metrics_network_client.h',
+ 'browser/net/metrics_network_client.mm',
+ 'browser/net/metrics_network_client_manager.h',
+ 'browser/net/metrics_network_client_manager.mm',
+ 'browser/net/retryable_url_fetcher.h',
+ 'browser/net/retryable_url_fetcher.mm',
'browser/passwords/password_generation_utils.h',
'browser/passwords/password_generation_utils.mm',
'browser/pref_names.cc',
diff --git a/ios/chrome/ios_chrome_tests.gyp b/ios/chrome/ios_chrome_tests.gyp
index 2029cf6..7cea1a3 100644
--- a/ios/chrome/ios_chrome_tests.gyp
+++ b/ios/chrome/ios_chrome_tests.gyp
@@ -24,6 +24,8 @@
],
'sources': [
'browser/net/image_fetcher_unittest.mm',
+ 'browser/net/metrics_network_client_unittest.mm',
+ 'browser/net/retryable_url_fetcher_unittest.mm',
'browser/snapshots/snapshot_cache_unittest.mm',
'browser/snapshots/snapshots_util_unittest.mm',
'browser/translate/translate_service_ios_unittest.cc',