Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(556)

Side by Side Diff: webkit/appcache/appcache_update_job_unittest.cc

Issue 8396013: AppCache INTERCEPT namespace. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "testing/gtest/include/gtest/gtest.h" 5 #include "testing/gtest/include/gtest/gtest.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/bind_helpers.h" 8 #include "base/bind_helpers.h"
9 #include "base/stl_util.h" 9 #include "base/stl_util.h"
10 #include "base/threading/thread.h" 10 #include "base/threading/thread.h"
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after
107 (*body) = "CACHE MANIFEST\n"; 107 (*body) = "CACHE MANIFEST\n";
108 } else if (path == "/files/explicit1") { 108 } else if (path == "/files/explicit1") {
109 (*headers) = std::string(ok_headers, arraysize(ok_headers)); 109 (*headers) = std::string(ok_headers, arraysize(ok_headers));
110 (*body) = "explicit1"; 110 (*body) = "explicit1";
111 } else if (path == "/files/explicit2") { 111 } else if (path == "/files/explicit2") {
112 (*headers) = std::string(ok_headers, arraysize(ok_headers)); 112 (*headers) = std::string(ok_headers, arraysize(ok_headers));
113 (*body) = "explicit2"; 113 (*body) = "explicit2";
114 } else if (path == "/files/fallback1a") { 114 } else if (path == "/files/fallback1a") {
115 (*headers) = std::string(ok_headers, arraysize(ok_headers)); 115 (*headers) = std::string(ok_headers, arraysize(ok_headers));
116 (*body) = "fallback1a"; 116 (*body) = "fallback1a";
117 } else if (path == "/files/intercept1a") {
118 (*headers) = std::string(ok_headers, arraysize(ok_headers));
119 (*body) = "intercept1a";
117 } else if (path == "/files/gone") { 120 } else if (path == "/files/gone") {
118 (*headers) = std::string(gone_headers, arraysize(gone_headers)); 121 (*headers) = std::string(gone_headers, arraysize(gone_headers));
119 (*body) = ""; 122 (*body) = "";
120 } else if (path == "/files/manifest1") { 123 } else if (path == "/files/manifest1") {
121 (*headers) = std::string(manifest_headers, arraysize(manifest_headers)); 124 (*headers) = std::string(manifest_headers, arraysize(manifest_headers));
122 (*body) = kManifest1Contents; 125 (*body) = kManifest1Contents;
123 } else if (path == "/files/manifest1-with-notmodified") { 126 } else if (path == "/files/manifest1-with-notmodified") {
124 (*headers) = std::string(manifest_headers, arraysize(manifest_headers)); 127 (*headers) = std::string(manifest_headers, arraysize(manifest_headers));
125 (*body) = kManifest1Contents; 128 (*body) = kManifest1Contents;
126 (*body).append("CACHE:\n" 129 (*body).append("CACHE:\n"
(...skipping 22 matching lines...) Expand all
149 (*headers) = std::string(manifest_headers, arraysize(manifest_headers)); 152 (*headers) = std::string(manifest_headers, arraysize(manifest_headers));
150 (*body) = "CACHE MANIFEST\n" 153 (*body) = "CACHE MANIFEST\n"
151 "explicit-404\n" 154 "explicit-404\n"
152 "explicit1\n" 155 "explicit1\n"
153 "explicit2\n" 156 "explicit2\n"
154 "explicit3\n" 157 "explicit3\n"
155 "FALLBACK:\n" 158 "FALLBACK:\n"
156 "fallback1 fallback1a\n" 159 "fallback1 fallback1a\n"
157 "NETWORK:\n" 160 "NETWORK:\n"
158 "online1\n"; 161 "online1\n";
162 } else if (path == "/files/manifest-with-intercept") {
163 (*headers) = std::string(manifest_headers, arraysize(manifest_headers));
164 (*body) = "CACHE MANIFEST\n"
165 "CHROMIUM-INTERCEPT:\n"
166 "intercept1 return intercept1a\n";
159 } else if (path == "/files/notmodified") { 167 } else if (path == "/files/notmodified") {
160 (*headers) = std::string(not_modified_headers, 168 (*headers) = std::string(not_modified_headers,
161 arraysize(not_modified_headers)); 169 arraysize(not_modified_headers));
162 (*body) = ""; 170 (*body) = "";
163 } else if (path == "/files/servererror") { 171 } else if (path == "/files/servererror") {
164 (*headers) = std::string(error_headers, 172 (*headers) = std::string(error_headers,
165 arraysize(error_headers)); 173 arraysize(error_headers));
166 (*body) = "error"; 174 (*body) = "error";
167 } else if (path == "/files/valid_cross_origin_https_manifest") { 175 } else if (path == "/files/valid_cross_origin_https_manifest") {
168 (*headers) = std::string(manifest_headers, arraysize(manifest_headers)); 176 (*headers) = std::string(manifest_headers, arraysize(manifest_headers));
(...skipping 15 matching lines...) Expand all
184 }; 192 };
185 193
186 class MockHttpServerJobFactory 194 class MockHttpServerJobFactory
187 : public net::URLRequestJobFactory::ProtocolHandler { 195 : public net::URLRequestJobFactory::ProtocolHandler {
188 public: 196 public:
189 virtual net::URLRequestJob* MaybeCreateJob(net::URLRequest* request) const { 197 virtual net::URLRequestJob* MaybeCreateJob(net::URLRequest* request) const {
190 return MockHttpServer::JobFactory(request); 198 return MockHttpServer::JobFactory(request);
191 } 199 }
192 }; 200 };
193 201
202 inline bool operator==(const Namespace& lhs, const Namespace& rhs) {
203 return lhs.type == rhs.type &&
204 lhs.namespace_url == rhs.namespace_url &&
205 lhs.target_url == rhs.target_url;
206 }
207
194 } // namespace 208 } // namespace
195 209
196 class MockFrontend : public AppCacheFrontend { 210 class MockFrontend : public AppCacheFrontend {
197 public: 211 public:
198 MockFrontend() 212 MockFrontend()
199 : ignore_progress_events_(false), verify_progress_events_(false), 213 : ignore_progress_events_(false), verify_progress_events_(false),
200 last_progress_total_(-1), last_progress_complete_(-1), 214 last_progress_total_(-1), last_progress_complete_(-1),
201 start_update_trigger_(CHECKING_EVENT), update_(NULL) { 215 start_update_trigger_(CHECKING_EVENT), update_(NULL) {
202 } 216 }
203 217
(...skipping 801 matching lines...) Expand 10 before | Expand all | Expand 10 after
1005 do_checks_after_update_finished_ = true; 1019 do_checks_after_update_finished_ = true;
1006 expect_group_obsolete_ = false; 1020 expect_group_obsolete_ = false;
1007 expect_group_has_cache_ = true; 1021 expect_group_has_cache_ = true;
1008 tested_manifest_ = MANIFEST1; 1022 tested_manifest_ = MANIFEST1;
1009 frontend->AddExpectedEvent(MockFrontend::HostIds(1, host->host_id()), 1023 frontend->AddExpectedEvent(MockFrontend::HostIds(1, host->host_id()),
1010 CHECKING_EVENT); 1024 CHECKING_EVENT);
1011 1025
1012 WaitForUpdateToFinish(); 1026 WaitForUpdateToFinish();
1013 } 1027 }
1014 1028
1029 void DownloadInterceptEntriesTest() {
1030 // Ensures we download intercept entries too.
1031 ASSERT_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type());
1032 GURL manifest_url = MockHttpServer::GetMockUrl(
1033 "files/manifest-with-intercept");
1034 MakeService();
1035 group_ = new AppCacheGroup(
1036 service_.get(), manifest_url,
1037 service_->storage()->NewGroupId());
1038 AppCacheUpdateJob* update = new AppCacheUpdateJob(service_.get(), group_);
1039 group_->update_job_ = update;
1040
1041 MockFrontend* frontend = MakeMockFrontend();
1042 AppCacheHost* host = MakeHost(1, frontend);
1043 update->StartUpdate(host, GURL());
1044
1045 // Set up checks for when update job finishes.
1046 do_checks_after_update_finished_ = true;
1047 expect_group_obsolete_ = false;
1048 expect_group_has_cache_ = true;
1049 tested_manifest_ = MANIFEST_WITH_INTERCEPT;
1050 frontend->AddExpectedEvent(MockFrontend::HostIds(1, host->host_id()),
1051 CHECKING_EVENT);
1052
1053 WaitForUpdateToFinish();
1054 }
1055
1015 void BasicUpgradeSuccessTest() { 1056 void BasicUpgradeSuccessTest() {
1016 ASSERT_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type()); 1057 ASSERT_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type());
1017 1058
1018 MakeService(); 1059 MakeService();
1019 group_ = new AppCacheGroup( 1060 group_ = new AppCacheGroup(
1020 service_.get(), MockHttpServer::GetMockUrl("files/manifest1"), 1061 service_.get(), MockHttpServer::GetMockUrl("files/manifest1"),
1021 service_->storage()->NewGroupId()); 1062 service_->storage()->NewGroupId());
1022 AppCacheUpdateJob* update = new AppCacheUpdateJob(service_.get(), group_); 1063 AppCacheUpdateJob* update = new AppCacheUpdateJob(service_.get(), group_);
1023 group_->update_job_ = update; 1064 group_->update_job_ = update;
1024 1065
(...skipping 1978 matching lines...) Expand 10 before | Expand all | Expand 10 after
3003 break; 3044 break;
3004 case EMPTY_MANIFEST: 3045 case EMPTY_MANIFEST:
3005 VerifyEmptyManifest(cache); 3046 VerifyEmptyManifest(cache);
3006 break; 3047 break;
3007 case EMPTY_FILE_MANIFEST: 3048 case EMPTY_FILE_MANIFEST:
3008 VerifyEmptyFileManifest(cache); 3049 VerifyEmptyFileManifest(cache);
3009 break; 3050 break;
3010 case PENDING_MASTER_NO_UPDATE: 3051 case PENDING_MASTER_NO_UPDATE:
3011 VerifyMasterEntryNoUpdate(cache); 3052 VerifyMasterEntryNoUpdate(cache);
3012 break; 3053 break;
3054 case MANIFEST_WITH_INTERCEPT:
3055 VerifyManifestWithIntercept(cache);
3056 break;
3013 case NONE: 3057 case NONE:
3014 default: 3058 default:
3015 break; 3059 break;
3016 } 3060 }
3017 } 3061 }
3018 } 3062 }
3019 3063
3020 void VerifyManifest1(AppCache* cache) { 3064 void VerifyManifest1(AppCache* cache) {
3021 size_t expected = 3 + expect_extra_entries_.size(); 3065 size_t expected = 3 + expect_extra_entries_.size();
3022 EXPECT_EQ(expected, cache->entries().size()); 3066 EXPECT_EQ(expected, cache->entries().size());
(...skipping 13 matching lines...) Expand all
3036 EXPECT_EQ(AppCacheEntry::FALLBACK, entry->types()); 3080 EXPECT_EQ(AppCacheEntry::FALLBACK, entry->types());
3037 3081
3038 for (AppCache::EntryMap::iterator i = expect_extra_entries_.begin(); 3082 for (AppCache::EntryMap::iterator i = expect_extra_entries_.begin();
3039 i != expect_extra_entries_.end(); ++i) { 3083 i != expect_extra_entries_.end(); ++i) {
3040 entry = cache->GetEntry(i->first); 3084 entry = cache->GetEntry(i->first);
3041 ASSERT_TRUE(entry); 3085 ASSERT_TRUE(entry);
3042 EXPECT_EQ(i->second.types(), entry->types()); 3086 EXPECT_EQ(i->second.types(), entry->types());
3043 } 3087 }
3044 3088
3045 expected = 1; 3089 expected = 1;
3046 EXPECT_EQ(expected, cache->fallback_namespaces_.size()); 3090 ASSERT_EQ(expected, cache->fallback_namespaces_.size());
3047 EXPECT_TRUE(cache->fallback_namespaces_.end() != 3091 EXPECT_TRUE(cache->fallback_namespaces_[0] ==
3048 std::find(cache->fallback_namespaces_.begin(), 3092 Namespace(
3049 cache->fallback_namespaces_.end(), 3093 FALLBACK_NAMESPACE,
3050 FallbackNamespace( 3094 MockHttpServer::GetMockUrl("files/fallback1"),
3051 MockHttpServer::GetMockUrl("files/fallback1"), 3095 MockHttpServer::GetMockUrl("files/fallback1a")));
3052 MockHttpServer::GetMockUrl("files/fallback1a"))));
3053 3096
3054 EXPECT_TRUE(cache->online_whitelist_namespaces_.empty()); 3097 EXPECT_TRUE(cache->online_whitelist_namespaces_.empty());
3055 EXPECT_TRUE(cache->online_whitelist_all_); 3098 EXPECT_TRUE(cache->online_whitelist_all_);
3056 3099
3057 EXPECT_TRUE(cache->update_time_ > base::Time()); 3100 EXPECT_TRUE(cache->update_time_ > base::Time());
3058 } 3101 }
3059 3102
3060 void VerifyManifestMergedTypes(AppCache* cache) { 3103 void VerifyManifestMergedTypes(AppCache* cache) {
3061 size_t expected = 2; 3104 size_t expected = 2;
3062 EXPECT_EQ(expected, cache->entries().size()); 3105 EXPECT_EQ(expected, cache->entries().size());
3063 AppCacheEntry* entry = cache->GetEntry( 3106 AppCacheEntry* entry = cache->GetEntry(
3064 MockHttpServer::GetMockUrl("files/manifest-merged-types")); 3107 MockHttpServer::GetMockUrl("files/manifest-merged-types"));
3065 ASSERT_TRUE(entry); 3108 ASSERT_TRUE(entry);
3066 EXPECT_EQ(AppCacheEntry::EXPLICIT | AppCacheEntry::MANIFEST, 3109 EXPECT_EQ(AppCacheEntry::EXPLICIT | AppCacheEntry::MANIFEST,
3067 entry->types()); 3110 entry->types());
3068 entry = cache->GetEntry(MockHttpServer::GetMockUrl("files/explicit1")); 3111 entry = cache->GetEntry(MockHttpServer::GetMockUrl("files/explicit1"));
3069 ASSERT_TRUE(entry); 3112 ASSERT_TRUE(entry);
3070 EXPECT_EQ(AppCacheEntry::EXPLICIT | AppCacheEntry::FALLBACK | 3113 EXPECT_EQ(AppCacheEntry::EXPLICIT | AppCacheEntry::FALLBACK |
3071 AppCacheEntry::MASTER, entry->types()); 3114 AppCacheEntry::MASTER, entry->types());
3072 3115
3073 expected = 1; 3116 expected = 1;
3074 EXPECT_EQ(expected, cache->fallback_namespaces_.size()); 3117 ASSERT_EQ(expected, cache->fallback_namespaces_.size());
3075 EXPECT_TRUE(cache->fallback_namespaces_.end() != 3118 EXPECT_TRUE(cache->fallback_namespaces_[0] ==
3076 std::find(cache->fallback_namespaces_.begin(), 3119 Namespace(
3077 cache->fallback_namespaces_.end(), 3120 FALLBACK_NAMESPACE,
3078 FallbackNamespace( 3121 MockHttpServer::GetMockUrl("files/fallback1"),
3079 MockHttpServer::GetMockUrl("files/fallback1"), 3122 MockHttpServer::GetMockUrl("files/explicit1")));
3080 MockHttpServer::GetMockUrl("files/explicit1"))));
3081 3123
3082 EXPECT_EQ(expected, cache->online_whitelist_namespaces_.size()); 3124 EXPECT_EQ(expected, cache->online_whitelist_namespaces_.size());
3083 EXPECT_TRUE(cache->online_whitelist_namespaces_.end() != 3125 EXPECT_TRUE(cache->online_whitelist_namespaces_.end() !=
3084 std::find(cache->online_whitelist_namespaces_.begin(), 3126 std::find(cache->online_whitelist_namespaces_.begin(),
3085 cache->online_whitelist_namespaces_.end(), 3127 cache->online_whitelist_namespaces_.end(),
3086 MockHttpServer::GetMockUrl("files/online1"))); 3128 MockHttpServer::GetMockUrl("files/online1")));
3087 EXPECT_FALSE(cache->online_whitelist_all_); 3129 EXPECT_FALSE(cache->online_whitelist_all_);
3088 3130
3089 EXPECT_TRUE(cache->update_time_ > base::Time()); 3131 EXPECT_TRUE(cache->update_time_ > base::Time());
3090 } 3132 }
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
3146 EXPECT_EQ(AppCacheEntry::EXPLICIT | AppCacheEntry::MASTER, entry->types()); 3188 EXPECT_EQ(AppCacheEntry::EXPLICIT | AppCacheEntry::MASTER, entry->types());
3147 EXPECT_TRUE(entry->has_response_id()); 3189 EXPECT_TRUE(entry->has_response_id());
3148 3190
3149 EXPECT_TRUE(cache->fallback_namespaces_.empty()); 3191 EXPECT_TRUE(cache->fallback_namespaces_.empty());
3150 EXPECT_TRUE(cache->online_whitelist_namespaces_.empty()); 3192 EXPECT_TRUE(cache->online_whitelist_namespaces_.empty());
3151 EXPECT_FALSE(cache->online_whitelist_all_); 3193 EXPECT_FALSE(cache->online_whitelist_all_);
3152 3194
3153 EXPECT_TRUE(cache->update_time_ > base::Time()); 3195 EXPECT_TRUE(cache->update_time_ > base::Time());
3154 } 3196 }
3155 3197
3198 void VerifyManifestWithIntercept(AppCache* cache) {
3199 EXPECT_EQ(2u, cache->entries().size());
3200 const char* kManifestPath = "files/manifest-with-intercept";
3201 AppCacheEntry* entry =
3202 cache->GetEntry(MockHttpServer::GetMockUrl(kManifestPath));
3203 ASSERT_TRUE(entry);
3204 EXPECT_EQ(AppCacheEntry::MANIFEST, entry->types());
3205 entry = cache->GetEntry(MockHttpServer::GetMockUrl("files/intercept1a"));
3206 ASSERT_TRUE(entry);
3207 EXPECT_TRUE(entry->IsIntercept());
3208 EXPECT_TRUE(cache->online_whitelist_namespaces_.empty());
3209 EXPECT_FALSE(cache->online_whitelist_all_);
3210 EXPECT_TRUE(cache->update_time_ > base::Time());
3211 }
3212
3156 private: 3213 private:
3157 // Various manifest files used in this test. 3214 // Various manifest files used in this test.
3158 enum TestedManifest { 3215 enum TestedManifest {
3159 NONE, 3216 NONE,
3160 MANIFEST1, 3217 MANIFEST1,
3161 MANIFEST_MERGED_TYPES, 3218 MANIFEST_MERGED_TYPES,
3162 EMPTY_MANIFEST, 3219 EMPTY_MANIFEST,
3163 EMPTY_FILE_MANIFEST, 3220 EMPTY_FILE_MANIFEST,
3164 PENDING_MASTER_NO_UPDATE, 3221 PENDING_MASTER_NO_UPDATE,
3222 MANIFEST_WITH_INTERCEPT
3165 }; 3223 };
3166 3224
3167 scoped_ptr<IOThread> io_thread_; 3225 scoped_ptr<IOThread> io_thread_;
3168 3226
3169 scoped_ptr<MockAppCacheService> service_; 3227 scoped_ptr<MockAppCacheService> service_;
3170 scoped_refptr<AppCacheGroup> group_; 3228 scoped_refptr<AppCacheGroup> group_;
3171 scoped_refptr<AppCache> protect_newest_cache_; 3229 scoped_refptr<AppCache> protect_newest_cache_;
3172 scoped_ptr<base::WaitableEvent> event_; 3230 scoped_ptr<base::WaitableEvent> event_;
3173 3231
3174 scoped_ptr<AppCacheResponseWriter> response_writer_; 3232 scoped_ptr<AppCacheResponseWriter> response_writer_;
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after
3300 } 3358 }
3301 3359
3302 TEST_F(AppCacheUpdateJobTest, UpgradeManifestDataUnchanged) { 3360 TEST_F(AppCacheUpdateJobTest, UpgradeManifestDataUnchanged) {
3303 RunTestOnIOThread(&AppCacheUpdateJobTest::UpgradeManifestDataUnchangedTest); 3361 RunTestOnIOThread(&AppCacheUpdateJobTest::UpgradeManifestDataUnchangedTest);
3304 } 3362 }
3305 3363
3306 TEST_F(AppCacheUpdateJobTest, BasicCacheAttemptSuccess) { 3364 TEST_F(AppCacheUpdateJobTest, BasicCacheAttemptSuccess) {
3307 RunTestOnIOThread(&AppCacheUpdateJobTest::BasicCacheAttemptSuccessTest); 3365 RunTestOnIOThread(&AppCacheUpdateJobTest::BasicCacheAttemptSuccessTest);
3308 } 3366 }
3309 3367
3368 TEST_F(AppCacheUpdateJobTest, DownloadInterceptEntriesTest) {
3369 RunTestOnIOThread(&AppCacheUpdateJobTest::DownloadInterceptEntriesTest);
3370 }
3371
3310 TEST_F(AppCacheUpdateJobTest, BasicUpgradeSuccess) { 3372 TEST_F(AppCacheUpdateJobTest, BasicUpgradeSuccess) {
3311 RunTestOnIOThread(&AppCacheUpdateJobTest::BasicUpgradeSuccessTest); 3373 RunTestOnIOThread(&AppCacheUpdateJobTest::BasicUpgradeSuccessTest);
3312 } 3374 }
3313 3375
3314 TEST_F(AppCacheUpdateJobTest, UpgradeLoadFromNewestCache) { 3376 TEST_F(AppCacheUpdateJobTest, UpgradeLoadFromNewestCache) {
3315 RunTestOnIOThread(&AppCacheUpdateJobTest::UpgradeLoadFromNewestCacheTest); 3377 RunTestOnIOThread(&AppCacheUpdateJobTest::UpgradeLoadFromNewestCacheTest);
3316 } 3378 }
3317 3379
3318 TEST_F(AppCacheUpdateJobTest, UpgradeNoLoadFromNewestCache) { 3380 TEST_F(AppCacheUpdateJobTest, UpgradeNoLoadFromNewestCache) {
3319 RunTestOnIOThread(&AppCacheUpdateJobTest::UpgradeNoLoadFromNewestCacheTest); 3381 RunTestOnIOThread(&AppCacheUpdateJobTest::UpgradeNoLoadFromNewestCacheTest);
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after
3459 3521
3460 TEST_F(AppCacheUpdateJobTest, CrossOriginHttpsSuccess) { 3522 TEST_F(AppCacheUpdateJobTest, CrossOriginHttpsSuccess) {
3461 RunTestOnIOThread(&AppCacheUpdateJobTest::CrossOriginHttpsSuccessTest); 3523 RunTestOnIOThread(&AppCacheUpdateJobTest::CrossOriginHttpsSuccessTest);
3462 } 3524 }
3463 3525
3464 TEST_F(AppCacheUpdateJobTest, CrossOriginHttpsDenied) { 3526 TEST_F(AppCacheUpdateJobTest, CrossOriginHttpsDenied) {
3465 RunTestOnIOThread(&AppCacheUpdateJobTest::CrossOriginHttpsDeniedTest); 3527 RunTestOnIOThread(&AppCacheUpdateJobTest::CrossOriginHttpsDeniedTest);
3466 } 3528 }
3467 3529
3468 } // namespace appcache 3530 } // namespace appcache
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698