summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorstuartmorgan <stuartmorgan@chromium.org>2015-03-09 12:19:15 -0700
committerCommit bot <commit-bot@chromium.org>2015-03-09 19:20:11 +0000
commitfc8c88d3db12845eb0b6c31f754f8664ba6466fe (patch)
treebdd66ee1c928032cd196c194f8f7d8730aab0572
parent32af42a8103c97258a0eca1ec7255e7a849b82aa (diff)
downloadchromium_src-fc8c88d3db12845eb0b6c31f754f8664ba6466fe.zip
chromium_src-fc8c88d3db12845eb0b6c31f754f8664ba6466fe.tar.gz
chromium_src-fc8c88d3db12845eb0b6c31f754f8664ba6466fe.tar.bz2
Upstream various ios/web utilities and helpers
Upstreams miscellaneous helper classes and utility files that don't have other dependencies, and can thus be built and tested as-is. BUG=464810 Review URL: https://codereview.chromium.org/988383002 Cr-Commit-Position: refs/heads/master@{#319706}
-rw-r--r--ios/web/crw_network_activity_indicator_manager.h67
-rw-r--r--ios/web/crw_network_activity_indicator_manager.mm115
-rw-r--r--ios/web/crw_network_activity_indicator_manager_unittest.mm154
-rw-r--r--ios/web/history_state_util.h33
-rw-r--r--ios/web/history_state_util.mm37
-rw-r--r--ios/web/history_state_util_unittest.mm108
-rw-r--r--ios/web/ios_web.gyp31
-rw-r--r--ios/web/ios_web_unittests.gyp5
-rw-r--r--ios/web/navigation/nscoder_util.h24
-rw-r--r--ios/web/navigation/nscoder_util.mm25
-rw-r--r--ios/web/navigation/nscoder_util_unittest.mm59
-rw-r--r--ios/web/navigation/time_smoother.cc25
-rw-r--r--ios/web/navigation/time_smoother.h32
-rw-r--r--ios/web/net/request_group_util.h58
-rw-r--r--ios/web/net/request_group_util.mm113
-rw-r--r--ios/web/net/request_group_util_unittest.mm31
-rw-r--r--ios/web/weak_nsobject_counter.h35
-rw-r--r--ios/web/weak_nsobject_counter.mm83
-rw-r--r--ios/web/weak_nsobject_counter_unittest.mm85
-rw-r--r--ios/web/web_state/blocked_popup_info.h47
-rw-r--r--ios/web/web_state/blocked_popup_info.mm39
-rw-r--r--ios/web/web_state/crw_recurring_task_delegate.h16
-rw-r--r--ios/web/web_state/js/crw_js_message_dynamic_manager.mm13
-rw-r--r--ios/web/web_state/wk_web_view_ssl_error_util.h29
-rw-r--r--ios/web/web_state/wk_web_view_ssl_error_util.mm96
-rw-r--r--ios/web/web_view_util.h15
-rw-r--r--ios/web/web_view_util.mm48
27 files changed, 1385 insertions, 38 deletions
diff --git a/ios/web/crw_network_activity_indicator_manager.h b/ios/web/crw_network_activity_indicator_manager.h
new file mode 100644
index 0000000..5af8f14
--- /dev/null
+++ b/ios/web/crw_network_activity_indicator_manager.h
@@ -0,0 +1,67 @@
+// 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_WEB_CRW_NETWORK_ACTIVITY_INDICATOR_MANAGER_H_
+#define IOS_WEB_CRW_NETWORK_ACTIVITY_INDICATOR_MANAGER_H_
+
+#import <Foundation/Foundation.h>
+
+// This class controls access to the network activity indicator across the
+// app. It provides a simple interface for clients to indicate they are
+// starting a network task and they would like the indicator shown, and to
+// indicate they have finished a network task.
+//
+// Clients are required to pass an NSString* to each method to identify
+// themselves. Separating clients into groups prevents a client from "stopping"
+// requests from other clients on accident, and makes those bugs easier to
+// track down. Specifically, the manager will immediately fail if the number
+// of tasks stopped for a group ever exceeds the number of tasks started for
+// that group. Clients are responsible for namespacing their group strings
+// properly. All methods must be called on the UI thread.
+@interface CRWNetworkActivityIndicatorManager : NSObject
+
+// Returns the singleton CRWNetworkActivityIndicatorManager.
++ (CRWNetworkActivityIndicatorManager*)sharedInstance;
+
+// Begins a single network task. The network activity indicator is guaranteed
+// to be shown after this finishes (if it isn't already). |group| must be
+// non-nil.
+- (void)startNetworkTaskForGroup:(NSString*)group;
+
+// Stops a single network task. The network activity indicator may or may not
+// stop being shown once this finishes, depending on whether there are other
+// unstopped tasks or not. |group| must be non-nil, and have at least one
+// unstopped task.
+- (void)stopNetworkTaskForGroup:(NSString*)group;
+
+// A convenience method for starting multiple network tasks at once. |group|
+// must be non-nil. |numTasks| must be greater than 0.
+- (void)startNetworkTasks:(NSUInteger)numTasks forGroup:(NSString*)group;
+
+// A convenience method for stopping multiple network tasks at once. |group|
+// must be non-nil. |numTasks| must be greater than 0, and |numTasks| must be
+// less than or equal to the number of unstopped tasks in |group|.
+- (void)stopNetworkTasks:(NSUInteger)numTasks forGroup:(NSString*)group;
+
+// A convenience method for stopping all network tasks for a group. |group|
+// must be non-nil. Can be called on any group at any time, regardless of
+// whether the group has any unstopped network tasks or not. Returns the number
+// of tasks stopped by this call.
+- (NSUInteger)clearNetworkTasksForGroup:(NSString*)group;
+
+// Returns the number of unstopped network tasks for |group|. |group| must be
+// non-nil. Can be called on any group at any time, regardless of whether the
+// group has any unstopped network tasks or not.
+- (NSUInteger)numNetworkTasksForGroup:(NSString*)group;
+
+// Returns the total number of unstopped network tasks, across all groups. This
+// method was added for testing only. Clients should never depend on this, and
+// should instead only be concerned with the number of unstopped network tasks
+// for the groups they control, which can be queried using
+// |-numNetworkTasksForGroup:|.
+- (NSUInteger)numTotalNetworkTasks;
+
+@end
+
+#endif // IOS_WEB_CRW_NETWORK_ACTIVITY_INDICATOR_MANAGER_H_
diff --git a/ios/web/crw_network_activity_indicator_manager.mm b/ios/web/crw_network_activity_indicator_manager.mm
new file mode 100644
index 0000000..ec8e0e3
--- /dev/null
+++ b/ios/web/crw_network_activity_indicator_manager.mm
@@ -0,0 +1,115 @@
+// 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/web/crw_network_activity_indicator_manager.h"
+
+#import <UIKit/UIKit.h>
+
+#include "base/logging.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/threading/thread_checker.h"
+
+@interface CRWNetworkActivityIndicatorManager () {
+ base::scoped_nsobject<NSMutableDictionary> _groupCounts;
+ NSUInteger _totalCount;
+ base::ThreadChecker _threadChecker;
+}
+
+@end
+
+@implementation CRWNetworkActivityIndicatorManager
+
++ (CRWNetworkActivityIndicatorManager*)sharedInstance {
+ static CRWNetworkActivityIndicatorManager* instance =
+ [[CRWNetworkActivityIndicatorManager alloc] init];
+ return instance;
+}
+
+- (id)init {
+ self = [super init];
+ if (self) {
+ _groupCounts.reset([[NSMutableDictionary alloc] init]);
+ _totalCount = 0;
+ }
+ return self;
+}
+
+- (void)startNetworkTaskForGroup:(NSString*)group {
+ [self startNetworkTasks:1 forGroup:group];
+}
+
+- (void)stopNetworkTaskForGroup:(NSString*)group {
+ [self stopNetworkTasks:1 forGroup:group];
+}
+
+- (void)startNetworkTasks:(NSUInteger)numTasks forGroup:(NSString*)group {
+ DCHECK(_threadChecker.CalledOnValidThread());
+ DCHECK(group);
+ DCHECK_GT(numTasks, 0U);
+ NSUInteger count = 0;
+ NSNumber* number = [_groupCounts objectForKey:group];
+ if (number) {
+ count = [number unsignedIntegerValue];
+ DCHECK_GT(count, 0U);
+ }
+ count += numTasks;
+ [_groupCounts setObject:[NSNumber numberWithUnsignedInteger:count]
+ forKey:group];
+ _totalCount += numTasks;
+ if (_totalCount == numTasks) {
+ [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
+ }
+}
+
+- (void)stopNetworkTasks:(NSUInteger)numTasks forGroup:(NSString*)group {
+ DCHECK(_threadChecker.CalledOnValidThread());
+ DCHECK(group);
+ DCHECK_GT(numTasks, 0U);
+ NSNumber* number = [_groupCounts objectForKey:group];
+ DCHECK(number);
+ NSUInteger count = [number unsignedIntegerValue];
+ DCHECK(count >= numTasks);
+ count -= numTasks;
+ if (count == 0) {
+ [_groupCounts removeObjectForKey:group];
+ } else {
+ [_groupCounts setObject:[NSNumber numberWithUnsignedInteger:count]
+ forKey:group];
+ }
+ _totalCount -= numTasks;
+ if (_totalCount == 0) {
+ [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
+ }
+}
+
+- (NSUInteger)clearNetworkTasksForGroup:(NSString*)group {
+ DCHECK(_threadChecker.CalledOnValidThread());
+ DCHECK(group);
+ NSNumber* number = [_groupCounts objectForKey:group];
+ if (!number) {
+ return 0;
+ }
+ NSUInteger count = [number unsignedIntegerValue];
+ DCHECK_GT(count, 0U);
+ [self stopNetworkTasks:count forGroup:group];
+ return count;
+}
+
+- (NSUInteger)numNetworkTasksForGroup:(NSString*)group {
+ DCHECK(_threadChecker.CalledOnValidThread());
+ DCHECK(group);
+ NSNumber* number = [_groupCounts objectForKey:group];
+ if (!number) {
+ return 0;
+ }
+ return [number unsignedIntegerValue];
+}
+
+- (NSUInteger)numTotalNetworkTasks {
+ DCHECK(_threadChecker.CalledOnValidThread());
+ return _totalCount;
+}
+
+
+@end
diff --git a/ios/web/crw_network_activity_indicator_manager_unittest.mm b/ios/web/crw_network_activity_indicator_manager_unittest.mm
new file mode 100644
index 0000000..081bd60
--- /dev/null
+++ b/ios/web/crw_network_activity_indicator_manager_unittest.mm
@@ -0,0 +1,154 @@
+// 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/web/crw_network_activity_indicator_manager.h"
+
+#import <UIKit/UIKit.h>
+
+#include "base/mac/scoped_nsobject.h"
+#include "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+
+namespace {
+
+NSString* const kNetworkActivityKeyOne =
+ @"CRWNetworkActivityIndicatorManagerTest.NetworkActivityIndicatorKeyOne";
+NSString* const kNetworkActivityKeyTwo =
+ @"CRWNetworkActivityIndicatorManagerTest.NetworkActivityIndicatorKeyTwo";
+
+class CRWNetworkActivityIndicatorManagerTest : public PlatformTest {
+ public:
+ CRWNetworkActivityIndicatorManagerTest()
+ : manager_([[CRWNetworkActivityIndicatorManager alloc] init]) {}
+
+ protected:
+ void ExpectNetworkActivity(NSUInteger groupOneCount,
+ NSUInteger groupTwoCount) {
+ EXPECT_EQ([manager_ numNetworkTasksForGroup:kNetworkActivityKeyOne],
+ groupOneCount);
+ EXPECT_EQ([manager_ numNetworkTasksForGroup:kNetworkActivityKeyTwo],
+ groupTwoCount);
+ EXPECT_EQ([manager_ numTotalNetworkTasks], groupOneCount + groupTwoCount);
+ if (groupOneCount + groupTwoCount > 0U) {
+ EXPECT_TRUE(
+ [[UIApplication sharedApplication]
+ isNetworkActivityIndicatorVisible]);
+ } else {
+ EXPECT_FALSE(
+ [[UIApplication sharedApplication]
+ isNetworkActivityIndicatorVisible]);
+ }
+ }
+ base::scoped_nsobject<CRWNetworkActivityIndicatorManager> manager_;
+};
+
+TEST_F(CRWNetworkActivityIndicatorManagerTest, TestNumNetworkTasksForGroup) {
+ EXPECT_EQ([manager_ numNetworkTasksForGroup:kNetworkActivityKeyOne], 0U);
+ EXPECT_EQ([manager_ numNetworkTasksForGroup:kNetworkActivityKeyTwo], 0U);
+ [manager_ startNetworkTasks:2U forGroup:kNetworkActivityKeyOne];
+ EXPECT_EQ([manager_ numNetworkTasksForGroup:kNetworkActivityKeyOne], 2U);
+ EXPECT_EQ([manager_ numNetworkTasksForGroup:kNetworkActivityKeyTwo], 0U);
+ [manager_ startNetworkTasks:3U forGroup:kNetworkActivityKeyTwo];
+ EXPECT_EQ([manager_ numNetworkTasksForGroup:kNetworkActivityKeyOne], 2U);
+ EXPECT_EQ([manager_ numNetworkTasksForGroup:kNetworkActivityKeyTwo], 3U);
+ [manager_ stopNetworkTasks:2U forGroup:kNetworkActivityKeyOne];
+ EXPECT_EQ([manager_ numNetworkTasksForGroup:kNetworkActivityKeyOne], 0U);
+ EXPECT_EQ([manager_ numNetworkTasksForGroup:kNetworkActivityKeyTwo], 3U);
+ [manager_ stopNetworkTasks:3U forGroup:kNetworkActivityKeyTwo];
+ EXPECT_EQ([manager_ numNetworkTasksForGroup:kNetworkActivityKeyOne], 0U);
+ EXPECT_EQ([manager_ numNetworkTasksForGroup:kNetworkActivityKeyTwo], 0U);
+}
+
+TEST_F(CRWNetworkActivityIndicatorManagerTest, TestNumTotalNetworkTasks) {
+ EXPECT_EQ([manager_ numTotalNetworkTasks], 0U);
+ [manager_ startNetworkTasks:2U forGroup:kNetworkActivityKeyOne];
+ EXPECT_EQ([manager_ numTotalNetworkTasks], 2U);
+ [manager_ startNetworkTasks:3U forGroup:kNetworkActivityKeyTwo];
+ EXPECT_EQ([manager_ numTotalNetworkTasks], 5U);
+ [manager_ stopNetworkTasks:2U forGroup:kNetworkActivityKeyOne];
+ EXPECT_EQ([manager_ numTotalNetworkTasks], 3U);
+ [manager_ stopNetworkTasks:3U forGroup:kNetworkActivityKeyTwo];
+ EXPECT_EQ([manager_ numTotalNetworkTasks], 0U);
+}
+
+TEST_F(CRWNetworkActivityIndicatorManagerTest,
+ StartStopNetworkTaskForOneGroup) {
+ ExpectNetworkActivity(0U, 0U);
+ [manager_ startNetworkTaskForGroup:kNetworkActivityKeyOne];
+ ExpectNetworkActivity(1U, 0U);
+ [manager_ stopNetworkTaskForGroup:kNetworkActivityKeyOne];
+ ExpectNetworkActivity(0U, 0U);
+}
+
+TEST_F(CRWNetworkActivityIndicatorManagerTest,
+ StartStopNetworkTaskForTwoGroups) {
+ ExpectNetworkActivity(0U, 0U);
+ [manager_ startNetworkTaskForGroup:kNetworkActivityKeyOne];
+ ExpectNetworkActivity(1U, 0U);
+ [manager_ startNetworkTaskForGroup:kNetworkActivityKeyTwo];
+ ExpectNetworkActivity(1U, 1U);
+ [manager_ stopNetworkTaskForGroup:kNetworkActivityKeyOne];
+ ExpectNetworkActivity(0U, 1U);
+ [manager_ stopNetworkTaskForGroup:kNetworkActivityKeyTwo];
+ ExpectNetworkActivity(0U, 0U);
+}
+
+TEST_F(CRWNetworkActivityIndicatorManagerTest,
+ StartStopNetworkTasksForOneGroup) {
+ ExpectNetworkActivity(0U, 0U);
+ [manager_ startNetworkTasks:2U forGroup:kNetworkActivityKeyOne];
+ ExpectNetworkActivity(2U, 0U);
+ [manager_ stopNetworkTasks:2U forGroup:kNetworkActivityKeyOne];
+ ExpectNetworkActivity(0U, 0U);
+}
+
+TEST_F(CRWNetworkActivityIndicatorManagerTest,
+ StartStopNetworkTasksForTwoGroups) {
+ ExpectNetworkActivity(0U, 0U);
+ [manager_ startNetworkTasks:2U forGroup:kNetworkActivityKeyOne];
+ ExpectNetworkActivity(2U, 0U);
+ [manager_ startNetworkTasks:3U forGroup:kNetworkActivityKeyTwo];
+ ExpectNetworkActivity(2U, 3U);
+ [manager_ stopNetworkTasks:2U forGroup:kNetworkActivityKeyOne];
+ ExpectNetworkActivity(0U, 3U);
+ [manager_ stopNetworkTasks:3U forGroup:kNetworkActivityKeyTwo];
+ ExpectNetworkActivity(0U, 0U);
+}
+
+TEST_F(CRWNetworkActivityIndicatorManagerTest, ClearNetworkTasksForGroup) {
+ ExpectNetworkActivity(0U, 0U);
+ [manager_ startNetworkTasks:2U forGroup:kNetworkActivityKeyOne];
+ ExpectNetworkActivity(2U, 0U);
+ [manager_ clearNetworkTasksForGroup:kNetworkActivityKeyOne];
+ ExpectNetworkActivity(0U, 0U);
+}
+
+TEST_F(CRWNetworkActivityIndicatorManagerTest,
+ ClearNetworkTasksForUnusedGroup) {
+ ExpectNetworkActivity(0U, 0U);
+ [manager_ clearNetworkTasksForGroup:kNetworkActivityKeyOne];
+ ExpectNetworkActivity(0U, 0U);
+}
+
+TEST_F(CRWNetworkActivityIndicatorManagerTest, StartStopNetworkTasksInChunks) {
+ ExpectNetworkActivity(0U, 0U);
+ [manager_ startNetworkTasks:2U forGroup:kNetworkActivityKeyOne];
+ ExpectNetworkActivity(2U, 0U);
+ [manager_ startNetworkTasks:3U forGroup:kNetworkActivityKeyTwo];
+ ExpectNetworkActivity(2U, 3U);
+ [manager_ startNetworkTasks:7U forGroup:kNetworkActivityKeyOne];
+ ExpectNetworkActivity(9U, 3U);
+ [manager_ startNetworkTaskForGroup:kNetworkActivityKeyTwo];
+ ExpectNetworkActivity(9U, 4U);
+ [manager_ stopNetworkTasks:4U forGroup:kNetworkActivityKeyOne];
+ ExpectNetworkActivity(5U, 4U);
+ [manager_ stopNetworkTasks:2U forGroup:kNetworkActivityKeyTwo];
+ ExpectNetworkActivity(5U, 2U);
+ [manager_ stopNetworkTasks:5U forGroup:kNetworkActivityKeyOne];
+ ExpectNetworkActivity(0U, 2U);
+ [manager_ clearNetworkTasksForGroup:kNetworkActivityKeyTwo];
+ ExpectNetworkActivity(0U, 0U);
+}
+
+} // namespace
diff --git a/ios/web/history_state_util.h b/ios/web/history_state_util.h
new file mode 100644
index 0000000..6c48928
--- /dev/null
+++ b/ios/web/history_state_util.h
@@ -0,0 +1,33 @@
+// Copyright 2012 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_WEB_HISTORY_STATE_UTIL_H_
+#define IOS_WEB_HISTORY_STATE_UTIL_H_
+
+#import <Foundation/Foundation.h>
+
+#include <string>
+
+class GURL;
+
+namespace web {
+namespace history_state_util {
+
+// Checks if toUrl is a valid argument to history.pushState() or
+// history.replaceState() given the current URL.
+bool IsHistoryStateChangeValid(const GURL& currentUrl,
+ const GURL& toUrl);
+
+// Generates the appropriate full URL for a history.pushState() or
+// history.replaceState() transition from currentURL to destination, resolved
+// against baseURL. |destination| may be a relative URL. Will return an invalid
+// URL if the resolved destination, or the transition, is not valid.
+GURL GetHistoryStateChangeUrl(const GURL& currentUrl,
+ const GURL& baseUrl,
+ const std::string& destination);
+
+} // namespace history_state_util
+} // namespace web
+
+#endif // IOS_WEB_HISTORY_STATE_UTIL_H_
diff --git a/ios/web/history_state_util.mm b/ios/web/history_state_util.mm
new file mode 100644
index 0000000..bdb11bce
--- /dev/null
+++ b/ios/web/history_state_util.mm
@@ -0,0 +1,37 @@
+// Copyright 2012 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/web/history_state_util.h"
+
+#include "base/logging.h"
+#include "url/gurl.h"
+
+namespace web {
+namespace history_state_util {
+
+bool IsHistoryStateChangeValid(const GURL& currentUrl,
+ const GURL& toUrl) {
+ // These two checks are very important to the security of the page. We cannot
+ // allow the page to change the state to an invalid URL.
+ CHECK(currentUrl.is_valid());
+ CHECK(toUrl.is_valid());
+
+ return toUrl.GetOrigin() == currentUrl.GetOrigin();
+}
+
+GURL GetHistoryStateChangeUrl(const GURL& currentUrl,
+ const GURL& baseUrl,
+ const std::string& destination) {
+ if (!baseUrl.is_valid())
+ return GURL();
+ GURL toUrl = baseUrl.Resolve(destination);
+
+ if (!toUrl.is_valid() || !IsHistoryStateChangeValid(currentUrl, toUrl))
+ return GURL();
+
+ return toUrl;
+}
+
+} // namespace history_state_util
+} // namespace web
diff --git a/ios/web/history_state_util_unittest.mm b/ios/web/history_state_util_unittest.mm
new file mode 100644
index 0000000..f7fe916
--- /dev/null
+++ b/ios/web/history_state_util_unittest.mm
@@ -0,0 +1,108 @@
+// Copyright 2012 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 <Foundation/Foundation.h>
+
+#import "ios/web/history_state_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+#include "url/gurl.h"
+
+namespace web {
+namespace {
+struct TestEntry {
+ std::string fromUrl;
+ std::string toUrl;
+ std::string expectedUrl;
+};
+
+class HistoryStateUtilTest : public ::testing::Test {
+ protected:
+ static const struct TestEntry tests_[];
+};
+
+const struct TestEntry HistoryStateUtilTest::tests_[] = {
+ // Valid absolute changes.
+ { "http://foo.com", "http://foo.com/bar", "http://foo.com/bar" },
+ { "https://foo.com", "https://foo.com/bar", "https://foo.com/bar" },
+ { "http://foo.com/", "http://foo.com#bar", "http://foo.com#bar" },
+ { "http://foo.com:80", "http://foo.com:80/b", "http://foo.com:80/b"},
+ { "http://foo.com:888", "http://foo.com:888/b", "http://foo.com:888/b"},
+ // Valid relative changes.
+ { "http://foo.com", "#bar", "http://foo.com#bar" },
+ { "http://foo.com/", "#bar", "http://foo.com/#bar" },
+ { "https://foo.com/", "bar", "https://foo.com/bar" },
+ { "http://foo.com/foo/1", "/bar", "http://foo.com/bar" },
+ { "http://foo.com/foo/1", "bar", "http://foo.com/foo/bar" },
+ { "http://foo.com/", "bar.com", "http://foo.com/bar.com" },
+ { "http://foo.com", "bar.com", "http://foo.com/bar.com" },
+ { "http://foo.com:888", "bar.com", "http://foo.com:888/bar.com" },
+ // Invalid scheme changes.
+ { "http://foo.com", "https://foo.com#bar", "" },
+ { "https://foo.com", "http://foo.com#bar", "" },
+ // Invalid domain changes.
+ { "http://foo.com/bar", "http://bar.com", "" },
+ { "http://foo.com/bar", "http://www.foo.com/bar2", "" },
+ // Valid port change.
+ { "http://foo.com", "http://foo.com:80/bar", "http://foo.com/bar" },
+ { "http://foo.com:80", "http://foo.com/bar", "http://foo.com/bar" },
+ // Invalid port change.
+ { "http://foo.com", "http://foo.com:42/bar", "" },
+ { "http://foo.com:42", "http://foo.com/bar", "" },
+ // Invalid URL.
+ { "http://foo.com", "http://fo o.c om/ba r", "" },
+ { "http://foo.com:80", "bar", "http://foo.com:80/bar" }
+};
+
+TEST_F(HistoryStateUtilTest, TestIsHistoryStateChangeValid) {
+ for (size_t i = 0; i < arraysize(tests_); ++i) {
+ GURL fromUrl(tests_[i].fromUrl);
+ GURL toUrl = history_state_util::GetHistoryStateChangeUrl(fromUrl, fromUrl,
+ tests_[i].toUrl);
+ bool expected_result = tests_[i].expectedUrl.size() > 0;
+ bool actual_result = toUrl.is_valid();
+ if (actual_result) {
+ actual_result = history_state_util::IsHistoryStateChangeValid(fromUrl,
+ toUrl);
+ }
+ EXPECT_EQ(expected_result, actual_result) << tests_[i].fromUrl << " "
+ << tests_[i].toUrl;
+ }
+}
+
+TEST_F(HistoryStateUtilTest, TestGetHistoryStateChangeUrl) {
+ for (size_t i = 0; i < arraysize(tests_); ++i) {
+ GURL fromUrl(tests_[i].fromUrl);
+ GURL expectedResult(tests_[i].expectedUrl);
+ GURL actualResult = history_state_util::GetHistoryStateChangeUrl(
+ fromUrl, fromUrl, tests_[i].toUrl);
+ EXPECT_EQ(expectedResult, actualResult);
+ }
+}
+
+// Ensures that the baseUrl is used to resolve the destination, not currentUrl.
+TEST_F(HistoryStateUtilTest, TestGetHistoryStateChangeUrlWithBase) {
+ GURL fromUrl("http://foo.com/relative/path");
+ GURL baseUrl("http://foo.com");
+ std::string destination = "bar";
+
+ GURL result = history_state_util::GetHistoryStateChangeUrl(fromUrl, baseUrl,
+ destination);
+ EXPECT_TRUE(result.is_valid());
+ EXPECT_EQ(GURL("http://foo.com/bar"), result);
+}
+
+// Ensures that an invalid baseUrl gracefully returns an invalid destination.
+TEST_F(HistoryStateUtilTest, TestGetHistoryStateChangeUrlWithInvalidBase) {
+ GURL fromUrl("http://foo.com");
+ GURL baseUrl("http://not a url");
+ std::string destination = "baz";
+
+ GURL result = history_state_util::GetHistoryStateChangeUrl(fromUrl, baseUrl,
+ destination);
+ EXPECT_FALSE(result.is_valid());
+}
+
+} // anonymous namespace
+} // namespace web
diff --git a/ios/web/ios_web.gyp b/ios/web/ios_web.gyp
index 37545c9..5e208e9 100644
--- a/ios/web/ios_web.gyp
+++ b/ios/web/ios_web.gyp
@@ -14,6 +14,7 @@
'../..',
],
'dependencies': [
+ 'ios_web_core',
'../../base/base.gyp:base',
'../../content/content.gyp:content_browser',
'../../net/net.gyp:net',
@@ -25,8 +26,14 @@
'load_committed_details.cc',
'navigation/navigation_item_impl.h',
'navigation/navigation_item_impl.mm',
+ 'navigation/nscoder_util.h',
+ 'navigation/nscoder_util.mm',
+ 'navigation/time_smoother.cc',
+ 'navigation/time_smoother.h',
'net/cert_policy.cc',
'net/certificate_policy_cache.cc',
+ 'net/request_group_util.h',
+ 'net/request_group_util.mm',
'public/block_types.h',
'public/browser_state.h',
'public/browser_url_rewriter.h',
@@ -74,6 +81,11 @@
'url_scheme_util.mm',
'url_util.cc',
'user_metrics.cc',
+ 'weak_nsobject_counter.h',
+ 'weak_nsobject_counter.mm',
+ 'web_state/blocked_popup_info.h',
+ 'web_state/blocked_popup_info.mm',
+ 'web_state/crw_recurring_task_delegate.h',
'web_state/js/crw_js_base_manager.mm',
'web_state/js/crw_js_common_manager.h',
'web_state/js/crw_js_common_manager.mm',
@@ -85,6 +97,8 @@
'web_state/js/crw_js_message_manager.mm',
'web_state/web_state_observer.cc',
'web_state/web_state_observer_bridge.mm',
+ 'web_state/wk_web_view_ssl_error_util.h',
+ 'web_state/wk_web_view_ssl_error_util.mm',
'web_thread.cc',
'web_thread_impl.cc',
'web_thread_impl.h',
@@ -92,6 +106,23 @@
'web_view_util.mm',
],
},
+ # Target shared by ios_web and CrNet.
+ {
+ 'target_name': 'ios_web_core',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../../base/base.gyp:base',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ 'sources': [
+ 'crw_network_activity_indicator_manager.h',
+ 'crw_network_activity_indicator_manager.mm',
+ 'history_state_util.h',
+ 'history_state_util.mm',
+ ],
+ },
{
'target_name': 'js_resources',
'type': 'none',
diff --git a/ios/web/ios_web_unittests.gyp b/ios/web/ios_web_unittests.gyp
index 1bf966f..d0a04bd 100644
--- a/ios/web/ios_web_unittests.gyp
+++ b/ios/web/ios_web_unittests.gyp
@@ -20,12 +20,17 @@
],
'sources': [
'browser_state_unittest.cc',
+ 'crw_network_activity_indicator_manager_unittest.mm',
+ 'history_state_util_unittest.mm',
'navigation/navigation_item_impl_unittest.mm',
+ 'navigation/nscoder_util_unittest.mm',
'net/cert_policy_unittest.cc',
+ 'net/request_group_util_unittest.mm',
'public/referrer_util_unittest.cc',
'string_util_unittest.cc',
'url_scheme_util_unittest.mm',
'url_util_unittest.cc',
+ 'weak_nsobject_counter_unittest.mm',
],
},
],
diff --git a/ios/web/navigation/nscoder_util.h b/ios/web/navigation/nscoder_util.h
new file mode 100644
index 0000000..5a92f19f
--- /dev/null
+++ b/ios/web/navigation/nscoder_util.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2012 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_WEB_NAVIGATION_NSCODER_UTIL_H_
+#define IOS_WEB_NAVIGATION_NSCODER_UTIL_H_
+
+#import <Foundation/Foundation.h>
+
+#include <string>
+
+namespace web {
+namespace nscoder_util {
+
+// Archives a std::string in an Objective-C key archiver.
+void EncodeString(NSCoder* coder, NSString* key, const std::string& string);
+
+// Decode a std::string from an Objective-C key unarchiver.
+std::string DecodeString(NSCoder* decoder, NSString* key);
+
+} // namespace nscoder_util
+} // namespace web
+
+#endif // IOS_WEB_NAVIGATION_NSCODER_UTIL_H_
diff --git a/ios/web/navigation/nscoder_util.mm b/ios/web/navigation/nscoder_util.mm
new file mode 100644
index 0000000..551dccd
--- /dev/null
+++ b/ios/web/navigation/nscoder_util.mm
@@ -0,0 +1,25 @@
+// Copyright (c) 2012 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 <string>
+
+#include "ios/web/navigation/nscoder_util.h"
+
+namespace web {
+namespace nscoder_util {
+
+void EncodeString(NSCoder* coder, NSString* key, const std::string& string) {
+ [coder encodeBytes:reinterpret_cast<const uint8_t*>(string.data())
+ length:string.size()
+ forKey:key];
+}
+
+std::string DecodeString(NSCoder* decoder, NSString* key) {
+ NSUInteger length;
+ const uint8_t* bytes = [decoder decodeBytesForKey:key returnedLength:&length];
+ return std::string(reinterpret_cast<const char*>(bytes), length);
+}
+
+} // namespace nscoder_util
+} // namespace web
diff --git a/ios/web/navigation/nscoder_util_unittest.mm b/ios/web/navigation/nscoder_util_unittest.mm
new file mode 100644
index 0000000..8d3b667
--- /dev/null
+++ b/ios/web/navigation/nscoder_util_unittest.mm
@@ -0,0 +1,59 @@
+// Copyright 2012 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 <Foundation/Foundation.h>
+
+#include "base/basictypes.h"
+#include "base/mac/scoped_nsobject.h"
+#include "ios/web/navigation/nscoder_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+namespace web {
+namespace {
+
+typedef PlatformTest NSCoderStdStringTest;
+
+const char* testStrings[] = {
+ "Arf",
+ "",
+ "This is working™",
+ "古池や蛙飛込む水の音\nふるいけやかわずとびこむみずのおと",
+ "ἀγεωμέτρητος μηδεὶς εἰσίτω",
+ "Bang!\t\n"
+};
+
+TEST_F(NSCoderStdStringTest, encodeDecode) {
+ for (size_t i = 0; i < arraysize(testStrings); ++i) {
+ NSMutableData* data = [NSMutableData data];
+
+ base::scoped_nsobject<NSKeyedArchiver> archiver(
+ [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]);
+ nscoder_util::EncodeString(archiver, @"test", testStrings[i]);
+ [archiver finishEncoding];
+
+ base::scoped_nsobject<NSKeyedUnarchiver> unarchiver(
+ [[NSKeyedUnarchiver alloc] initForReadingWithData:data]);
+ const std::string decoded = nscoder_util::DecodeString(unarchiver, @"test");
+
+ EXPECT_EQ(decoded, testStrings[i]);
+ }
+}
+
+TEST_F(NSCoderStdStringTest, decodeEmpty) {
+ NSMutableData* data = [NSMutableData data];
+
+ base::scoped_nsobject<NSKeyedArchiver> archiver(
+ [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]);
+ [archiver finishEncoding];
+
+ base::scoped_nsobject<NSKeyedUnarchiver> unarchiver(
+ [[NSKeyedUnarchiver alloc] initForReadingWithData:data]);
+ const std::string decoded = nscoder_util::DecodeString(unarchiver, @"test");
+
+ EXPECT_EQ(decoded, "");
+}
+
+} // namespace
+} // namespace web
diff --git a/ios/web/navigation/time_smoother.cc b/ios/web/navigation/time_smoother.cc
new file mode 100644
index 0000000..b8aaec6
--- /dev/null
+++ b/ios/web/navigation/time_smoother.cc
@@ -0,0 +1,25 @@
+// 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 "ios/web/navigation/time_smoother.h"
+
+namespace web {
+
+// Duplicated from content/browser/web_contents/navigation_controller_impl.cc.
+base::Time TimeSmoother::GetSmoothedTime(base::Time t) {
+ // If |t| is between the water marks, we're in a run of duplicates
+ // or just getting out of it, so increase the high-water mark to get
+ // a time that probably hasn't been used before and return it.
+ if (low_water_mark_ <= t && t <= high_water_mark_) {
+ high_water_mark_ += base::TimeDelta::FromMicroseconds(1);
+ return high_water_mark_;
+ }
+
+ // Otherwise, we're clear of the last duplicate run, so reset the
+ // water marks.
+ low_water_mark_ = high_water_mark_ = t;
+ return t;
+}
+
+} // namespace web
diff --git a/ios/web/navigation/time_smoother.h b/ios/web/navigation/time_smoother.h
new file mode 100644
index 0000000..59eb2a27
--- /dev/null
+++ b/ios/web/navigation/time_smoother.h
@@ -0,0 +1,32 @@
+// 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_WEB_NAVIGATION_TIME_SMOOTHER_H_
+#define IOS_WEB_NAVIGATION_TIME_SMOOTHER_H_
+
+#include "base/time/time.h"
+
+namespace web {
+
+// Helper class to smooth out runs of duplicate timestamps while still
+// allowing time to jump backwards.
+//
+// Duplicated from NavigationControllerImpl (until we have a better
+// idea how to handle NavigationController implementation overlap
+// in general).
+class TimeSmoother {
+ public:
+ // Returns |t| with possibly some time added on.
+ base::Time GetSmoothedTime(base::Time t);
+
+ private:
+ // |low_water_mark_| is the first time in a sequence of adjusted
+ // times and |high_water_mark_| is the last.
+ base::Time low_water_mark_;
+ base::Time high_water_mark_;
+};
+
+} // namespace web
+
+#endif // IOS_WEB_NAVIGATION_TIME_SMOOTHER_H_
diff --git a/ios/web/net/request_group_util.h b/ios/web/net/request_group_util.h
new file mode 100644
index 0000000..525f2d0
--- /dev/null
+++ b/ios/web/net/request_group_util.h
@@ -0,0 +1,58 @@
+// 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_WEB_NET_REQUEST_GROUP_UTIL_H_
+#define IOS_WEB_NET_REQUEST_GROUP_UTIL_H_
+
+@class NSString;
+@class NSURL;
+@class NSURLRequest;
+
+// Request group IDs are internally used by the web layer to associate network
+// requests to RequestTracker instances, and in turn, to WebState instances.
+// The ID can be added to the user-agent string by UIWebView in most cases and
+// then extracted by the network layer.
+// However, when using non-standard schemes, UIWebView does not add the
+// "User-Agent" HTTP header to the requests. The workaround for this case is to
+// add the ID directly in the URL of the main request (which is the only request
+// accessible from the UIWebView delegate).
+
+namespace web {
+
+// Generates a request-group ID.
+NSString* GenerateNewRequestGroupID();
+
+// Extracts the requestGroupID embedded in a User-Agent string or nil if a
+// requestGroupID cannot be located.
+NSString* ExtractRequestGroupIDFromUserAgent(NSString* user_agent);
+
+// Returns a new user agent, which is the result of the encoding of
+// |request_group_id| in |base_user_agent|. The request group ID can be later
+// extracted with ExtractRequestGroupIDFromUserAgent().
+NSString* AddRequestGroupIDToUserAgent(NSString* base_user_agent,
+ NSString* request_group_id);
+
+// Extracts the requestGroupID embedded in a NSURL or nil if a requestGroupID
+// cannot be located.
+NSString* ExtractRequestGroupIDFromURL(NSURL* url);
+
+// Returns a new user agent, which is the result of the encoding of
+// |request_group_id| in |base_url|. The request group ID can be later extracted
+// with ExtractRequestGroupIDFromURL().
+NSURL* AddRequestGroupIDToURL(NSURL* base_url, NSString* request_group_id);
+
+// Extracts the request group ID from |request| by retrieving it from the
+// user-agent if possible, and from the parent URL otherwise.
+// The ID can only be retrived from the parent URL if its scheme is
+// |application_scheme|.
+// The reason why the |application_scheme| case is different is because
+// UIWebView does not provide a "User-Agent" HTTP header for these requests.
+// The ID is then encoded in the URL of the main request, and thus sub-requests
+// have to look in their parent URL for the ID.
+NSString* ExtractRequestGroupIDFromRequest(NSURLRequest* request,
+ NSString* application_scheme);
+
+} // namespace web
+
+#endif // IOS_WEB_NET_REQUEST_GROUP_UTIL_H_
diff --git a/ios/web/net/request_group_util.mm b/ios/web/net/request_group_util.mm
new file mode 100644
index 0000000..9a41cef
--- /dev/null
+++ b/ios/web/net/request_group_util.mm
@@ -0,0 +1,113 @@
+// 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/web/net/request_group_util.h"
+
+#include <Foundation/Foundation.h>
+
+#include "base/base64.h"
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "base/strings/sys_string_conversions.h"
+#import "net/base/mac/url_conversions.h"
+#include "url/gurl.h"
+
+namespace {
+// Minimum length for a request group ID. A shorter string is considered as an
+// invalid ID.
+const int kMinimumIDLength = 5;
+}
+
+namespace web {
+
+// Generates a request-group ID used to correlate web requests with the embedder
+// that triggered it. It is important that the return value should not be unique
+// for different users. See crbug/355613 for context.
+NSString* GenerateNewRequestGroupID() {
+ const unsigned int kGroupSize = 1000;
+ static unsigned long count = 0;
+ static unsigned int offset = base::RandInt(0, kGroupSize - 1);
+
+ unsigned long current = count++;
+ if (current < kGroupSize)
+ current = (current + offset) % kGroupSize;
+
+ // The returned string must have a minimum of kMinimumIDLength characters, and
+ // no spaces.
+ // TODO(blundell): Develop a long-term solution to this problem.
+ // crbug.com/329243
+ return [NSString stringWithFormat:@"%06lu", current];
+}
+
+NSString* ExtractRequestGroupIDFromUserAgent(NSString* user_agent) {
+ if (![user_agent length])
+ return nil;
+
+ // The request_group_id is wrapped by parenthesis in the last space-delimited
+ // fragment.
+ NSString* fragment =
+ [[user_agent componentsSeparatedByString:@" "] lastObject];
+ NSString* request_group_id =
+ [fragment hasPrefix:@"("] && [fragment hasSuffix:@")"]
+ ? [fragment substringWithRange:NSMakeRange(1, [fragment length] - 2)]
+ : nil;
+ // GTLService constructs user agents that end with "(gzip)". To avoid these
+ // getting treated as having the request_group_id "gzip", short-circuit out if
+ // the request_group_id is not long enough to be a valid request_group_id (all
+ // valid request_group_id are at least kMinimumIDLength characters long).
+ // TODO(blundell): Develop a long-term solution to this problem.
+ // crbug.com/329243
+ if ([request_group_id length] < kMinimumIDLength)
+ return nil;
+ return request_group_id;
+}
+
+NSString* AddRequestGroupIDToUserAgent(NSString* base_user_agent,
+ NSString* request_group_id) {
+ // TODO(blundell): Develop a long-term solution to this problem.
+ // crbug.com/329243
+ DCHECK([request_group_id length] >= kMinimumIDLength);
+ return
+ [NSString stringWithFormat:@"%@ (%@)", base_user_agent, request_group_id];
+}
+
+NSString* ExtractRequestGroupIDFromURL(NSURL* url) {
+ GURL gurl = net::GURLWithNSURL(url);
+ if (!gurl.has_username())
+ return nil;
+
+ std::string request_group_id_as_string;
+ if (base::Base64Decode(gurl.username(), &request_group_id_as_string))
+ return base::SysUTF8ToNSString(request_group_id_as_string);
+
+ return nil;
+}
+
+NSURL* AddRequestGroupIDToURL(NSURL* base_url, NSString* request_group_id) {
+ GURL url = net::GURLWithNSURL(base_url);
+ std::string base64RequestGroupID;
+ base::Base64Encode(base::SysNSStringToUTF8(request_group_id),
+ &base64RequestGroupID);
+ GURL::Replacements replacements;
+ replacements.SetUsernameStr(base64RequestGroupID);
+ url = url.ReplaceComponents(replacements);
+ return net::NSURLWithGURL(url);
+}
+
+NSString* ExtractRequestGroupIDFromRequest(NSURLRequest* request,
+ NSString* application_scheme) {
+ NSString* user_agent =
+ [[request allHTTPHeaderFields] objectForKey:@"User-Agent"];
+ NSString* request_group_id = ExtractRequestGroupIDFromUserAgent(user_agent);
+ if (request_group_id)
+ return request_group_id;
+ if (application_scheme &&
+ [[request.mainDocumentURL scheme]
+ caseInsensitiveCompare:application_scheme] == NSOrderedSame) {
+ return ExtractRequestGroupIDFromURL(request.mainDocumentURL);
+ }
+ return nil;
+}
+
+} // namespace web
diff --git a/ios/web/net/request_group_util_unittest.mm b/ios/web/net/request_group_util_unittest.mm
new file mode 100644
index 0000000..b21e30e
--- /dev/null
+++ b/ios/web/net/request_group_util_unittest.mm
@@ -0,0 +1,31 @@
+// 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/web/net/request_group_util.h"
+
+#include <Foundation/Foundation.h>
+
+#include "base/mac/scoped_nsobject.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Checks that all newly generated groupID are unique and that there are no
+// duplicates.
+TEST(RequestGroupUtilTest, RequestGroupID) {
+ base::scoped_nsobject<NSMutableSet> set([[NSMutableSet alloc] init]);
+ const size_t kGenerated = 2000;
+ for (size_t i = 0; i < kGenerated; ++i)
+ [set addObject:web::GenerateNewRequestGroupID()];
+ EXPECT_EQ(kGenerated, [set count]);
+}
+
+// Tests that the ExtractRequestGroupIDFromUserAgent function behaves as
+// intended.
+TEST(RequestGroupUtilTest, ExtractRequestGroupIDFromUserAgent) {
+ EXPECT_FALSE(web::ExtractRequestGroupIDFromUserAgent(nil));
+ EXPECT_FALSE(web::ExtractRequestGroupIDFromUserAgent(
+ @"Lynx/2.8.1pre.9 libwww-FM/2.14"));
+ EXPECT_FALSE(web::ExtractRequestGroupIDFromUserAgent(@" "));
+ EXPECT_TRUE([web::ExtractRequestGroupIDFromUserAgent(@"Mozilla/3.04 (WinNT)")
+ isEqualToString:@"WinNT"]);
+}
diff --git a/ios/web/weak_nsobject_counter.h b/ios/web/weak_nsobject_counter.h
new file mode 100644
index 0000000..f5fb62f
--- /dev/null
+++ b/ios/web/weak_nsobject_counter.h
@@ -0,0 +1,35 @@
+// 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_WEB_WEAK_NSOBJECT_COUNTER_H_
+#define IOS_WEB_WEAK_NSOBJECT_COUNTER_H_
+
+#import <Foundation/Foundation.h>
+
+#include "base/memory/linked_ptr.h"
+#include "base/threading/non_thread_safe.h"
+
+namespace web {
+
+// WeakNSObjectCounter is a class to maintain a counter of all the objects
+// that are added using |Insert| that are alive.
+// This class is not thread safe and objects must be destroyed and created on
+// the same thread.
+// TODO(stuartmorgan): Remove this once ARC is supported.
+class WeakNSObjectCounter : public base::NonThreadSafe {
+ public:
+ WeakNSObjectCounter();
+ ~WeakNSObjectCounter();
+ // Inserts an object. |object| cannot be nil.
+ void Insert(id object);
+ // Returns the count of all active objects that have been inserted.
+ NSUInteger Size() const;
+ private:
+ // The object that maintains the number of active items.
+ linked_ptr<NSUInteger> counter_;
+};
+
+} // namespace web
+
+#endif // IOS_WEB_WEAK_NSOBJECT_COUNTER_H_
diff --git a/ios/web/weak_nsobject_counter.mm b/ios/web/weak_nsobject_counter.mm
new file mode 100644
index 0000000..bd14b7b
--- /dev/null
+++ b/ios/web/weak_nsobject_counter.mm
@@ -0,0 +1,83 @@
+// 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/web/weak_nsobject_counter.h"
+
+#import <objc/runtime.h>
+
+#include "base/logging.h"
+#import "base/mac/scoped_nsobject.h"
+
+namespace {
+// The key needed for objc_setAssociatedObject. Any value will do, because the
+// address is the key.
+const char kObserverAssociatedObjectKey = 'h';
+}
+
+// Used for observing the objects tracked in the WeakNSObjectCounter. This
+// object will be dealloced when the tracked object is dealloced and will
+// notify the shared counter.
+@interface CRBWeakNSObjectDeallocationObserver : NSObject
+// Designated initializer. |object| cannot be nil. It registers self as an
+// associated object to |object|.
+- (instancetype)initWithSharedCounter:(const linked_ptr<NSUInteger>&)counter
+ objectToBeObserved:(id)object;
+@end
+
+@implementation CRBWeakNSObjectDeallocationObserver {
+ linked_ptr<NSUInteger> _counter;
+}
+
+- (instancetype)init {
+ NOTREACHED();
+ return nil;
+}
+
+- (instancetype)initWithSharedCounter:(const linked_ptr<NSUInteger>&)counter
+ objectToBeObserved:(id)object {
+ self = [super init];
+ if (self) {
+ DCHECK(counter.get());
+ DCHECK(object);
+ _counter = counter;
+ objc_setAssociatedObject(object, &kObserverAssociatedObjectKey, self,
+ OBJC_ASSOCIATION_RETAIN);
+ (*_counter)++;
+ }
+ return self;
+}
+
+- (void)dealloc {
+ DCHECK(_counter.get());
+ (*_counter)--;
+ _counter.reset();
+ [super dealloc];
+}
+
+@end
+
+namespace web {
+
+WeakNSObjectCounter::WeakNSObjectCounter() : counter_(new NSUInteger(0)) {
+}
+
+WeakNSObjectCounter::~WeakNSObjectCounter() {
+ DCHECK(CalledOnValidThread());
+}
+
+void WeakNSObjectCounter::Insert(id object) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(object);
+ // Create an associated object and register it with |object|.
+ base::scoped_nsobject<CRBWeakNSObjectDeallocationObserver> observingObject(
+ [[CRBWeakNSObjectDeallocationObserver alloc]
+ initWithSharedCounter:counter_ objectToBeObserved:object]);
+}
+
+NSUInteger WeakNSObjectCounter::Size() const {
+ DCHECK(CalledOnValidThread());
+ return *counter_;
+}
+
+} // namespace web
diff --git a/ios/web/weak_nsobject_counter_unittest.mm b/ios/web/weak_nsobject_counter_unittest.mm
new file mode 100644
index 0000000..b981d2d
--- /dev/null
+++ b/ios/web/weak_nsobject_counter_unittest.mm
@@ -0,0 +1,85 @@
+// 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/web/weak_nsobject_counter.h"
+
+#import <Foundation/Foundation.h>
+
+#import "base/ios/weak_nsobject.h"
+#import "base/mac/scoped_nsobject.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// Tests that a WeakNSObjectCounter correctly maintains a weak reference and
+// calculates the size correctly when many objects are added.
+TEST(WeakNSObjectCounter, ManyObjects) {
+ web::WeakNSObjectCounter object_counter;
+ base::scoped_nsobject<NSObject> p1([[NSObject alloc] init]);
+ base::scoped_nsobject<NSObject> p2([[NSObject alloc] init]);
+ base::scoped_nsobject<NSObject> p3([[NSObject alloc] init]);
+ object_counter.Insert(p1);
+ EXPECT_EQ(1U, object_counter.Size());
+ object_counter.Insert(p2);
+ EXPECT_EQ(2U, object_counter.Size());
+ object_counter.Insert(p3);
+ EXPECT_EQ(3U, object_counter.Size());
+ // Make sure p3 is correctly removed from the object counter.
+ p3.reset();
+ EXPECT_FALSE(p3);
+ EXPECT_EQ(2U, object_counter.Size());
+ // Make sure p2 is correctly removed from the object counter.
+ p2.reset();
+ EXPECT_EQ(1U, object_counter.Size());
+ // Make sure p1 is correctly removed from the object counter.
+ p1.reset();
+ EXPECT_FALSE(p1);
+ EXPECT_EQ(0U, object_counter.Size());
+}
+
+// Tests that WeakNSObjectCounter correctly works when the object outlives the
+// WeakNSObjectCounter that is observing it.
+TEST(WeakNSObjectCounter, ObjectOutlivesWeakNSObjectCounter) {
+ base::scoped_nsobject<NSObject> p1([[NSObject alloc] init]);
+ {
+ web::WeakNSObjectCounter object_counter;
+ object_counter.Insert(p1);
+ EXPECT_EQ(1U, object_counter.Size());
+ }
+ p1.reset();
+ EXPECT_FALSE(p1);
+}
+
+// Tests that WeakNSObjectCounter does not add the same object twice.
+TEST(WeakNSObjectCounter, SameObject) {
+ base::scoped_nsobject<NSObject> p1([[NSObject alloc] init]);
+ base::WeakNSObject<NSObject> p2(p1);
+ // Make sure they are the same object.
+ ASSERT_EQ(p1, p2);
+ web::WeakNSObjectCounter object_counter;
+ object_counter.Insert(p1);
+ EXPECT_EQ(1U, object_counter.Size());
+ object_counter.Insert(p2);
+ EXPECT_EQ(1U, object_counter.Size());
+}
+
+// Tests that WeakNSObjectCounter correctly maintains a reference to the object
+// and reduces its size only when the last reference to the object goes away.
+TEST(WeakNSObjectCounter, LastReference) {
+ web::WeakNSObjectCounter object_counter;
+ base::scoped_nsobject<NSObject> p1([[NSObject alloc] init]);
+ object_counter.Insert(p1);
+ EXPECT_EQ(1U, object_counter.Size());
+ base::scoped_nsobject<NSObject> p2(p1);
+ base::scoped_nsobject<NSObject> p3(p1);
+ p1.reset();
+ EXPECT_EQ(1U, object_counter.Size());
+ p2.reset();
+ EXPECT_EQ(1U, object_counter.Size());
+ // Last reference goes away.
+ p3.reset();
+ EXPECT_FALSE(object_counter.Size());
+}
+
+} // namespace
diff --git a/ios/web/web_state/blocked_popup_info.h b/ios/web/web_state/blocked_popup_info.h
new file mode 100644
index 0000000..9373ad36
--- /dev/null
+++ b/ios/web/web_state/blocked_popup_info.h
@@ -0,0 +1,47 @@
+// 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_WEB_WEB_STATE_BLOCKED_POPUP_INFO_H_
+#define IOS_WEB_WEB_STATE_BLOCKED_POPUP_INFO_H_
+
+#import <Foundation/Foundation.h>
+
+#include "base/ios/block_types.h"
+#include "base/mac/scoped_nsobject.h"
+#include "ios/web/public/referrer.h"
+#include "url/gurl.h"
+
+namespace web {
+
+// Contain all information related to a blocked popup.
+// TODO(eugenebut): rename to BlockedPopup as it's not an info object anymore.
+class BlockedPopupInfo {
+ public:
+ BlockedPopupInfo(const GURL& url,
+ const Referrer& referrer,
+ NSString* window_name,
+ ProceduralBlock show_popup_handler);
+ ~BlockedPopupInfo();
+
+ // Returns the URL of the popup that was blocked.
+ const GURL& url() const { return url_; }
+ // Returns the Referrer of the URL that was blocked.
+ const Referrer& referrer() const { return referrer_; }
+ // Returns the window name of the popup that was blocked.
+ NSString* window_name() const { return window_name_; }
+ // Allows the popup by opening the blocked popup window.
+ void ShowPopup() const;
+
+ BlockedPopupInfo(const BlockedPopupInfo& blocked_popup_info);
+ void operator=(const BlockedPopupInfo& blocked_popup_info);
+ private:
+ GURL url_;
+ Referrer referrer_;
+ base::scoped_nsobject<NSString> window_name_;
+ ProceduralBlock show_popup_handler_;
+};
+
+} // namespace web
+
+#endif // IOS_WEB_WEB_STATE_BLOCKED_POPUP_INFO_H_
diff --git a/ios/web/web_state/blocked_popup_info.mm b/ios/web/web_state/blocked_popup_info.mm
new file mode 100644
index 0000000..721d18e
--- /dev/null
+++ b/ios/web/web_state/blocked_popup_info.mm
@@ -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.
+
+#include "ios/web/web_state/blocked_popup_info.h"
+
+namespace web {
+
+BlockedPopupInfo::BlockedPopupInfo(const GURL& url,
+ const Referrer& referrer,
+ NSString* window_name,
+ ProceduralBlock show_popup_handler)
+ : url_(url),
+ referrer_(referrer),
+ window_name_([window_name copy]),
+ show_popup_handler_([show_popup_handler copy]){
+}
+
+BlockedPopupInfo::BlockedPopupInfo(const BlockedPopupInfo& blocked_popup_info)
+ : url_(blocked_popup_info.url_),
+ referrer_(blocked_popup_info.referrer_),
+ window_name_([blocked_popup_info.window_name_ copy]),
+ show_popup_handler_([blocked_popup_info.show_popup_handler_ copy]) {
+}
+
+BlockedPopupInfo::~BlockedPopupInfo() {}
+
+void BlockedPopupInfo::ShowPopup() const {
+ show_popup_handler_();
+}
+
+void BlockedPopupInfo::operator=(const BlockedPopupInfo& blocked_popup_info) {
+ url_ = blocked_popup_info.url_;
+ referrer_ = blocked_popup_info.referrer_;
+ window_name_.reset([blocked_popup_info.window_name_ copy]);
+ show_popup_handler_ = [blocked_popup_info.show_popup_handler_ copy];
+}
+
+} // namespace web
diff --git a/ios/web/web_state/crw_recurring_task_delegate.h b/ios/web/web_state/crw_recurring_task_delegate.h
new file mode 100644
index 0000000..12d6fd8f
--- /dev/null
+++ b/ios/web/web_state/crw_recurring_task_delegate.h
@@ -0,0 +1,16 @@
+// Copyright 2012 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_WEB_WEB_STATE_CRW_RECURRING_TASK_DELEGATE_H_
+#define IOS_WEB_WEB_STATE_CRW_RECURRING_TASK_DELEGATE_H_
+
+// A delegate object to run some recurring task.
+@protocol CRWRecurringTaskDelegate
+
+// Runs a recurring task.
+- (void)runRecurringTask;
+
+@end
+
+#endif // IOS_WEB_WEB_STATE_CRW_RECURRING_TASK_DELEGATE_H_
diff --git a/ios/web/web_state/js/crw_js_message_dynamic_manager.mm b/ios/web/web_state/js/crw_js_message_dynamic_manager.mm
index 50059f0..9067a19 100644
--- a/ios/web/web_state/js/crw_js_message_dynamic_manager.mm
+++ b/ios/web/web_state/js/crw_js_message_dynamic_manager.mm
@@ -4,15 +4,20 @@
#import "ios/web/web_state/js/crw_js_message_dynamic_manager.h"
+#include "base/logging.h"
#import "ios/web/web_state/js/crw_js_common_manager.h"
-#include "ios/web/web_view_util.h"
@implementation CRWJSMessageDynamicManager
- (NSString*)scriptPath {
- if (web::IsWKWebViewEnabled())
- return @"message_dynamic_wk";
- return @"message_dynamic_ui";
+ switch ([[self receiver] webViewType]) {
+ case web::UI_WEB_VIEW_TYPE:
+ return @"message_dynamic_ui";
+ case web::WK_WEB_VIEW_TYPE:
+ return @"message_dynamic_wk";
+ }
+ NOTREACHED();
+ return nil;
}
- (NSString*)presenceBeacon {
diff --git a/ios/web/web_state/wk_web_view_ssl_error_util.h b/ios/web/web_state/wk_web_view_ssl_error_util.h
new file mode 100644
index 0000000..a9610e5
--- /dev/null
+++ b/ios/web/web_state/wk_web_view_ssl_error_util.h
@@ -0,0 +1,29 @@
+// 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_WEB_WEB_STATE_WK_WEB_VIEW_SSL_ERROR_UTIL_H_
+#define IOS_WEB_WEB_STATE_WK_WEB_VIEW_SSL_ERROR_UTIL_H_
+
+#import <Foundation/Foundation.h>
+
+namespace net {
+class SSLInfo;
+}
+
+namespace web {
+
+// NSErrorPeerCertificateChainKey from NSError's userInfo dict.
+extern NSString* const kNSErrorPeerCertificateChainKey;
+
+// Returns YES if geven error is a SSL error.
+BOOL IsWKWebViewSSLError(NSError* error);
+
+// Fills SSLInfo object with information extracted from |error|. Callers are
+// responsible to ensure that given |error| is an SSL error by calling
+// |web::IsSSLError| function.
+void GetSSLInfoFromWKWebViewSSLError(NSError* error, net::SSLInfo* ssl_info);
+
+} // namespace web
+
+#endif // IOS_WEB_WEB_STATE_WK_WEB_VIEW_SSL_ERROR_UTIL_H_
diff --git a/ios/web/web_state/wk_web_view_ssl_error_util.mm b/ios/web/web_state/wk_web_view_ssl_error_util.mm
new file mode 100644
index 0000000..18b38f8
--- /dev/null
+++ b/ios/web/web_state/wk_web_view_ssl_error_util.mm
@@ -0,0 +1,96 @@
+// 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/web/web_state/wk_web_view_ssl_error_util.h"
+
+#include "base/strings/sys_string_conversions.h"
+#include "net/cert/x509_certificate.h"
+#include "net/ssl/ssl_info.h"
+
+namespace web {
+
+// This key was determined by inspecting userInfo dict of an SSL error.
+NSString* const kNSErrorPeerCertificateChainKey =
+ @"NSErrorPeerCertificateChainKey";
+
+}
+
+namespace {
+
+// Creates certificate from subject string.
+net::X509Certificate* CreateCertFromSubject(NSString* subject) {
+ std::string issuer = "";
+ base::Time start_date;
+ base::Time expiration_date;
+ return new net::X509Certificate(base::SysNSStringToUTF8(subject),
+ issuer,
+ start_date,
+ expiration_date);
+}
+
+// Creates certificate from array of SecCertificateRef objects.
+net::X509Certificate* CreateCertFromChain(NSArray* certs) {
+ if (certs.count == 0)
+ return nullptr;
+ net::X509Certificate::OSCertHandles intermediates;
+ for (NSUInteger i = 1; i < certs.count; i++) {
+ intermediates.push_back(reinterpret_cast<SecCertificateRef>(certs[i]));
+ }
+ return net::X509Certificate::CreateFromHandle(
+ reinterpret_cast<SecCertificateRef>(certs[0]), intermediates);
+}
+
+// Creates certificate using information extracted from NSError.
+net::X509Certificate* CreateCertFromSSLError(NSError* error) {
+ net::X509Certificate* cert = CreateCertFromChain(
+ error.userInfo[web::kNSErrorPeerCertificateChainKey]);
+ if (cert)
+ return cert;
+ return CreateCertFromSubject(
+ error.userInfo[NSURLErrorFailingURLStringErrorKey]);
+}
+
+// Maps NSError code to net::CertStatus.
+net::CertStatus GetCertStatusFromNSErrorCode(NSInteger code) {
+ switch (code) {
+ // Regardless of real certificate problem the system always returns
+ // NSURLErrorServerCertificateUntrusted. The mapping is done in case this
+ // bug is fixed (rdar://18517043).
+ case NSURLErrorServerCertificateUntrusted:
+ case NSURLErrorSecureConnectionFailed:
+ case NSURLErrorServerCertificateHasUnknownRoot:
+ case NSURLErrorClientCertificateRejected:
+ case NSURLErrorClientCertificateRequired:
+ return net::CERT_STATUS_INVALID;
+ case NSURLErrorServerCertificateHasBadDate:
+ case NSURLErrorServerCertificateNotYetValid:
+ return net::CERT_STATUS_DATE_INVALID;
+ }
+ NOTREACHED();
+ return 0;
+}
+
+} // namespace
+
+
+namespace web {
+
+BOOL IsWKWebViewSSLError(NSError* error) {
+ // SSL errors range is (-2000..-1200], represented by kCFURLError constants:
+ // (kCFURLErrorCannotLoadFromNetwork..kCFURLErrorSecureConnectionFailed].
+ // It's reasonable to expect that all SSL errors will have the error code
+ // less or equal to NSURLErrorSecureConnectionFailed but greater than
+ // NSURLErrorCannotLoadFromNetwork.
+ return [error.domain isEqualToString:NSURLErrorDomain] &&
+ (error.code <= NSURLErrorSecureConnectionFailed &&
+ NSURLErrorCannotLoadFromNetwork < error.code);
+}
+
+void GetSSLInfoFromWKWebViewSSLError(NSError* error, net::SSLInfo* ssl_info) {
+ DCHECK(IsWKWebViewSSLError(error));
+ ssl_info->cert_status = GetCertStatusFromNSErrorCode(error.code);
+ ssl_info->cert = CreateCertFromSSLError(error);
+}
+
+} // namespace web
diff --git a/ios/web/web_view_util.h b/ios/web/web_view_util.h
index 5b58ad8..a625c57 100644
--- a/ios/web/web_view_util.h
+++ b/ios/web/web_view_util.h
@@ -7,19 +7,8 @@
namespace web {
-// Returns true if WKWebView is being used instead of UIWebView.
-// TODO(stuartmorgan): Eliminate this global flag in favor of a per-web-view
-// flag.
-bool IsWKWebViewEnabled();
-
-// If |flag| is true, causes IsWKWebViewEnabled to return false, even if
-// WKWebView is enabled using the compile time flag. Should only be called from
-// ScopedUIWebViewEnforcer for use in unit tests that need to test UIWebView
-// while WKWebView is enabled.
-void SetForceUIWebView(bool flag);
-
-// Returns true if use of UIWebView is to be enforced.
-bool GetForceUIWebView();
+// Returns true if WKWebView is supported on current OS/platform/arch.
+bool IsWKWebViewSupported();
} // web
diff --git a/ios/web/web_view_util.mm b/ios/web/web_view_util.mm
index d983306..6807d50 100644
--- a/ios/web/web_view_util.mm
+++ b/ios/web/web_view_util.mm
@@ -4,33 +4,39 @@
#include "ios/web/web_view_util.h"
-#include "base/ios/ios_util.h"
-
-namespace {
+#include <Foundation/Foundation.h>
+#include <sys/sysctl.h>
-// If true, UIWebView is always used even if WKWebView is available.
-bool g_force_ui_web_view = false;
-
-} // namespace
+#include "base/ios/ios_util.h"
namespace web {
-bool IsWKWebViewEnabled() {
-#if defined(ENABLE_WKWEBVIEW)
- // Eventually this may be a dynamic flag, but for now it's purely a
- // compile-time option.
- return !g_force_ui_web_view && base::ios::IsRunningOnIOS8OrLater();
+bool IsWKWebViewSupported() {
+ // WKWebView is available starting from iOS8.
+ if (!base::ios::IsRunningOnIOS8OrLater())
+ return false;
+
+// WKWebView does not work with 32-bit binaries running on 64-bit hardware.
+// (rdar://18268087)
+#if !defined(__LP64__)
+ NSString* simulator_model_id =
+ [[NSProcessInfo processInfo] environment][@"SIMULATOR_MODEL_IDENTIFIER"];
+ // For simulator it's not possible to query hw.cpu64bit_capable value as the
+ // function will return information for mac hardware rather than simulator.
+ if (simulator_model_id) {
+ // A set of known 32-bit simulators that are also iOS 8 compatible.
+ // (taken from iOS 8.1 SDK simulators list).
+ NSSet* known_32_bit_devices = [NSSet
+ setWithArray:@[ @"iPad2,1", @"iPad3,1", @"iPhone4,1", @"iPhone5,1" ]];
+ return [known_32_bit_devices containsObject:simulator_model_id];
+ }
+ uint32_t cpu64bit_capable = 0;
+ size_t sizes = sizeof(cpu64bit_capable);
+ sysctlbyname("hw.cpu64bit_capable", &cpu64bit_capable, &sizes, NULL, 0);
+ return !cpu64bit_capable;
#else
- return false;
+ return true;
#endif
}
-void SetForceUIWebView(bool flag) {
- g_force_ui_web_view = flag;
-}
-
-bool GetForceUIWebView() {
- return g_force_ui_web_view;
-}
-
} // namespace web