diff options
-rw-r--r-- | webkit/appcache/appcache.cc | 68 | ||||
-rw-r--r-- | webkit/appcache/appcache.h | 13 | ||||
-rw-r--r-- | webkit/appcache/appcache_database.cc | 81 | ||||
-rw-r--r-- | webkit/appcache/appcache_database.h | 10 | ||||
-rw-r--r-- | webkit/appcache/appcache_database_unittest.cc | 306 | ||||
-rw-r--r-- | webkit/appcache/appcache_interfaces.cc | 23 | ||||
-rw-r--r-- | webkit/appcache/appcache_interfaces.h | 9 | ||||
-rw-r--r-- | webkit/appcache/appcache_storage_impl.cc | 28 | ||||
-rw-r--r-- | webkit/appcache/appcache_storage_impl_unittest.cc | 229 | ||||
-rw-r--r-- | webkit/appcache/appcache_unittest.cc | 299 | ||||
-rw-r--r-- | webkit/appcache/appcache_update_job_unittest.cc | 15 | ||||
-rw-r--r-- | webkit/appcache/manifest_parser.cc | 30 | ||||
-rw-r--r-- | webkit/appcache/manifest_parser.h | 2 | ||||
-rw-r--r-- | webkit/appcache/manifest_parser_unittest.cc | 94 | ||||
-rw-r--r-- | webkit/appcache/mock_appcache_storage_unittest.cc | 9 |
15 files changed, 1042 insertions, 174 deletions
diff --git a/webkit/appcache/appcache.cc b/webkit/appcache/appcache.cc index c77320d..92d688d 100644 --- a/webkit/appcache/appcache.cc +++ b/webkit/appcache/appcache.cc @@ -128,21 +128,11 @@ void AppCache::InitializeWithDatabaseRecords( } DCHECK(cache_size_ == cache_record.cache_size); - for (size_t i = 0; i < intercepts.size(); ++i) { - const AppCacheDatabase::NamespaceRecord& intercept = intercepts.at(i); - intercept_namespaces_.push_back( - Namespace(INTERCEPT_NAMESPACE, - intercept.namespace_url, - intercept.target_url)); - } + for (size_t i = 0; i < intercepts.size(); ++i) + intercept_namespaces_.push_back(intercepts.at(i).namespace_); - for (size_t i = 0; i < fallbacks.size(); ++i) { - const AppCacheDatabase::NamespaceRecord& fallback = fallbacks.at(i); - fallback_namespaces_.push_back( - Namespace(FALLBACK_NAMESPACE, - fallback.namespace_url, - fallback.target_url)); - } + for (size_t i = 0; i < fallbacks.size(); ++i) + fallback_namespaces_.push_back(fallbacks.at(i).namespace_); // Sort the fallback namespaces by url string length, longest to shortest, // since longer matches trump when matching a url to a namespace. @@ -151,8 +141,14 @@ void AppCache::InitializeWithDatabaseRecords( std::sort(fallback_namespaces_.begin(), fallback_namespaces_.end(), SortNamespacesByLength); - for (size_t i = 0; i < whitelists.size(); ++i) - online_whitelist_namespaces_.push_back(whitelists.at(i).namespace_url); + for (size_t i = 0; i < whitelists.size(); ++i) { + const AppCacheDatabase::OnlineWhiteListRecord& record = whitelists.at(i); + online_whitelist_namespaces_.push_back( + Namespace(NETWORK_NAMESPACE, + record.namespace_url, + GURL(), + record.is_pattern)); + } } void AppCache::ToDatabaseRecords( @@ -190,9 +186,7 @@ void AppCache::ToDatabaseRecords( AppCacheDatabase::NamespaceRecord& record = intercepts->back(); record.cache_id = cache_id_; record.origin = origin; - record.type = INTERCEPT_NAMESPACE; - record.namespace_url = intercept_namespaces_[i].namespace_url; - record.target_url = intercept_namespaces_[i].target_url; + record.namespace_ = intercept_namespaces_[i]; } for (size_t i = 0; i < fallback_namespaces_.size(); ++i) { @@ -200,16 +194,15 @@ void AppCache::ToDatabaseRecords( AppCacheDatabase::NamespaceRecord& record = fallbacks->back(); record.cache_id = cache_id_; record.origin = origin; - record.type = FALLBACK_NAMESPACE; - record.namespace_url = fallback_namespaces_[i].namespace_url; - record.target_url = fallback_namespaces_[i].target_url; + record.namespace_ = fallback_namespaces_[i]; } for (size_t i = 0; i < online_whitelist_namespaces_.size(); ++i) { whitelists->push_back(AppCacheDatabase::OnlineWhiteListRecord()); AppCacheDatabase::OnlineWhiteListRecord& record = whitelists->back(); record.cache_id = cache_id_; - record.namespace_url = online_whitelist_namespaces_[i]; + record.namespace_url = online_whitelist_namespaces_[i].namespace_url; + record.is_pattern = online_whitelist_namespaces_[i].is_pattern; } } @@ -235,10 +228,8 @@ bool AppCache::FindResponseForRequest(const GURL& url, return true; } - if ((*found_network_namespace = - IsInNetworkNamespace(url_no_ref, online_whitelist_namespaces_))) { + if ((*found_network_namespace = IsInNetworkNamespace(url_no_ref))) return true; - } const Namespace* intercept_namespace = FindInterceptNamespace(url_no_ref); if (intercept_namespace) { @@ -262,17 +253,6 @@ bool AppCache::FindResponseForRequest(const GURL& url, return *found_network_namespace; } -const Namespace* AppCache::FindNamespace( - const NamespaceVector& namespaces, const GURL& url) { - size_t count = namespaces.size(); - for (size_t i = 0; i < count; ++i) { - if (StartsWithASCII( - url.spec(), namespaces[i].namespace_url.spec(), true)) { - return &namespaces[i]; - } - } - return NULL; -} void AppCache::ToResourceInfoVector(AppCacheResourceInfoVector* infos) const { DCHECK(infos && infos->empty()); @@ -293,17 +273,15 @@ void AppCache::ToResourceInfoVector(AppCacheResourceInfoVector* infos) const { } // static -bool AppCache::IsInNetworkNamespace( - const GURL& url, - const std::vector<GURL> &namespaces) { - // TODO(michaeln): There are certainly better 'prefix matching' - // structures and algorithms that can be applied here and above. +const Namespace* AppCache::FindNamespace( + const NamespaceVector& namespaces, + const GURL& url) { size_t count = namespaces.size(); for (size_t i = 0; i < count; ++i) { - if (StartsWithASCII(url.spec(), namespaces[i].spec(), true)) - return true; + if (namespaces[i].IsMatch(url)) + return &namespaces[i]; } - return false; + return NULL; } } // namespace appcache diff --git a/webkit/appcache/appcache.h b/webkit/appcache/appcache.h index bfc40ab..fee3169 100644 --- a/webkit/appcache/appcache.h +++ b/webkit/appcache/appcache.h @@ -117,9 +117,9 @@ class WEBKIT_STORAGE_EXPORT AppCache : public base::RefCounted<AppCache> { // Populates the 'infos' vector with an element per entry in the appcache. void ToResourceInfoVector(AppCacheResourceInfoVector* infos) const; - static bool IsInNetworkNamespace( - const GURL& url, - const std::vector<GURL> &namespaces); + static const Namespace* FindNamespace( + const NamespaceVector& namespaces, + const GURL& url); private: friend class AppCacheGroup; @@ -140,8 +140,9 @@ class WEBKIT_STORAGE_EXPORT AppCache : public base::RefCounted<AppCache> { const Namespace* FindFallbackNamespace(const GURL& url) { return FindNamespace(fallback_namespaces_, url); } - const Namespace* FindNamespace(const NamespaceVector& namespaces, - const GURL& url); + bool IsInNetworkNamespace(const GURL& url) { + return FindNamespace(online_whitelist_namespaces_, url) != NULL; + } GURL GetNamespaceEntryUrl(const NamespaceVector& namespaces, const GURL& namespace_url) const; @@ -160,7 +161,7 @@ class WEBKIT_STORAGE_EXPORT AppCache : public base::RefCounted<AppCache> { NamespaceVector intercept_namespaces_; NamespaceVector fallback_namespaces_; - std::vector<GURL> online_whitelist_namespaces_; + NamespaceVector online_whitelist_namespaces_; bool online_whitelist_all_; bool is_complete_; diff --git a/webkit/appcache/appcache_database.cc b/webkit/appcache/appcache_database.cc index 95b4309..3911831 100644 --- a/webkit/appcache/appcache_database.cc +++ b/webkit/appcache/appcache_database.cc @@ -18,8 +18,8 @@ // Schema ------------------------------------------------------------------- namespace { -const int kCurrentVersion = 4; -const int kCompatibleVersion = 4; +const int kCurrentVersion = 5; +const int kCompatibleVersion = 5; const char kGroupsTable[] = "Groups"; const char kCachesTable[] = "Caches"; @@ -67,11 +67,13 @@ const TableInfo kTables[] = { " origin TEXT," // intentionally not normalized " type INTEGER," " namespace_url TEXT," - " target_url TEXT)" }, + " target_url TEXT," + " is_pattern INTEGER CHECK(is_pattern IN (0, 1)))" }, { kOnlineWhiteListsTable, "(cache_id INTEGER," - " namespace_url TEXT)" }, + " namespace_url TEXT," + " is_pattern INTEGER CHECK(is_pattern IN (0, 1)))" }, { kDeletableResponseIdsTable, "(response_id INTEGER NOT NULL)" }, @@ -170,7 +172,7 @@ AppCacheDatabase::GroupRecord::~GroupRecord() { } AppCacheDatabase::NamespaceRecord::NamespaceRecord() - : cache_id(0), type(FALLBACK_NAMESPACE) { + : cache_id(0) { } AppCacheDatabase::NamespaceRecord::~NamespaceRecord() { @@ -645,7 +647,7 @@ bool AppCacheDatabase::FindNamespacesForOrigin( return false; const char* kSql = - "SELECT cache_id, origin, type, namespace_url, target_url" + "SELECT cache_id, origin, type, namespace_url, target_url, is_pattern" " FROM Namespaces WHERE origin = ?"; sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); @@ -666,7 +668,7 @@ bool AppCacheDatabase::FindNamespacesForCache( return false; const char* kSql = - "SELECT cache_id, origin, type, namespace_url, target_url" + "SELECT cache_id, origin, type, namespace_url, target_url, is_pattern" " FROM Namespaces WHERE cache_id = ?"; sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); @@ -684,16 +686,16 @@ bool AppCacheDatabase::InsertNamespace( const char* kSql = "INSERT INTO Namespaces" - " (cache_id, origin, type, namespace_url, target_url)" - " VALUES (?, ?, ?, ?, ?)"; + " (cache_id, origin, type, namespace_url, target_url, is_pattern)" + " VALUES (?, ?, ?, ?, ?, ?)"; sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); statement.BindInt64(0, record->cache_id); statement.BindString(1, record->origin.spec()); - statement.BindInt(2, record->type); - statement.BindString(3, record->namespace_url.spec()); - statement.BindString(4, record->target_url.spec()); - + statement.BindInt(2, record->namespace_.type); + statement.BindString(3, record->namespace_.namespace_url.spec()); + statement.BindString(4, record->namespace_.target_url.spec()); + statement.BindBool(5, record->namespace_.is_pattern); return statement.Run(); } @@ -733,7 +735,7 @@ bool AppCacheDatabase::FindOnlineWhiteListForCache( return false; const char* kSql = - "SELECT cache_id, namespace_url FROM OnlineWhiteLists" + "SELECT cache_id, namespace_url, is_pattern FROM OnlineWhiteLists" " WHERE cache_id = ?"; sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); @@ -753,11 +755,13 @@ bool AppCacheDatabase::InsertOnlineWhiteList( return false; const char* kSql = - "INSERT INTO OnlineWhiteLists (cache_id, namespace_url) VALUES (?, ?)"; + "INSERT INTO OnlineWhiteLists (cache_id, namespace_url, is_pattern)" + " VALUES (?, ?, ?)"; sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); statement.BindInt64(0, record->cache_id); statement.BindString(1, record->namespace_url.spec()); + statement.BindBool(2, record->is_pattern); return statement.Run(); } @@ -932,15 +936,17 @@ void AppCacheDatabase::ReadNamespaceRecord( const sql::Statement* statement, NamespaceRecord* record) { record->cache_id = statement->ColumnInt64(0); record->origin = GURL(statement->ColumnString(1)); - record->type = static_cast<NamespaceType>(statement->ColumnInt(2)); - record->namespace_url = GURL(statement->ColumnString(3)); - record->target_url = GURL(statement->ColumnString(4)); + record->namespace_.type = static_cast<NamespaceType>(statement->ColumnInt(2)); + record->namespace_.namespace_url = GURL(statement->ColumnString(3)); + record->namespace_.target_url = GURL(statement->ColumnString(4)); + record->namespace_.is_pattern = statement->ColumnBool(5); } void AppCacheDatabase::ReadOnlineWhiteListRecord( const sql::Statement& statement, OnlineWhiteListRecord* record) { record->cache_id = statement.ColumnInt64(0); record->namespace_url = GURL(statement.ColumnString(1)); + record->is_pattern = statement.ColumnBool(2); } bool AppCacheDatabase::LazyOpen(bool create_if_needed) { @@ -1046,15 +1052,26 @@ bool AppCacheDatabase::CreateSchema() { bool AppCacheDatabase::UpgradeSchema() { if (meta_table_->GetVersionNumber() == 3) { + // version 3 was pre 12/17/2011 DCHECK_EQ(strcmp(kNamespacesTable, kTables[3].table_name), 0); DCHECK_EQ(strcmp(kNamespacesTable, kIndexes[6].table_name), 0); DCHECK_EQ(strcmp(kNamespacesTable, kIndexes[7].table_name), 0); DCHECK_EQ(strcmp(kNamespacesTable, kIndexes[8].table_name), 0); - // Migrate from the old "FallbackNameSpaces" to the new "Namespaces" table. + const TableInfo kNamespaceTable_v4 = { + kNamespacesTable, + "(cache_id INTEGER," + " origin TEXT," // intentionally not normalized + " type INTEGER," + " namespace_url TEXT," + " target_url TEXT)" + }; + + // Migrate from the old FallbackNameSpaces to the newer Namespaces table, + // but without the is_pattern column added in v5. sql::Transaction transaction(db_.get()); if (!transaction.Begin() || - !CreateTable(db_.get(), kTables[3])) { + !CreateTable(db_.get(), kNamespaceTable_v4)) { return false; } @@ -1079,9 +1096,31 @@ bool AppCacheDatabase::UpgradeSchema() { return false; } - // Finally bump the version numbers and commit it. meta_table_->SetVersionNumber(4); meta_table_->SetCompatibleVersionNumber(4); + if (!transaction.Commit()) + return false; + } + + if (meta_table_->GetVersionNumber() == 4) { + // version 4 pre 3/30/2013 + // Add the is_pattern column to the Namespaces and OnlineWhitelists tables. + DCHECK_EQ(strcmp(kNamespacesTable, "Namespaces"), 0); + sql::Transaction transaction(db_.get()); + if (!transaction.Begin()) + return false; + if (!db_->Execute( + "ALTER TABLE Namespaces ADD COLUMN" + " is_pattern INTEGER CHECK(is_pattern IN (0, 1))")) { + return false; + } + if (!db_->Execute( + "ALTER TABLE OnlineWhitelists ADD COLUMN" + " is_pattern INTEGER CHECK(is_pattern IN (0, 1))")) { + return false; + } + meta_table_->SetVersionNumber(5); + meta_table_->SetCompatibleVersionNumber(5); return transaction.Commit(); } diff --git a/webkit/appcache/appcache_database.h b/webkit/appcache/appcache_database.h index 0eb0948..99154df 100644 --- a/webkit/appcache/appcache_database.h +++ b/webkit/appcache/appcache_database.h @@ -67,18 +67,17 @@ class WEBKIT_STORAGE_EXPORT AppCacheDatabase { int64 cache_id; GURL origin; - NamespaceType type; - GURL namespace_url; - GURL target_url; + Namespace namespace_; }; typedef std::vector<NamespaceRecord> NamespaceRecordVector; struct OnlineWhiteListRecord { - OnlineWhiteListRecord() : cache_id(0) {} + OnlineWhiteListRecord() : cache_id(0), is_pattern(false) {} int64 cache_id; GURL namespace_url; + bool is_pattern; }; explicit AppCacheDatabase(const base::FilePath& path); @@ -215,7 +214,8 @@ class WEBKIT_STORAGE_EXPORT AppCacheDatabase { FRIEND_TEST_ALL_PREFIXES(AppCacheDatabaseTest, ReCreate); FRIEND_TEST_ALL_PREFIXES(AppCacheDatabaseTest, DeletableResponseIds); FRIEND_TEST_ALL_PREFIXES(AppCacheDatabaseTest, OriginUsage); - FRIEND_TEST_ALL_PREFIXES(AppCacheDatabaseTest, UpgradeSchema3to4); + FRIEND_TEST_ALL_PREFIXES(AppCacheDatabaseTest, UpgradeSchema3to5); + FRIEND_TEST_ALL_PREFIXES(AppCacheDatabaseTest, UpgradeSchema4to5); DISALLOW_COPY_AND_ASSIGN(AppCacheDatabase); }; diff --git a/webkit/appcache/appcache_database_unittest.cc b/webkit/appcache/appcache_database_unittest.cc index 4c7affb..96627c7 100644 --- a/webkit/appcache/appcache_database_unittest.cc +++ b/webkit/appcache/appcache_database_unittest.cc @@ -360,15 +360,15 @@ TEST(AppCacheDatabaseTest, NamespaceRecords) { // Two records for two differenent caches in the Foo origin. record.cache_id = 1; record.origin = kFooOrigin; - record.namespace_url = kFooNameSpace1; - record.target_url = kFooFallbackEntry; + record.namespace_.namespace_url = kFooNameSpace1; + record.namespace_.target_url = kFooFallbackEntry; EXPECT_TRUE(db.InsertNamespace(&record)); EXPECT_FALSE(db.InsertNamespace(&record)); record.cache_id = 2; record.origin = kFooOrigin; - record.namespace_url = kFooNameSpace2; - record.target_url = kFooFallbackEntry; + record.namespace_.namespace_url = kFooNameSpace2; + record.namespace_.target_url = kFooFallbackEntry; EXPECT_TRUE(db.InsertNamespace(&record)); fallbacks.clear(); @@ -376,28 +376,32 @@ TEST(AppCacheDatabaseTest, NamespaceRecords) { EXPECT_EQ(1U, fallbacks.size()); EXPECT_EQ(1, fallbacks[0].cache_id); EXPECT_EQ(kFooOrigin, fallbacks[0].origin); - EXPECT_EQ(kFooNameSpace1, fallbacks[0].namespace_url); - EXPECT_EQ(kFooFallbackEntry, fallbacks[0].target_url); + EXPECT_EQ(kFooNameSpace1, fallbacks[0].namespace_.namespace_url); + EXPECT_EQ(kFooFallbackEntry, fallbacks[0].namespace_.target_url); + EXPECT_FALSE(fallbacks[0].namespace_.is_pattern); fallbacks.clear(); EXPECT_TRUE(db.FindNamespacesForCache(2, &intercepts, &fallbacks)); EXPECT_EQ(1U, fallbacks.size()); EXPECT_EQ(2, fallbacks[0].cache_id); EXPECT_EQ(kFooOrigin, fallbacks[0].origin); - EXPECT_EQ(kFooNameSpace2, fallbacks[0].namespace_url); - EXPECT_EQ(kFooFallbackEntry, fallbacks[0].target_url); + EXPECT_EQ(kFooNameSpace2, fallbacks[0].namespace_.namespace_url); + EXPECT_EQ(kFooFallbackEntry, fallbacks[0].namespace_.target_url); + EXPECT_FALSE(fallbacks[0].namespace_.is_pattern); fallbacks.clear(); EXPECT_TRUE(db.FindNamespacesForOrigin(kFooOrigin, &intercepts, &fallbacks)); EXPECT_EQ(2U, fallbacks.size()); EXPECT_EQ(1, fallbacks[0].cache_id); EXPECT_EQ(kFooOrigin, fallbacks[0].origin); - EXPECT_EQ(kFooNameSpace1, fallbacks[0].namespace_url); - EXPECT_EQ(kFooFallbackEntry, fallbacks[0].target_url); + EXPECT_EQ(kFooNameSpace1, fallbacks[0].namespace_.namespace_url); + EXPECT_EQ(kFooFallbackEntry, fallbacks[0].namespace_.target_url); + EXPECT_FALSE(fallbacks[0].namespace_.is_pattern); EXPECT_EQ(2, fallbacks[1].cache_id); EXPECT_EQ(kFooOrigin, fallbacks[1].origin); - EXPECT_EQ(kFooNameSpace2, fallbacks[1].namespace_url); - EXPECT_EQ(kFooFallbackEntry, fallbacks[1].target_url); + EXPECT_EQ(kFooNameSpace2, fallbacks[1].namespace_.namespace_url); + EXPECT_EQ(kFooFallbackEntry, fallbacks[1].namespace_.target_url); + EXPECT_FALSE(fallbacks[1].namespace_.is_pattern); EXPECT_TRUE(db.DeleteNamespacesForCache(1)); fallbacks.clear(); @@ -405,28 +409,36 @@ TEST(AppCacheDatabaseTest, NamespaceRecords) { EXPECT_EQ(1U, fallbacks.size()); EXPECT_EQ(2, fallbacks[0].cache_id); EXPECT_EQ(kFooOrigin, fallbacks[0].origin); - EXPECT_EQ(kFooNameSpace2, fallbacks[0].namespace_url); - EXPECT_EQ(kFooFallbackEntry, fallbacks[0].target_url); + EXPECT_EQ(kFooNameSpace2, fallbacks[0].namespace_.namespace_url); + EXPECT_EQ(kFooFallbackEntry, fallbacks[0].namespace_.target_url); + EXPECT_FALSE(fallbacks[0].namespace_.is_pattern); // Two more records for the same cache in the Bar origin. record.cache_id = 3; record.origin = kBarOrigin; - record.namespace_url = kBarNameSpace1; - record.target_url = kBarFallbackEntry; + record.namespace_.namespace_url = kBarNameSpace1; + record.namespace_.target_url = kBarFallbackEntry; + record.namespace_.is_pattern = true; EXPECT_TRUE(db.InsertNamespace(&record)); record.cache_id = 3; record.origin = kBarOrigin; - record.namespace_url = kBarNameSpace2; - record.target_url = kBarFallbackEntry; + record.namespace_.namespace_url = kBarNameSpace2; + record.namespace_.target_url = kBarFallbackEntry; + record.namespace_.is_pattern = true; EXPECT_TRUE(db.InsertNamespace(&record)); fallbacks.clear(); EXPECT_TRUE(db.FindNamespacesForCache(3, &intercepts, &fallbacks)); EXPECT_EQ(2U, fallbacks.size()); + EXPECT_TRUE(fallbacks[0].namespace_.is_pattern); + EXPECT_TRUE(fallbacks[1].namespace_.is_pattern); + fallbacks.clear(); EXPECT_TRUE(db.FindNamespacesForOrigin(kBarOrigin, &intercepts, &fallbacks)); EXPECT_EQ(2U, fallbacks.size()); + EXPECT_TRUE(fallbacks[0].namespace_.is_pattern); + EXPECT_TRUE(fallbacks[1].namespace_.is_pattern); } TEST(AppCacheDatabaseTest, OnlineWhiteListRecords) { @@ -453,14 +465,17 @@ TEST(AppCacheDatabaseTest, OnlineWhiteListRecords) { record.namespace_url = kFooNameSpace1; EXPECT_TRUE(db.InsertOnlineWhiteList(&record)); record.namespace_url = kFooNameSpace2; + record.is_pattern = true; EXPECT_TRUE(db.InsertOnlineWhiteList(&record)); records.clear(); EXPECT_TRUE(db.FindOnlineWhiteListForCache(1, &records)); EXPECT_EQ(2U, records.size()); EXPECT_EQ(1, records[0].cache_id); EXPECT_EQ(kFooNameSpace1, records[0].namespace_url); + EXPECT_FALSE(records[0].is_pattern); EXPECT_EQ(1, records[1].cache_id); EXPECT_EQ(kFooNameSpace2, records[1].namespace_url); + EXPECT_TRUE(records[1].is_pattern); record.cache_id = 2; record.namespace_url = kBarNameSpace1; @@ -617,11 +632,11 @@ TEST(AppCacheDatabaseTest, OriginUsage) { EXPECT_EQ(5000, usage_map[kOtherOrigin]); } -TEST(AppCacheDatabaseTest, UpgradeSchema3to4) { +TEST(AppCacheDatabaseTest, UpgradeSchema3to5) { // Real file on disk for this test. base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - const base::FilePath kDbFile = temp_dir.path().AppendASCII("upgrade.db"); + const base::FilePath kDbFile = temp_dir.path().AppendASCII("upgrade3.db"); const GURL kMockOrigin("http://mockorigin/"); const char kNamespaceUrlFormat[] = "namespace%d"; @@ -808,9 +823,11 @@ TEST(AppCacheDatabaseTest, UpgradeSchema3to4) { EXPECT_TRUE(db.db_->DoesIndexExist("NamespacesCacheIndex")); EXPECT_TRUE(db.db_->DoesIndexExist("NamespacesOriginIndex")); EXPECT_TRUE(db.db_->DoesIndexExist("NamespacesCacheAndUrlIndex")); + EXPECT_TRUE(db.db_->DoesColumnExist("Namespaces", "is_pattern")); + EXPECT_TRUE(db.db_->DoesColumnExist("OnlineWhiteLists", "is_pattern")); - EXPECT_EQ(4, db.meta_table_->GetVersionNumber()); - EXPECT_EQ(4, db.meta_table_->GetCompatibleVersionNumber()); + EXPECT_EQ(5, db.meta_table_->GetVersionNumber()); + EXPECT_EQ(5, db.meta_table_->GetCompatibleVersionNumber()); std::vector<AppCacheDatabase::NamespaceRecord> intercepts; std::vector<AppCacheDatabase::NamespaceRecord> fallbacks; @@ -826,10 +843,249 @@ TEST(AppCacheDatabaseTest, UpgradeSchema3to4) { kMockOrigin.Resolve(base::StringPrintf(kTargetUrlFormat, i))); EXPECT_EQ(i, fallbacks[i].cache_id); - EXPECT_EQ(FALLBACK_NAMESPACE, fallbacks[i].type); + EXPECT_EQ(FALLBACK_NAMESPACE, fallbacks[i].namespace_.type); + EXPECT_EQ(kMockOrigin, fallbacks[i].origin); + EXPECT_EQ(expected_namespace_url, fallbacks[i].namespace_.namespace_url); + EXPECT_EQ(expected_target_url, fallbacks[i].namespace_.target_url); + EXPECT_FALSE(fallbacks[i].namespace_.is_pattern); + } +} + +TEST(AppCacheDatabaseTest, UpgradeSchema4to5) { + // Real file on disk for this test. + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + const base::FilePath kDbFile = temp_dir.path().AppendASCII("upgrade4.db"); + + const GURL kMockOrigin("http://mockorigin/"); + const char kNamespaceUrlFormat[] = "namespace%d"; + const char kWhitelistUrlFormat[] = "whitelist%d"; + const char kTargetUrlFormat[] = "target%d"; + const int kNumNamespaces = 10; + const int kWhitelistCacheId = 1; + + // Create a v4 schema based database containing some fallback records. + { + const int kVersion4 = 4; + const char kGroupsTable[] = "Groups"; + const char kCachesTable[] = "Caches"; + const char kEntriesTable[] = "Entries"; + const char kNamespacesTable[] = "Namespaces"; + const char kOnlineWhiteListsTable[] = "OnlineWhiteLists"; + const char kDeletableResponseIdsTable[] = "DeletableResponseIds"; + + struct TableInfo { + const char* table_name; + const char* columns; + }; + + struct IndexInfo { + const char* index_name; + const char* table_name; + const char* columns; + bool unique; + }; + + const TableInfo kTables4[] = { + { kGroupsTable, + "(group_id INTEGER PRIMARY KEY," + " origin TEXT," + " manifest_url TEXT," + " creation_time INTEGER," + " last_access_time INTEGER)" }, + + { kCachesTable, + "(cache_id INTEGER PRIMARY KEY," + " group_id INTEGER," + " online_wildcard INTEGER CHECK(online_wildcard IN (0, 1))," + " update_time INTEGER," + " cache_size INTEGER)" }, // intentionally not normalized + + { kEntriesTable, + "(cache_id INTEGER," + " url TEXT," + " flags INTEGER," + " response_id INTEGER," + " response_size INTEGER)" }, + + { kNamespacesTable, + "(cache_id INTEGER," + " origin TEXT," // intentionally not normalized + " type INTEGER," + " namespace_url TEXT," + " target_url TEXT)" }, + + { kOnlineWhiteListsTable, + "(cache_id INTEGER," + " namespace_url TEXT)" }, + + { kDeletableResponseIdsTable, + "(response_id INTEGER NOT NULL)" }, + }; + + const IndexInfo kIndexes4[] = { + { "GroupsOriginIndex", + kGroupsTable, + "(origin)", + false }, + + { "GroupsManifestIndex", + kGroupsTable, + "(manifest_url)", + true }, + + { "CachesGroupIndex", + kCachesTable, + "(group_id)", + false }, + + { "EntriesCacheIndex", + kEntriesTable, + "(cache_id)", + false }, + + { "EntriesCacheAndUrlIndex", + kEntriesTable, + "(cache_id, url)", + true }, + + { "EntriesResponseIdIndex", + kEntriesTable, + "(response_id)", + true }, + + { "NamespacesCacheIndex", + kNamespacesTable, + "(cache_id)", + false }, + + { "NamespacesOriginIndex", + kNamespacesTable, + "(origin)", + false }, + + { "NamespacesCacheAndUrlIndex", + kNamespacesTable, + "(cache_id, namespace_url)", + true }, + + { "OnlineWhiteListCacheIndex", + kOnlineWhiteListsTable, + "(cache_id)", + false }, + + { "DeletableResponsesIdIndex", + kDeletableResponseIdsTable, + "(response_id)", + true }, + }; + + const int kTableCount4 = ARRAYSIZE_UNSAFE(kTables4); + const int kIndexCount4 = ARRAYSIZE_UNSAFE(kIndexes4); + + sql::Connection connection; + EXPECT_TRUE(connection.Open(kDbFile)); + + sql::Transaction transaction(&connection); + EXPECT_TRUE(transaction.Begin()); + + sql::MetaTable meta_table; + EXPECT_TRUE(meta_table.Init(&connection, kVersion4, kVersion4)); + + for (int i = 0; i < kTableCount4; ++i) { + std::string sql("CREATE TABLE "); + sql += kTables4[i].table_name; + sql += kTables4[i].columns; + EXPECT_TRUE(connection.Execute(sql.c_str())); + } + + for (int i = 0; i < kIndexCount4; ++i) { + std::string sql; + if (kIndexes4[i].unique) + sql += "CREATE UNIQUE INDEX "; + else + sql += "CREATE INDEX "; + sql += kIndexes4[i].index_name; + sql += " ON "; + sql += kIndexes4[i].table_name; + sql += kIndexes4[i].columns; + EXPECT_TRUE(connection.Execute(sql.c_str())); + } + + const char* kNamespacesSql = + "INSERT INTO Namespaces" + " (cache_id, origin, type, namespace_url, target_url)" + " VALUES (?, ?, ?, ?, ?)"; + sql::Statement statement; + statement.Assign(connection.GetUniqueStatement(kNamespacesSql)); + EXPECT_TRUE(statement.is_valid()); + for (int i = 0; i < kNumNamespaces; ++i) { + GURL namespace_url( + kMockOrigin.Resolve(base::StringPrintf(kNamespaceUrlFormat, i))); + GURL target_url( + kMockOrigin.Resolve(base::StringPrintf(kTargetUrlFormat, i))); + statement.BindInt64(0, i); + statement.BindString(1, kMockOrigin.spec().c_str()); + statement.BindInt(2, FALLBACK_NAMESPACE); + statement.BindString(3, namespace_url.spec().c_str()); + statement.BindString(4, target_url.spec().c_str()); + ASSERT_TRUE(statement.Run()); + statement.Reset(true); + } + + const char* kWhitelistsSql = + "INSERT INTO OnlineWhiteLists" + " (cache_id, namespace_url)" + " VALUES (?, ?)"; + statement.Assign(connection.GetUniqueStatement(kWhitelistsSql)); + EXPECT_TRUE(statement.is_valid()); + for (int i = 0; i < kNumNamespaces; ++i) { + GURL namespace_url( + kMockOrigin.Resolve(base::StringPrintf(kWhitelistUrlFormat, i))); + statement.BindInt64(0, kWhitelistCacheId); + statement.BindString(1, namespace_url.spec().c_str()); + ASSERT_TRUE(statement.Run()); + statement.Reset(true); + } + + EXPECT_TRUE(transaction.Commit()); + } + + // Open that database and verify that it got upgraded to v5. + AppCacheDatabase db(kDbFile); + EXPECT_TRUE(db.LazyOpen(true)); + EXPECT_TRUE(db.db_->DoesColumnExist("Namespaces", "is_pattern")); + EXPECT_TRUE(db.db_->DoesColumnExist("OnlineWhiteLists", "is_pattern")); + EXPECT_EQ(5, db.meta_table_->GetVersionNumber()); + EXPECT_EQ(5, db.meta_table_->GetCompatibleVersionNumber()); + + std::vector<AppCacheDatabase::NamespaceRecord> intercepts; + std::vector<AppCacheDatabase::NamespaceRecord> fallbacks; + EXPECT_TRUE(db.FindNamespacesForOrigin(kMockOrigin, &intercepts, + &fallbacks)); + EXPECT_TRUE(intercepts.empty()); + EXPECT_EQ(kNumNamespaces, static_cast<int>(fallbacks.size())); + + std::vector<AppCacheDatabase::OnlineWhiteListRecord> whitelists; + EXPECT_TRUE(db.FindOnlineWhiteListForCache(kWhitelistCacheId, &whitelists)); + EXPECT_EQ(kNumNamespaces, static_cast<int>(whitelists.size())); + + for (int i = 0; i < kNumNamespaces; ++i) { + GURL expected_namespace_url( + kMockOrigin.Resolve(base::StringPrintf(kNamespaceUrlFormat, i))); + GURL expected_target_url( + kMockOrigin.Resolve(base::StringPrintf(kTargetUrlFormat, i))); + GURL expected_whitelist_url( + kMockOrigin.Resolve(base::StringPrintf(kWhitelistUrlFormat, i))); + + EXPECT_EQ(i, fallbacks[i].cache_id); + EXPECT_EQ(FALLBACK_NAMESPACE, fallbacks[i].namespace_.type); EXPECT_EQ(kMockOrigin, fallbacks[i].origin); - EXPECT_EQ(expected_namespace_url, fallbacks[i].namespace_url); - EXPECT_EQ(expected_target_url, fallbacks[i].target_url); + EXPECT_EQ(expected_namespace_url, fallbacks[i].namespace_.namespace_url); + EXPECT_EQ(expected_target_url, fallbacks[i].namespace_.target_url); + EXPECT_FALSE(fallbacks[i].namespace_.is_pattern); + EXPECT_EQ(expected_whitelist_url, whitelists[i].namespace_url); + EXPECT_FALSE(whitelists[i].is_pattern); } } diff --git a/webkit/appcache/appcache_interfaces.cc b/webkit/appcache/appcache_interfaces.cc index bbc222c..a3b6f31 100644 --- a/webkit/appcache/appcache_interfaces.cc +++ b/webkit/appcache/appcache_interfaces.cc @@ -4,6 +4,7 @@ #include "webkit/appcache/appcache_interfaces.h" +#include "base/string_util.h" #include "googleurl/src/gurl.h" #include "net/url_request/url_request.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebApplicationCacheHost.h" @@ -49,16 +50,32 @@ AppCacheResourceInfo::~AppCacheResourceInfo() { } Namespace::Namespace() - : type(FALLBACK_NAMESPACE) { + : type(FALLBACK_NAMESPACE), + is_pattern(false) { } -Namespace::Namespace(NamespaceType type, const GURL& url, const GURL& target) - : type(type), namespace_url(url), target_url(target) { +Namespace::Namespace( + NamespaceType type, const GURL& url, const GURL& target, bool is_pattern) + : type(type), + namespace_url(url), + target_url(target), + is_pattern(is_pattern) { } Namespace::~Namespace() { } +bool Namespace::IsMatch(const GURL& url) const { + if (is_pattern) { + // We have to escape '?' characters since MatchPattern also treats those + // as wildcards which we don't want here, we only do '*'s. + std::string pattern = namespace_url.spec(); + if (namespace_url.has_query()) + ReplaceSubstringsAfterOffset(&pattern, 0, "?", "\\?"); + return MatchPattern(url.spec(), pattern); + } + return StartsWithASCII(url.spec(), namespace_url.spec(), true); +} bool IsSchemeSupported(const GURL& url) { bool supported = url.SchemeIs(kHttpScheme) || url.SchemeIs(kHttpsScheme); diff --git a/webkit/appcache/appcache_interfaces.h b/webkit/appcache/appcache_interfaces.h index 4fc7a56..b45ee8f 100644 --- a/webkit/appcache/appcache_interfaces.h +++ b/webkit/appcache/appcache_interfaces.h @@ -57,7 +57,8 @@ enum LogLevel { enum NamespaceType { FALLBACK_NAMESPACE, - INTERCEPT_NAMESPACE + INTERCEPT_NAMESPACE, + NETWORK_NAMESPACE }; struct WEBKIT_STORAGE_EXPORT AppCacheInfo { @@ -97,12 +98,16 @@ typedef std::vector<AppCacheResourceInfo> AppCacheResourceInfoVector; struct WEBKIT_STORAGE_EXPORT Namespace { Namespace(); // Type is set to FALLBACK_NAMESPACE by default. - Namespace(NamespaceType type, const GURL& url, const GURL& target); + Namespace(NamespaceType type, const GURL& url, const GURL& target, + bool is_pattern); ~Namespace(); + bool IsMatch(const GURL& url) const; + NamespaceType type; GURL namespace_url; GURL target_url; + bool is_pattern; }; typedef std::vector<Namespace> NamespaceVector; diff --git a/webkit/appcache/appcache_storage_impl.cc b/webkit/appcache/appcache_storage_impl.cc index 688542e..8fe803c 100644 --- a/webkit/appcache/appcache_storage_impl.cc +++ b/webkit/appcache/appcache_storage_impl.cc @@ -811,7 +811,8 @@ class SortByCachePreference bool SortByLength( const AppCacheDatabase::NamespaceRecord& lhs, const AppCacheDatabase::NamespaceRecord& rhs) { - return lhs.namespace_url.spec().length() > rhs.namespace_url.spec().length(); + return lhs.namespace_.namespace_url.spec().length() > + rhs.namespace_.namespace_url.spec().length(); } class NetworkNamespaceHelper { @@ -821,19 +822,18 @@ class NetworkNamespaceHelper { } bool IsInNetworkNamespace(const GURL& url, int64 cache_id) { - const std::vector<GURL> kEmptyVector; typedef std::pair<WhiteListMap::iterator, bool> InsertResult; InsertResult result = namespaces_map_.insert( - WhiteListMap::value_type(cache_id, kEmptyVector)); + WhiteListMap::value_type(cache_id, NamespaceVector())); if (result.second) GetOnlineWhiteListForCache(cache_id, &result.first->second); - return AppCache::IsInNetworkNamespace(url, result.first->second); + return AppCache::FindNamespace(result.first->second, url) != NULL; } private: void GetOnlineWhiteListForCache( - int64 cache_id, std::vector<GURL>* urls) { - DCHECK(urls && urls->empty()); + int64 cache_id, NamespaceVector* namespaces) { + DCHECK(namespaces && namespaces->empty()); typedef std::vector<AppCacheDatabase::OnlineWhiteListRecord> WhiteListVector; WhiteListVector records; @@ -841,13 +841,15 @@ class NetworkNamespaceHelper { return; WhiteListVector::const_iterator iter = records.begin(); while (iter != records.end()) { - urls->push_back(iter->namespace_url); + namespaces->push_back( + Namespace(NETWORK_NAMESPACE, iter->namespace_url, GURL(), + iter->is_pattern)); ++iter; } } // Key is cache id - typedef std::map<int64, std::vector<GURL> > WhiteListMap; + typedef std::map<int64, NamespaceVector> WhiteListMap; WhiteListMap namespaces_map_; AppCacheDatabase* database_; }; @@ -1009,8 +1011,8 @@ FindMainResponseTask::FindNamespaceHelper( NamespaceRecordPtrVector other_namespaces; std::vector<AppCacheDatabase::NamespaceRecord>::iterator iter; for (iter = namespaces->begin(); iter < namespaces->end(); ++iter) { - // Skip those that aren't a prefix match. - if (!StartsWithASCII(url_.spec(), iter->namespace_url.spec(), true)) + // Skip those that aren't a match. + if (!iter->namespace_.IsMatch(url_)) continue; // Skip namespaces where the requested url falls into a network @@ -1043,7 +1045,7 @@ FindMainResponseTask::FindFirstValidNamespace( NamespaceRecordPtrVector::const_iterator iter; for (iter = namespaces.begin(); iter < namespaces.end(); ++iter) { AppCacheDatabase::EntryRecord entry_record; - if (database_->FindEntry((*iter)->cache_id, (*iter)->target_url, + if (database_->FindEntry((*iter)->cache_id, (*iter)->namespace_.target_url, &entry_record)) { AppCacheDatabase::GroupRecord group_record; if ((entry_record.flags & AppCacheEntry::FOREIGN) || @@ -1053,8 +1055,8 @@ FindMainResponseTask::FindFirstValidNamespace( manifest_url_ = group_record.manifest_url; group_id_ = group_record.group_id; cache_id_ = (*iter)->cache_id; - namespace_entry_url_ = (*iter)->target_url; - if ((*iter)->type == FALLBACK_NAMESPACE) + namespace_entry_url_ = (*iter)->namespace_.target_url; + if ((*iter)->namespace_.type == FALLBACK_NAMESPACE) fallback_entry_ = AppCacheEntry(entry_record.flags, entry_record.response_id); else diff --git a/webkit/appcache/appcache_storage_impl_unittest.cc b/webkit/appcache/appcache_storage_impl_unittest.cc index d419794..1c6d976 100644 --- a/webkit/appcache/appcache_storage_impl_unittest.cc +++ b/webkit/appcache/appcache_storage_impl_unittest.cc @@ -39,6 +39,16 @@ const GURL kOnlineNamespaceWithinFallback( const GURL kInterceptNamespace("http://blah/intercept_namespace/"); const GURL kInterceptNamespace2("http://blah/intercept_namespace/longer/"); const GURL kInterceptTestUrl("http://blah/intercept_namespace/longer/test"); +const GURL kInterceptPatternNamespace("http://blah/intercept_pattern/*/bar"); +const GURL kInterceptPatternTestPositiveUrl( + "http://blah/intercept_pattern/foo/bar"); +const GURL kInterceptPatternTestNegativeUrl( + "http://blah/intercept_pattern/foo/not_bar"); +const GURL kFallbackPatternNamespace("http://blah/fallback_pattern/*/bar"); +const GURL kFallbackPatternTestPositiveUrl( + "http://blah/fallback_pattern/foo/bar"); +const GURL kFallbackPatternTestNegativeUrl( + "http://blah/fallback_pattern/foo/not_bar"); const GURL kOrigin(kManifestUrl.GetOrigin()); const int kManifestEntryIdOffset = 100; @@ -676,8 +686,8 @@ class AppCacheStorageImplTest : public testing::Test { AppCacheDatabase::NamespaceRecord fallback_namespace_record; fallback_namespace_record.cache_id = 1; - fallback_namespace_record.target_url = kEntryUrl; - fallback_namespace_record.namespace_url = kFallbackNamespace; + fallback_namespace_record.namespace_.target_url = kEntryUrl; + fallback_namespace_record.namespace_.namespace_url = kFallbackNamespace; fallback_namespace_record.origin = kManifestUrl.GetOrigin(); EXPECT_TRUE(database()->InsertNamespace(&fallback_namespace_record)); @@ -896,9 +906,9 @@ class AppCacheStorageImplTest : public testing::Test { cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::FALLBACK, 1)); cache_->AddEntry(kEntryUrl2, AppCacheEntry(AppCacheEntry::FALLBACK, 2)); cache_->fallback_namespaces_.push_back( - Namespace(FALLBACK_NAMESPACE, kFallbackNamespace2, kEntryUrl2)); + Namespace(FALLBACK_NAMESPACE, kFallbackNamespace2, kEntryUrl2, false)); cache_->fallback_namespaces_.push_back( - Namespace(FALLBACK_NAMESPACE, kFallbackNamespace, kEntryUrl)); + Namespace(FALLBACK_NAMESPACE, kFallbackNamespace, kEntryUrl, false)); AppCacheDatabase::CacheRecord cache_record; std::vector<AppCacheDatabase::EntryRecord> entries; std::vector<AppCacheDatabase::NamespaceRecord> intercepts; @@ -964,9 +974,11 @@ class AppCacheStorageImplTest : public testing::Test { cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::INTERCEPT, 1)); cache_->AddEntry(kEntryUrl2, AppCacheEntry(AppCacheEntry::INTERCEPT, 2)); cache_->intercept_namespaces_.push_back( - Namespace(INTERCEPT_NAMESPACE, kInterceptNamespace2, kEntryUrl2)); + Namespace(INTERCEPT_NAMESPACE, kInterceptNamespace2, + kEntryUrl2, false)); cache_->intercept_namespaces_.push_back( - Namespace(INTERCEPT_NAMESPACE, kInterceptNamespace, kEntryUrl)); + Namespace(INTERCEPT_NAMESPACE, kInterceptNamespace, + kEntryUrl, false)); AppCacheDatabase::CacheRecord cache_record; std::vector<AppCacheDatabase::EntryRecord> entries; std::vector<AppCacheDatabase::NamespaceRecord> intercepts; @@ -1011,6 +1023,171 @@ class AppCacheStorageImplTest : public testing::Test { EXPECT_FALSE(delegate()->found_fallback_entry_.has_response_id()); TestFinished(); } + + // FindInterceptPatternMatch ---------------------------------------- + + void FindInterceptPatternMatchInDatabase() { + FindInterceptPatternMatch(true); + } + + void FindInterceptPatternMatchInWorkingSet() { + FindInterceptPatternMatch(false); + } + + void FindInterceptPatternMatch(bool drop_from_working_set) { + // Setup some preconditions. Create a complete cache with an + // pattern matching intercept namespace and entry. + MakeCacheAndGroup(kManifestUrl, 2, 1, true); + cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::INTERCEPT, 1)); + cache_->intercept_namespaces_.push_back( + Namespace(INTERCEPT_NAMESPACE, kInterceptPatternNamespace, + kEntryUrl, true)); + AppCacheDatabase::CacheRecord cache_record; + std::vector<AppCacheDatabase::EntryRecord> entries; + std::vector<AppCacheDatabase::NamespaceRecord> intercepts; + std::vector<AppCacheDatabase::NamespaceRecord> fallbacks; + std::vector<AppCacheDatabase::OnlineWhiteListRecord> whitelists; + cache_->ToDatabaseRecords(group_, + &cache_record, &entries, &intercepts, &fallbacks, &whitelists); + + std::vector<AppCacheDatabase::EntryRecord>::const_iterator iter = + entries.begin(); + while (iter != entries.end()) { + // MakeCacheAndGroup has inserted the default entry record already + if (iter->url != kDefaultEntryUrl) + EXPECT_TRUE(database()->InsertEntry(&(*iter))); + ++iter; + } + + EXPECT_TRUE(database()->InsertNamespaceRecords(intercepts)); + if (drop_from_working_set) { + EXPECT_TRUE(cache_->HasOneRef()); + cache_ = NULL; + EXPECT_TRUE(group_->HasOneRef()); + group_ = NULL; + } + + // First test something that does not match the pattern. + PushNextTask(base::Bind( + &AppCacheStorageImplTest::Verify_FindInterceptPatternMatchNegative, + base::Unretained(this))); + storage()->FindResponseForMainRequest( + kInterceptPatternTestNegativeUrl, GURL(), delegate()); + EXPECT_EQ(GURL(), delegate()->found_url_); // Is always async. + } + + void Verify_FindInterceptPatternMatchNegative() { + EXPECT_EQ(kInterceptPatternTestNegativeUrl, delegate()->found_url_); + EXPECT_TRUE(delegate()->found_manifest_url_.is_empty()); + EXPECT_EQ(kNoCacheId, delegate()->found_cache_id_); + EXPECT_EQ(kNoResponseId, delegate()->found_entry_.response_id()); + EXPECT_EQ(kNoResponseId, delegate()->found_fallback_entry_.response_id()); + EXPECT_TRUE(delegate()->found_namespace_entry_url_.is_empty()); + EXPECT_EQ(0, delegate()->found_entry_.types()); + EXPECT_EQ(0, delegate()->found_fallback_entry_.types()); + + // Then test something that matches. + PushNextTask(base::Bind( + &AppCacheStorageImplTest::Verify_FindInterceptPatternMatchPositive, + base::Unretained(this))); + storage()->FindResponseForMainRequest( + kInterceptPatternTestPositiveUrl, GURL(), delegate()); + } + + void Verify_FindInterceptPatternMatchPositive() { + EXPECT_EQ(kInterceptPatternTestPositiveUrl, delegate()->found_url_); + EXPECT_EQ(kManifestUrl, delegate()->found_manifest_url_); + EXPECT_EQ(1, delegate()->found_cache_id_); + EXPECT_EQ(2, delegate()->found_group_id_); + EXPECT_EQ(1, delegate()->found_entry_.response_id()); + EXPECT_TRUE(delegate()->found_entry_.IsIntercept()); + EXPECT_EQ(kEntryUrl, delegate()->found_namespace_entry_url_); + EXPECT_FALSE(delegate()->found_fallback_entry_.has_response_id()); + TestFinished(); + } + + // FindFallbackPatternMatch ------------------------------- + + void FindFallbackPatternMatchInDatabase() { + FindFallbackPatternMatch(true); + } + + void FindFallbackPatternMatchInWorkingSet() { + FindFallbackPatternMatch(false); + } + + void FindFallbackPatternMatch(bool drop_from_working_set) { + // Setup some preconditions. Create a complete cache with a + // pattern matching fallback namespace and entry. + MakeCacheAndGroup(kManifestUrl, 2, 1, true); + cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::FALLBACK, 1)); + cache_->fallback_namespaces_.push_back( + Namespace(FALLBACK_NAMESPACE, kFallbackPatternNamespace, + kEntryUrl, true)); + AppCacheDatabase::CacheRecord cache_record; + std::vector<AppCacheDatabase::EntryRecord> entries; + std::vector<AppCacheDatabase::NamespaceRecord> intercepts; + std::vector<AppCacheDatabase::NamespaceRecord> fallbacks; + std::vector<AppCacheDatabase::OnlineWhiteListRecord> whitelists; + cache_->ToDatabaseRecords(group_, + &cache_record, &entries, &intercepts, &fallbacks, &whitelists); + + std::vector<AppCacheDatabase::EntryRecord>::const_iterator iter = + entries.begin(); + while (iter != entries.end()) { + // MakeCacheAndGroup has inserted the default entry record already. + if (iter->url != kDefaultEntryUrl) + EXPECT_TRUE(database()->InsertEntry(&(*iter))); + ++iter; + } + + EXPECT_TRUE(database()->InsertNamespaceRecords(fallbacks)); + if (drop_from_working_set) { + EXPECT_TRUE(cache_->HasOneRef()); + cache_ = NULL; + EXPECT_TRUE(group_->HasOneRef()); + group_ = NULL; + } + + // First test something that does not match the pattern. + PushNextTask(base::Bind( + &AppCacheStorageImplTest::Verify_FindFallbackPatternMatchNegative, + base::Unretained(this))); + storage()->FindResponseForMainRequest( + kFallbackPatternTestNegativeUrl, GURL(), delegate()); + EXPECT_EQ(GURL(), delegate()->found_url_); // Is always async. + } + + void Verify_FindFallbackPatternMatchNegative() { + EXPECT_EQ(kFallbackPatternTestNegativeUrl, delegate()->found_url_); + EXPECT_TRUE(delegate()->found_manifest_url_.is_empty()); + EXPECT_EQ(kNoCacheId, delegate()->found_cache_id_); + EXPECT_EQ(kNoResponseId, delegate()->found_entry_.response_id()); + EXPECT_EQ(kNoResponseId, delegate()->found_fallback_entry_.response_id()); + EXPECT_TRUE(delegate()->found_namespace_entry_url_.is_empty()); + EXPECT_EQ(0, delegate()->found_entry_.types()); + EXPECT_EQ(0, delegate()->found_fallback_entry_.types()); + + // Then test something that matches. + PushNextTask(base::Bind( + &AppCacheStorageImplTest::Verify_FindFallbackPatternMatchPositive, + base::Unretained(this))); + storage()->FindResponseForMainRequest( + kFallbackPatternTestPositiveUrl, GURL(), delegate()); + } + + void Verify_FindFallbackPatternMatchPositive() { + EXPECT_EQ(kFallbackPatternTestPositiveUrl, delegate()->found_url_); + EXPECT_EQ(kManifestUrl, delegate()->found_manifest_url_); + EXPECT_EQ(1, delegate()->found_cache_id_); + EXPECT_EQ(2, delegate()->found_group_id_); + EXPECT_EQ(1, delegate()->found_fallback_entry_.response_id()); + EXPECT_TRUE(delegate()->found_fallback_entry_.IsFallback()); + EXPECT_EQ(kEntryUrl, delegate()->found_namespace_entry_url_); + EXPECT_FALSE(delegate()->found_entry_.has_response_id()); + TestFinished(); + } + // FindMainResponseWithMultipleHits ------------------------------- void FindMainResponseWithMultipleHits() { @@ -1066,12 +1243,12 @@ class AppCacheStorageImplTest : public testing::Test { AppCacheEntry(entry_record.flags, entry_record.response_id)); AppCacheDatabase::NamespaceRecord fallback_namespace_record; fallback_namespace_record.cache_id = id; - fallback_namespace_record.target_url = entry_record.url; - fallback_namespace_record.namespace_url = kFallbackNamespace; + fallback_namespace_record.namespace_.target_url = entry_record.url; + fallback_namespace_record.namespace_.namespace_url = kFallbackNamespace; fallback_namespace_record.origin = manifest_url.GetOrigin(); EXPECT_TRUE(database()->InsertNamespace(&fallback_namespace_record)); cache_->fallback_namespaces_.push_back( - Namespace(FALLBACK_NAMESPACE, kFallbackNamespace, kEntryUrl2)); + Namespace(FALLBACK_NAMESPACE, kFallbackNamespace, kEntryUrl2, false)); } void Verify_FindMainResponseWithMultipleHits() { @@ -1181,13 +1358,15 @@ class AppCacheStorageImplTest : public testing::Test { MakeCacheAndGroup(kManifestUrl, 1, 1, true); cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::EXPLICIT | AppCacheEntry::FOREIGN, 1)); - cache_->online_whitelist_namespaces_.push_back(kOnlineNamespace); cache_->AddEntry(kEntryUrl2, AppCacheEntry(AppCacheEntry::FALLBACK, 2)); cache_->fallback_namespaces_.push_back( - Namespace(FALLBACK_NAMESPACE, kFallbackNamespace, kEntryUrl2)); - cache_->online_whitelist_namespaces_.push_back(kOnlineNamespace); + Namespace(FALLBACK_NAMESPACE, kFallbackNamespace, kEntryUrl2, false)); cache_->online_whitelist_namespaces_.push_back( - kOnlineNamespaceWithinFallback); + Namespace(NETWORK_NAMESPACE, kOnlineNamespace, + GURL(), false)); + cache_->online_whitelist_namespaces_.push_back( + Namespace(NETWORK_NAMESPACE, kOnlineNamespaceWithinFallback, + GURL(), false)); AppCacheDatabase::EntryRecord entry_record; entry_record.cache_id = 1; @@ -1201,8 +1380,8 @@ class AppCacheStorageImplTest : public testing::Test { EXPECT_TRUE(database()->InsertOnlineWhiteList(&whitelist_record)); AppCacheDatabase::NamespaceRecord fallback_namespace_record; fallback_namespace_record.cache_id = 1; - fallback_namespace_record.target_url = kEntryUrl2; - fallback_namespace_record.namespace_url = kFallbackNamespace; + fallback_namespace_record.namespace_.target_url = kEntryUrl2; + fallback_namespace_record.namespace_.namespace_url = kFallbackNamespace; fallback_namespace_record.origin = kManifestUrl.GetOrigin(); EXPECT_TRUE(database()->InsertNamespace(&fallback_namespace_record)); whitelist_record.cache_id = 1; @@ -1419,6 +1598,26 @@ TEST_F(AppCacheStorageImplTest, FindMainResponseExclusionsInWorkingSet) { &AppCacheStorageImplTest::FindMainResponseExclusionsInWorkingSet); } +TEST_F(AppCacheStorageImplTest, FindInterceptPatternMatchInWorkingSet) { + RunTestOnIOThread( + &AppCacheStorageImplTest::FindInterceptPatternMatchInWorkingSet); +} + +TEST_F(AppCacheStorageImplTest, FindInterceptPatternMatchInDatabase) { + RunTestOnIOThread( + &AppCacheStorageImplTest::FindInterceptPatternMatchInDatabase); +} + +TEST_F(AppCacheStorageImplTest, FindFallbackPatternMatchInWorkingSet) { + RunTestOnIOThread( + &AppCacheStorageImplTest::FindFallbackPatternMatchInWorkingSet); +} + +TEST_F(AppCacheStorageImplTest, FindFallbackPatternMatchInDatabase) { + RunTestOnIOThread( + &AppCacheStorageImplTest::FindFallbackPatternMatchInDatabase); +} + // That's all folks! } // namespace appcache diff --git a/webkit/appcache/appcache_unittest.cc b/webkit/appcache/appcache_unittest.cc index 2fbf607..d097a45 100644 --- a/webkit/appcache/appcache_unittest.cc +++ b/webkit/appcache/appcache_unittest.cc @@ -89,9 +89,11 @@ TEST(AppCacheTest, InitializeWithManifest) { manifest.explicit_urls.insert("http://two.com"); manifest.fallback_namespaces.push_back( Namespace(FALLBACK_NAMESPACE, GURL("http://fb1.com"), - GURL("http://fbone.com"))); - manifest.online_whitelist_namespaces.push_back(GURL("http://w1.com")); - manifest.online_whitelist_namespaces.push_back(GURL("http://w2.com")); + GURL("http://fbone.com"), true)); + manifest.online_whitelist_namespaces.push_back( + Namespace(NETWORK_NAMESPACE, GURL("http://w1.com"), GURL(), false)); + manifest.online_whitelist_namespaces.push_back( + Namespace(NETWORK_NAMESPACE, GURL("http://w2.com"), GURL(), false)); manifest.online_whitelist_all = true; cache->InitializeWithManifest(&manifest); @@ -101,11 +103,12 @@ TEST(AppCacheTest, InitializeWithManifest) { EXPECT_EQ(expected, fallbacks.size()); EXPECT_EQ(GURL("http://fb1.com"), fallbacks[0].namespace_url); EXPECT_EQ(GURL("http://fbone.com"), fallbacks[0].target_url); - const std::vector<GURL>& whitelist = cache->online_whitelist_namespaces_; + EXPECT_TRUE(fallbacks[0].is_pattern); + const NamespaceVector& whitelist = cache->online_whitelist_namespaces_; expected = 2; EXPECT_EQ(expected, whitelist.size()); - EXPECT_EQ(GURL("http://w1.com"), whitelist[0]); - EXPECT_EQ(GURL("http://w2.com"), whitelist[1]); + EXPECT_EQ(GURL("http://w1.com"), whitelist[0].namespace_url); + EXPECT_EQ(GURL("http://w2.com"), whitelist[1].namespace_url); EXPECT_TRUE(cache->online_whitelist_all_); // Ensure collections in manifest were taken over by the cache rather than @@ -145,21 +148,24 @@ TEST(AppCacheTest, FindResponseForRequest) { const int64 kInterceptResponseId = 6; Manifest manifest; - manifest.online_whitelist_namespaces.push_back(kOnlineNamespaceUrl); manifest.online_whitelist_namespaces.push_back( - kOnlineNamespaceWithinOtherNamespaces); + Namespace(NETWORK_NAMESPACE, kOnlineNamespaceUrl, + GURL(), false)); + manifest.online_whitelist_namespaces.push_back( + Namespace(NETWORK_NAMESPACE, kOnlineNamespaceWithinOtherNamespaces, + GURL(), false)); manifest.fallback_namespaces.push_back( Namespace(FALLBACK_NAMESPACE, kFallbackNamespaceUrl1, - kFallbackEntryUrl1)); + kFallbackEntryUrl1, false)); manifest.fallback_namespaces.push_back( Namespace(FALLBACK_NAMESPACE, kFallbackNamespaceUrl2, - kFallbackEntryUrl2)); + kFallbackEntryUrl2, false)); manifest.intercept_namespaces.push_back( Namespace(INTERCEPT_NAMESPACE, kInterceptNamespace, - kInterceptNamespaceEntry)); + kInterceptNamespaceEntry, false)); manifest.intercept_namespaces.push_back( Namespace(INTERCEPT_NAMESPACE, kInterceptNamespaceWithinFallback, - kInterceptNamespaceEntry)); + kInterceptNamespaceEntry, false)); // Create a cache with some namespaces and entries. scoped_refptr<AppCache> cache(new AppCache(service.storage(), 1234)); @@ -323,6 +329,191 @@ TEST(AppCacheTest, FindResponseForRequest) { EXPECT_FALSE(network_namespace); } +TEST(AppCacheTest, FindInterceptPatternResponseForRequest) { + MockAppCacheService service; + + // Setup an appcache with an intercept namespace that uses pattern matching. + const GURL kInterceptNamespaceBase("http://blah/intercept_namespace/"); + const GURL kInterceptPatternNamespace( + kInterceptNamespaceBase.Resolve("*.hit*")); + const GURL kInterceptNamespaceEntry("http://blah/intercept_resource"); + const int64 kInterceptResponseId = 1; + Manifest manifest; + manifest.intercept_namespaces.push_back( + Namespace(INTERCEPT_NAMESPACE, kInterceptPatternNamespace, + kInterceptNamespaceEntry, true)); + scoped_refptr<AppCache> cache(new AppCache(service.storage(), 1234)); + cache->InitializeWithManifest(&manifest); + cache->AddEntry( + kInterceptNamespaceEntry, + AppCacheEntry(AppCacheEntry::INTERCEPT, kInterceptResponseId)); + cache->set_complete(true); + + // See that the pattern match works. + bool found = false; + AppCacheEntry entry; + AppCacheEntry fallback_entry; + GURL intercept_namespace; + GURL fallback_namespace; + bool network_namespace = false; + + found = cache->FindResponseForRequest( + GURL("http://blah/miss"), + &entry, &intercept_namespace, + &fallback_entry, &fallback_namespace, + &network_namespace); + EXPECT_FALSE(found); + + found = cache->FindResponseForRequest( + GURL("http://blah/intercept_namespace/another_miss"), + &entry, &intercept_namespace, + &fallback_entry, &fallback_namespace, + &network_namespace); + EXPECT_FALSE(found); + + found = cache->FindResponseForRequest( + GURL("http://blah/intercept_namespace/path.hit"), + &entry, &intercept_namespace, + &fallback_entry, &fallback_namespace, + &network_namespace); + EXPECT_TRUE(found); + EXPECT_EQ(kInterceptResponseId, entry.response_id()); + EXPECT_EQ(kInterceptNamespaceEntry, + cache->GetInterceptEntryUrl(intercept_namespace)); + EXPECT_FALSE(fallback_entry.has_response_id()); + EXPECT_TRUE(fallback_namespace.is_empty()); + EXPECT_FALSE(network_namespace); + + entry = AppCacheEntry(); // reset + + found = cache->FindResponseForRequest( + GURL("http://blah/intercept_namespace/longer/path.hit?arg=ok"), + &entry, &intercept_namespace, + &fallback_entry, &fallback_namespace, + &network_namespace); + EXPECT_TRUE(found); + EXPECT_EQ(kInterceptResponseId, entry.response_id()); + EXPECT_EQ(kInterceptNamespaceEntry, + cache->GetInterceptEntryUrl(intercept_namespace)); + EXPECT_FALSE(fallback_entry.has_response_id()); + EXPECT_TRUE(fallback_namespace.is_empty()); + EXPECT_FALSE(network_namespace); +} + +TEST(AppCacheTest, FindFallbackPatternResponseForRequest) { + MockAppCacheService service; + + // Setup an appcache with a fallback namespace that uses pattern matching. + const GURL kFallbackNamespaceBase("http://blah/fallback_namespace/"); + const GURL kFallbackPatternNamespace( + kFallbackNamespaceBase.Resolve("*.hit*")); + const GURL kFallbackNamespaceEntry("http://blah/fallback_resource"); + const int64 kFallbackResponseId = 1; + Manifest manifest; + manifest.fallback_namespaces.push_back( + Namespace(FALLBACK_NAMESPACE, kFallbackPatternNamespace, + kFallbackNamespaceEntry, true)); + scoped_refptr<AppCache> cache(new AppCache(service.storage(), 1234)); + cache->InitializeWithManifest(&manifest); + cache->AddEntry( + kFallbackNamespaceEntry, + AppCacheEntry(AppCacheEntry::FALLBACK, kFallbackResponseId)); + cache->set_complete(true); + + // See that the pattern match works. + bool found = false; + AppCacheEntry entry; + AppCacheEntry fallback_entry; + GURL intercept_namespace; + GURL fallback_namespace; + bool network_namespace = false; + + found = cache->FindResponseForRequest( + GURL("http://blah/miss"), + &entry, &intercept_namespace, + &fallback_entry, &fallback_namespace, + &network_namespace); + EXPECT_FALSE(found); + + found = cache->FindResponseForRequest( + GURL("http://blah/fallback_namespace/another_miss"), + &entry, &intercept_namespace, + &fallback_entry, &fallback_namespace, + &network_namespace); + EXPECT_FALSE(found); + + found = cache->FindResponseForRequest( + GURL("http://blah/fallback_namespace/path.hit"), + &entry, &intercept_namespace, + &fallback_entry, &fallback_namespace, + &network_namespace); + EXPECT_TRUE(found); + EXPECT_FALSE(entry.has_response_id()); + EXPECT_EQ(kFallbackResponseId, fallback_entry.response_id()); + EXPECT_EQ(kFallbackNamespaceEntry, + cache->GetFallbackEntryUrl(fallback_namespace)); + EXPECT_FALSE(network_namespace); + + fallback_entry = AppCacheEntry(); + fallback_namespace = GURL(); + + found = cache->FindResponseForRequest( + GURL("http://blah/fallback_namespace/longer/path.hit?arg=ok"), + &entry, &intercept_namespace, + &fallback_entry, &fallback_namespace, + &network_namespace); + EXPECT_TRUE(found); + EXPECT_FALSE(entry.has_response_id()); + EXPECT_EQ(kFallbackResponseId, fallback_entry.response_id()); + EXPECT_EQ(kFallbackNamespaceEntry, + cache->GetFallbackEntryUrl(fallback_namespace)); + EXPECT_TRUE(intercept_namespace.is_empty()); + EXPECT_FALSE(network_namespace); +} + + +TEST(AppCacheTest, FindNetworkNamespacePatternResponseForRequest) { + MockAppCacheService service; + + // Setup an appcache with a network namespace that uses pattern matching. + const GURL kNetworkNamespaceBase("http://blah/network_namespace/"); + const GURL kNetworkPatternNamespace( + kNetworkNamespaceBase.Resolve("*.hit*")); + Manifest manifest; + manifest.online_whitelist_namespaces.push_back( + Namespace(NETWORK_NAMESPACE, kNetworkPatternNamespace, + GURL(), true)); + manifest.online_whitelist_all = false; + scoped_refptr<AppCache> cache(new AppCache(service.storage(), 1234)); + cache->InitializeWithManifest(&manifest); + cache->set_complete(true); + + // See that the pattern match works. + bool found = false; + AppCacheEntry entry; + AppCacheEntry fallback_entry; + GURL intercept_namespace; + GURL fallback_namespace; + bool network_namespace = false; + + found = cache->FindResponseForRequest( + GURL("http://blah/miss"), + &entry, &intercept_namespace, + &fallback_entry, &fallback_namespace, + &network_namespace); + EXPECT_FALSE(found); + + found = cache->FindResponseForRequest( + GURL("http://blah/network_namespace/path.hit"), + &entry, &intercept_namespace, + &fallback_entry, &fallback_namespace, + &network_namespace); + EXPECT_TRUE(found); + EXPECT_TRUE(network_namespace); + EXPECT_FALSE(entry.has_response_id()); + EXPECT_FALSE(fallback_entry.has_response_id()); +} + TEST(AppCacheTest, ToFromDatabaseRecords) { // Setup a cache with some entries. const int64 kCacheId = 1234; @@ -330,6 +521,7 @@ TEST(AppCacheTest, ToFromDatabaseRecords) { const GURL kManifestUrl("http://foo.com/manifest"); const GURL kInterceptUrl("http://foo.com/intercept.html"); const GURL kFallbackUrl("http://foo.com/fallback.html"); + const GURL kWhitelistUrl("http://foo.com/whitelist*"); const std::string kData( "CACHE MANIFEST\r" "CHROMIUM-INTERCEPT:\r" @@ -337,7 +529,7 @@ TEST(AppCacheTest, ToFromDatabaseRecords) { "FALLBACK:\r" "/ /fallback.html\r" "NETWORK:\r" - "/whitelist\r" + "/whitelist* isPattern\r" "*\r"); MockAppCacheService service; scoped_refptr<AppCacheGroup> group = @@ -347,6 +539,10 @@ TEST(AppCacheTest, ToFromDatabaseRecords) { EXPECT_TRUE( ParseManifest(kManifestUrl, kData.c_str(), kData.length(), manifest)); cache->InitializeWithManifest(&manifest); + EXPECT_EQ(NETWORK_NAMESPACE, cache->online_whitelist_namespaces_[0].type); + EXPECT_TRUE(cache->online_whitelist_namespaces_[0].is_pattern); + EXPECT_EQ(kWhitelistUrl, + cache->online_whitelist_namespaces_[0].namespace_url); cache->AddEntry( kManifestUrl, AppCacheEntry(AppCacheEntry::MANIFEST, 1, 1)); @@ -391,6 +587,83 @@ TEST(AppCacheTest, ToFromDatabaseRecords) { EXPECT_EQ(kFallbackUrl, cache->GetFallbackEntryUrl(GURL("http://foo.com/"))); EXPECT_EQ(1 + 2 + 3, cache->cache_size()); + EXPECT_EQ(NETWORK_NAMESPACE, cache->online_whitelist_namespaces_[0].type); + EXPECT_TRUE(cache->online_whitelist_namespaces_[0].is_pattern); + EXPECT_EQ(kWhitelistUrl, + cache->online_whitelist_namespaces_[0].namespace_url); +} + +TEST(AppCacheTest, IsNamespaceMatch) { + Namespace prefix; + prefix.namespace_url = GURL("http://foo.com/prefix"); + prefix.is_pattern = false; + EXPECT_TRUE(prefix.IsMatch( + GURL("http://foo.com/prefix_and_anothing_goes"))); + EXPECT_FALSE(prefix.IsMatch( + GURL("http://foo.com/nope"))); + + Namespace bar_no_star; + bar_no_star.namespace_url = GURL("http://foo.com/bar"); + bar_no_star.is_pattern = true; + EXPECT_TRUE(bar_no_star.IsMatch( + GURL("http://foo.com/bar"))); + EXPECT_FALSE(bar_no_star.IsMatch( + GURL("http://foo.com/bar/nope"))); + + Namespace bar_star; + bar_star.namespace_url = GURL("http://foo.com/bar/*"); + bar_star.is_pattern = true; + EXPECT_TRUE(bar_star.IsMatch( + GURL("http://foo.com/bar/"))); + EXPECT_TRUE(bar_star.IsMatch( + GURL("http://foo.com/bar/should_match"))); + EXPECT_FALSE(bar_star.IsMatch( + GURL("http://foo.com/not_bar/should_not_match"))); + + Namespace star_bar_star; + star_bar_star.namespace_url = GURL("http://foo.com/*/bar/*"); + star_bar_star.is_pattern = true; + EXPECT_TRUE(star_bar_star.IsMatch( + GURL("http://foo.com/any/bar/should_match"))); + EXPECT_TRUE(star_bar_star.IsMatch( + GURL("http://foo.com/any/bar/"))); + EXPECT_FALSE(star_bar_star.IsMatch( + GURL("http://foo.com/any/not_bar/no_match"))); + + Namespace query_star_edit; + query_star_edit.namespace_url = GURL("http://foo.com/query?id=*&verb=edit*"); + query_star_edit.is_pattern = true; + EXPECT_TRUE(query_star_edit.IsMatch( + GURL("http://foo.com/query?id=1234&verb=edit&option=blue"))); + EXPECT_TRUE(query_star_edit.IsMatch( + GURL("http://foo.com/query?id=12345&option=blue&verb=edit"))); + EXPECT_FALSE(query_star_edit.IsMatch( + GURL("http://foo.com/query?id=12345&option=blue&verb=print"))); + EXPECT_TRUE(query_star_edit.IsMatch( + GURL("http://foo.com/query?id=123&verb=print&verb=edit"))); + + Namespace star_greediness; + star_greediness.namespace_url = GURL("http://foo.com/*/b"); + star_greediness.is_pattern = true; + EXPECT_TRUE(star_greediness.IsMatch( + GURL("http://foo.com/a/b"))); + EXPECT_TRUE(star_greediness.IsMatch( + GURL("http://foo.com/a/wxy/z/b"))); + EXPECT_TRUE(star_greediness.IsMatch( + GURL("http://foo.com/a/b/b"))); + EXPECT_TRUE(star_greediness.IsMatch( + GURL("http://foo.com/b/b"))); + EXPECT_TRUE(star_greediness.IsMatch( + GURL("http://foo.com/a/b/b/b/b/b"))); + EXPECT_TRUE(star_greediness.IsMatch( + GURL("http://foo.com/a/b/b/b/a/b"))); + EXPECT_TRUE(star_greediness.IsMatch( + GURL("http://foo.com/a/b/01234567890abcdef/b"))); + EXPECT_TRUE(star_greediness.IsMatch( + GURL("http://foo.com/a/b/01234567890abcdef/b01234567890abcdef/b"))); + EXPECT_TRUE(star_greediness.IsMatch( + GURL("http://foo.com/a/b/01234567890abcdef_eat_some_more_characters_" + "/and_even_more_for_the_heck_of_it/01234567890abcdef/b"))); } } // namespace appacache diff --git a/webkit/appcache/appcache_update_job_unittest.cc b/webkit/appcache/appcache_update_job_unittest.cc index a7a2667..86b3df6 100644 --- a/webkit/appcache/appcache_update_job_unittest.cc +++ b/webkit/appcache/appcache_update_job_unittest.cc @@ -3192,7 +3192,8 @@ class AppCacheUpdateJobTest : public testing::Test, Namespace( FALLBACK_NAMESPACE, MockHttpServer::GetMockUrl("files/fallback1"), - MockHttpServer::GetMockUrl("files/fallback1a"))); + MockHttpServer::GetMockUrl("files/fallback1a"), + false)); EXPECT_TRUE(cache->online_whitelist_namespaces_.empty()); EXPECT_TRUE(cache->online_whitelist_all_); @@ -3219,13 +3220,15 @@ class AppCacheUpdateJobTest : public testing::Test, Namespace( FALLBACK_NAMESPACE, MockHttpServer::GetMockUrl("files/fallback1"), - MockHttpServer::GetMockUrl("files/explicit1"))); + MockHttpServer::GetMockUrl("files/explicit1"), + false)); EXPECT_EQ(expected, cache->online_whitelist_namespaces_.size()); - EXPECT_TRUE(cache->online_whitelist_namespaces_.end() != - std::find(cache->online_whitelist_namespaces_.begin(), - cache->online_whitelist_namespaces_.end(), - MockHttpServer::GetMockUrl("files/online1"))); + EXPECT_TRUE(cache->online_whitelist_namespaces_[0] == + Namespace( + NETWORK_NAMESPACE, + MockHttpServer::GetMockUrl("files/online1"), + GURL(), false)); EXPECT_FALSE(cache->online_whitelist_all_); EXPECT_TRUE(cache->update_time_ > base::Time()); diff --git a/webkit/appcache/manifest_parser.cc b/webkit/appcache/manifest_parser.cc index 0d98d1e..3afbad7 100644 --- a/webkit/appcache/manifest_parser.cc +++ b/webkit/appcache/manifest_parser.cc @@ -38,6 +38,23 @@ namespace appcache { +namespace { + +// Helper function used to identify 'isPattern' annotations. +bool HasPatternMatchingAnnotation(const wchar_t* line_p, + const wchar_t* line_end) { + // Skip whitespace separating the resource url from the annotation. + // Note: trailing whitespace has already been trimmed from the line. + while (line_p < line_end && (*line_p == '\t' || *line_p == ' ')) + ++line_p; + if (line_p == line_end) + return false; + std::wstring annotation(line_p, line_end - line_p); + return annotation == L"isPattern"; +} + +} + enum Mode { EXPLICIT, INTERCEPT, @@ -189,7 +206,9 @@ bool ParseManifest(const GURL& manifest_url, const char* data, int length, if (mode == EXPLICIT) { manifest.explicit_urls.insert(url.spec()); } else { - manifest.online_whitelist_namespaces.push_back(url); + bool is_pattern = HasPatternMatchingAnnotation(line_p, line_end); + manifest.online_whitelist_namespaces.push_back( + Namespace(NETWORK_NAMESPACE, url, GURL(), is_pattern)); } } else if (mode == INTERCEPT) { // Lines of the form, @@ -258,8 +277,10 @@ bool ParseManifest(const GURL& manifest_url, const char* data, int length, if (manifest_url.GetOrigin() != target_url.GetOrigin()) continue; + bool is_pattern = HasPatternMatchingAnnotation(line_p, line_end); manifest.intercept_namespaces.push_back( - Namespace(INTERCEPT_NAMESPACE, namespace_url, target_url)); + Namespace(INTERCEPT_NAMESPACE, namespace_url, + target_url, is_pattern)); } else if (mode == FALLBACK) { const wchar_t* line_p = line.c_str(); const wchar_t* line_end = line_p + line.length(); @@ -316,10 +337,13 @@ bool ParseManifest(const GURL& manifest_url, const char* data, int length, continue; } + bool is_pattern = HasPatternMatchingAnnotation(line_p, line_end); + // Store regardless of duplicate namespace URL. Only first match // will ever be used. manifest.fallback_namespaces.push_back( - Namespace(FALLBACK_NAMESPACE, namespace_url, fallback_url)); + Namespace(FALLBACK_NAMESPACE, namespace_url, + fallback_url, is_pattern)); } else { NOTREACHED(); } diff --git a/webkit/appcache/manifest_parser.h b/webkit/appcache/manifest_parser.h index 47ebe77..25fde8c 100644 --- a/webkit/appcache/manifest_parser.h +++ b/webkit/appcache/manifest_parser.h @@ -50,7 +50,7 @@ struct WEBKIT_STORAGE_EXPORT Manifest { base::hash_set<std::string> explicit_urls; NamespaceVector intercept_namespaces; NamespaceVector fallback_namespaces; - std::vector<GURL> online_whitelist_namespaces; + NamespaceVector online_whitelist_namespaces; bool online_whitelist_all; }; diff --git a/webkit/appcache/manifest_parser_unittest.cc b/webkit/appcache/manifest_parser_unittest.cc index eaafffa..84cf7cd 100644 --- a/webkit/appcache/manifest_parser_unittest.cc +++ b/webkit/appcache/manifest_parser_unittest.cc @@ -134,15 +134,18 @@ TEST(AppCacheManifestParserTest, WhitelistUrls) { EXPECT_TRUE(manifest.intercept_namespaces.empty()); EXPECT_FALSE(manifest.online_whitelist_all); - std::vector<GURL> online = manifest.online_whitelist_namespaces; + const NamespaceVector& online = manifest.online_whitelist_namespaces; const size_t kExpected = 6; ASSERT_EQ(kExpected, online.size()); - EXPECT_EQ(GURL("http://www.bar.com/relative/one"), online[0]); - EXPECT_EQ(GURL("http://www.bar.com/two"), online[1]); - EXPECT_EQ(GURL("http://www.diff.com/three"), online[2]); - EXPECT_EQ(GURL("http://www.bar.com/relative/four"), online[3]); - EXPECT_EQ(GURL("http://www.five.com"), online[4]); - EXPECT_EQ(GURL("http://www.bar.com/*foo"), online[5]); + EXPECT_EQ(NETWORK_NAMESPACE, online[0].type); + EXPECT_FALSE(online[0].is_pattern); + EXPECT_TRUE(online[0].target_url.is_empty()); + EXPECT_EQ(GURL("http://www.bar.com/relative/one"), online[0].namespace_url); + EXPECT_EQ(GURL("http://www.bar.com/two"), online[1].namespace_url); + EXPECT_EQ(GURL("http://www.diff.com/three"), online[2].namespace_url); + EXPECT_EQ(GURL("http://www.bar.com/relative/four"), online[3].namespace_url); + EXPECT_EQ(GURL("http://www.five.com"), online[4].namespace_url); + EXPECT_EQ(GURL("http://www.bar.com/*foo"), online[5].namespace_url); } TEST(AppCacheManifestParserTest, FallbackUrls) { @@ -322,13 +325,17 @@ TEST(AppCacheManifestParserTest, ComboUrls) { EXPECT_TRUE(urls.find("http://combo.com:99/explicit-2") != urls.end()); EXPECT_TRUE(urls.find("http://www.diff.com/explicit-3") != urls.end()); - std::vector<GURL> online = manifest.online_whitelist_namespaces; + const NamespaceVector& online = manifest.online_whitelist_namespaces; expected = 4; ASSERT_EQ(expected, online.size()); - EXPECT_EQ(GURL("http://combo.com/whitelist-1"), online[0]); - EXPECT_EQ(GURL("http://www.diff.com/whitelist-2"), online[1]); - EXPECT_EQ(GURL("http://combo.com:42/relative/whitelist-3"), online[2]); - EXPECT_EQ(GURL("http://combo.com:99/whitelist-4"), online[3]); + EXPECT_EQ(GURL("http://combo.com/whitelist-1"), + online[0].namespace_url); + EXPECT_EQ(GURL("http://www.diff.com/whitelist-2"), + online[1].namespace_url); + EXPECT_EQ(GURL("http://combo.com:42/relative/whitelist-3"), + online[2].namespace_url); + EXPECT_EQ(GURL("http://combo.com:99/whitelist-4"), + online[3].namespace_url); const NamespaceVector& fallbacks = manifest.fallback_namespaces; expected = 2; @@ -399,4 +406,67 @@ TEST(AppCacheManifestParserTest, DifferentOriginUrlWithSecureScheme) { urls.end()); } +TEST(AppCacheManifestParserTest, PatternMatching) { + const GURL kUrl("http://foo.com/manifest"); + const std::string kManifestBody( + "CACHE MANIFEST\r" + "CACHE: \r" + "http://foo.com/page.html\r" + "CHROMIUM-INTERCEPT:\r" + "http://foo.com/intercept_prefix return /prefix\r" + "http://foo.com/intercept_pattern return /pattern isPattern\r" + "http://foo.com/*/intercept_pattern?query return /pattern isPattern\r" + "FALLBACK:\r" + "http://foo.com/fallback_prefix /prefix wrongAnnotation\r" + "http://foo.com/fallback_pattern* /pattern\tisPattern \r" + "NETWORK:\r" + "*\r" + "isPattern\r" // should not be interpretted as a pattern + "http://foo.com/network_pattern* isPattern\r"); + + + Manifest manifest; + EXPECT_TRUE(ParseManifest(kUrl, kManifestBody.c_str(), + kManifestBody.length(), manifest)); + EXPECT_TRUE(manifest.online_whitelist_all); + EXPECT_EQ(1u, manifest.explicit_urls.size()); + EXPECT_EQ(3u, manifest.intercept_namespaces.size()); + EXPECT_EQ(2u, manifest.fallback_namespaces.size()); + EXPECT_EQ(2u, manifest.online_whitelist_namespaces.size()); + EXPECT_EQ(INTERCEPT_NAMESPACE, manifest.intercept_namespaces[0].type); + EXPECT_EQ(FALLBACK_NAMESPACE, manifest.fallback_namespaces[0].type); + EXPECT_EQ(NETWORK_NAMESPACE, manifest.online_whitelist_namespaces[0].type); + EXPECT_FALSE(manifest.intercept_namespaces[0].is_pattern); + EXPECT_TRUE(manifest.intercept_namespaces[1].is_pattern); + EXPECT_TRUE(manifest.intercept_namespaces[2].is_pattern); + EXPECT_FALSE(manifest.fallback_namespaces[0].is_pattern); + EXPECT_TRUE(manifest.fallback_namespaces[1].is_pattern); + EXPECT_FALSE(manifest.online_whitelist_namespaces[0].is_pattern); + EXPECT_TRUE(manifest.online_whitelist_namespaces[1].is_pattern); + EXPECT_EQ( + GURL("http://foo.com/*/intercept_pattern?query"), + manifest.intercept_namespaces[2].namespace_url); + EXPECT_EQ( + GURL("http://foo.com/pattern"), + manifest.intercept_namespaces[2].target_url); + EXPECT_EQ( + GURL("http://foo.com/fallback_pattern*"), + manifest.fallback_namespaces[1].namespace_url); + EXPECT_EQ( + GURL("http://foo.com/pattern"), + manifest.fallback_namespaces[1].target_url); + EXPECT_EQ( + GURL("http://foo.com/isPattern"), + manifest.online_whitelist_namespaces[0].namespace_url); + EXPECT_EQ( + GURL(), + manifest.online_whitelist_namespaces[0].target_url); + EXPECT_EQ( + GURL("http://foo.com/network_pattern*"), + manifest.online_whitelist_namespaces[1].namespace_url); + EXPECT_EQ( + GURL(), + manifest.online_whitelist_namespaces[1].target_url); +} + } // namespace appcache diff --git a/webkit/appcache/mock_appcache_storage_unittest.cc b/webkit/appcache/mock_appcache_storage_unittest.cc index cb898e7..351e340 100644 --- a/webkit/appcache/mock_appcache_storage_unittest.cc +++ b/webkit/appcache/mock_appcache_storage_unittest.cc @@ -471,10 +471,10 @@ TEST_F(MockAppCacheStorageTest, BasicFindMainFallbackResponse) { Manifest manifest; manifest.fallback_namespaces.push_back( Namespace(FALLBACK_NAMESPACE, kFallbackNamespaceUrl1, - kFallbackEntryUrl1)); + kFallbackEntryUrl1, false)); manifest.fallback_namespaces.push_back( Namespace(FALLBACK_NAMESPACE, kFallbackNamespaceUrl2, - kFallbackEntryUrl2)); + kFallbackEntryUrl2, false)); scoped_refptr<AppCache> cache(new AppCache(service.storage(), kCacheId)); cache->InitializeWithManifest(&manifest); @@ -581,8 +581,9 @@ TEST_F(MockAppCacheStorageTest, FindMainResponseExclusions) { const int64 kResponseId = 1; Manifest manifest; - manifest.online_whitelist_namespaces.push_back(kOnlineNamespaceUrl); - + manifest.online_whitelist_namespaces.push_back( + Namespace(NETWORK_NAMESPACE, kOnlineNamespaceUrl, + GURL(), false)); scoped_refptr<AppCache> cache(new AppCache(service.storage(), kCacheId)); cache->InitializeWithManifest(&manifest); cache->AddEntry( |