1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/bind.h" 6 #include "base/file_util.h" 7 #include "base/files/scoped_temp_dir.h" 8 #include "base/message_loop/message_loop.h" 9 #include "base/message_loop/message_loop_proxy.h" 10 #include "base/strings/utf_string_conversions.h" 11 #include "base/threading/sequenced_worker_pool.h" 12 #include "base/time/time.h" 13 #include "content/browser/dom_storage/dom_storage_area.h" 14 #include "content/browser/dom_storage/dom_storage_context_impl.h" 15 #include "content/browser/dom_storage/dom_storage_namespace.h" 16 #include "content/browser/dom_storage/dom_storage_task_runner.h" 17 #include "content/public/browser/local_storage_usage_info.h" 18 #include "content/public/browser/session_storage_namespace.h" 19 #include "content/public/browser/session_storage_usage_info.h" 20 #include "content/public/test/mock_special_storage_policy.h" 21 #include "testing/gtest/include/gtest/gtest.h" 22 23 using base::ASCIIToUTF16; 24 25 namespace content { 26 27 class DOMStorageContextImplTest : public testing::Test { 28 public: 29 DOMStorageContextImplTest() 30 : kOrigin(GURL("http://dom_storage/")), 31 kKey(ASCIIToUTF16("key")), 32 kValue(ASCIIToUTF16("value")), 33 kDontIncludeFileInfo(false), 34 kDoIncludeFileInfo(true) { 35 } 36 37 const GURL kOrigin; 38 const base::string16 kKey; 39 const base::string16 kValue; 40 const bool kDontIncludeFileInfo; 41 const bool kDoIncludeFileInfo; 42 43 virtual void SetUp() { 44 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 45 storage_policy_ = new MockSpecialStoragePolicy; 46 task_runner_ = 47 new MockDOMStorageTaskRunner(base::MessageLoopProxy::current().get()); 48 context_ = new DOMStorageContextImpl(temp_dir_.path(), 49 base::FilePath(), 50 storage_policy_.get(), 51 task_runner_.get()); 52 } 53 54 virtual void TearDown() { 55 base::MessageLoop::current()->RunUntilIdle(); 56 } 57 58 void VerifySingleOriginRemains(const GURL& origin) { 59 // Use a new instance to examine the contexts of temp_dir_. 60 scoped_refptr<DOMStorageContextImpl> context = 61 new DOMStorageContextImpl(temp_dir_.path(), base::FilePath(), 62 NULL, NULL); 63 std::vector<LocalStorageUsageInfo> infos; 64 context->GetLocalStorageUsage(&infos, kDontIncludeFileInfo); 65 ASSERT_EQ(1u, infos.size()); 66 EXPECT_EQ(origin, infos[0].origin); 67 } 68 69 protected: 70 base::MessageLoop message_loop_; 71 base::ScopedTempDir temp_dir_; 72 scoped_refptr<MockSpecialStoragePolicy> storage_policy_; 73 scoped_refptr<MockDOMStorageTaskRunner> task_runner_; 74 scoped_refptr<DOMStorageContextImpl> context_; 75 DISALLOW_COPY_AND_ASSIGN(DOMStorageContextImplTest); 76 }; 77 78 TEST_F(DOMStorageContextImplTest, Basics) { 79 // This test doesn't do much, checks that the constructor 80 // initializes members properly and that invoking methods 81 // on a newly created object w/o any data on disk do no harm. 82 EXPECT_EQ(temp_dir_.path(), context_->localstorage_directory()); 83 EXPECT_EQ(base::FilePath(), context_->sessionstorage_directory()); 84 EXPECT_EQ(storage_policy_.get(), context_->special_storage_policy_.get()); 85 context_->DeleteLocalStorage(GURL("http://chromium.org/")); 86 const int kFirstSessionStorageNamespaceId = 1; 87 EXPECT_TRUE(context_->GetStorageNamespace(kLocalStorageNamespaceId)); 88 EXPECT_FALSE(context_->GetStorageNamespace(kFirstSessionStorageNamespaceId)); 89 EXPECT_EQ(kFirstSessionStorageNamespaceId, context_->AllocateSessionId()); 90 std::vector<LocalStorageUsageInfo> infos; 91 context_->GetLocalStorageUsage(&infos, kDontIncludeFileInfo); 92 EXPECT_TRUE(infos.empty()); 93 context_->Shutdown(); 94 } 95 96 TEST_F(DOMStorageContextImplTest, UsageInfo) { 97 // Should be empty initially 98 std::vector<LocalStorageUsageInfo> infos; 99 context_->GetLocalStorageUsage(&infos, kDontIncludeFileInfo); 100 EXPECT_TRUE(infos.empty()); 101 context_->GetLocalStorageUsage(&infos, kDoIncludeFileInfo); 102 EXPECT_TRUE(infos.empty()); 103 104 // Put some data into local storage and shutdown the context 105 // to ensure data is written to disk. 106 base::NullableString16 old_value; 107 EXPECT_TRUE(context_->GetStorageNamespace(kLocalStorageNamespaceId)-> 108 OpenStorageArea(kOrigin)->SetItem(kKey, kValue, &old_value)); 109 context_->Shutdown(); 110 context_ = NULL; 111 base::MessageLoop::current()->RunUntilIdle(); 112 113 // Create a new context that points to the same directory, see that 114 // it knows about the origin that we stored data for. 115 context_ = new DOMStorageContextImpl(temp_dir_.path(), base::FilePath(), 116 NULL, NULL); 117 context_->GetLocalStorageUsage(&infos, kDontIncludeFileInfo); 118 EXPECT_EQ(1u, infos.size()); 119 EXPECT_EQ(kOrigin, infos[0].origin); 120 EXPECT_EQ(0u, infos[0].data_size); 121 EXPECT_EQ(base::Time(), infos[0].last_modified); 122 infos.clear(); 123 context_->GetLocalStorageUsage(&infos, kDoIncludeFileInfo); 124 EXPECT_EQ(1u, infos.size()); 125 EXPECT_EQ(kOrigin, infos[0].origin); 126 EXPECT_NE(0u, infos[0].data_size); 127 EXPECT_NE(base::Time(), infos[0].last_modified); 128 } 129 130 TEST_F(DOMStorageContextImplTest, SessionOnly) { 131 const GURL kSessionOnlyOrigin("http://www.sessiononly.com/"); 132 storage_policy_->AddSessionOnly(kSessionOnlyOrigin); 133 134 // Store data for a normal and a session-only origin and then 135 // invoke Shutdown() which should delete data for session-only 136 // origins. 137 base::NullableString16 old_value; 138 EXPECT_TRUE(context_->GetStorageNamespace(kLocalStorageNamespaceId)-> 139 OpenStorageArea(kOrigin)->SetItem(kKey, kValue, &old_value)); 140 EXPECT_TRUE(context_->GetStorageNamespace(kLocalStorageNamespaceId)-> 141 OpenStorageArea(kSessionOnlyOrigin)->SetItem(kKey, kValue, &old_value)); 142 context_->Shutdown(); 143 context_ = NULL; 144 base::MessageLoop::current()->RunUntilIdle(); 145 146 // Verify that the session-only origin data is gone. 147 VerifySingleOriginRemains(kOrigin); 148 } 149 150 TEST_F(DOMStorageContextImplTest, SetForceKeepSessionState) { 151 const GURL kSessionOnlyOrigin("http://www.sessiononly.com/"); 152 storage_policy_->AddSessionOnly(kSessionOnlyOrigin); 153 154 // Store data for a session-only origin, setup to save session data, then 155 // shutdown. 156 base::NullableString16 old_value; 157 EXPECT_TRUE(context_->GetStorageNamespace(kLocalStorageNamespaceId)-> 158 OpenStorageArea(kSessionOnlyOrigin)->SetItem(kKey, kValue, &old_value)); 159 context_->SetForceKeepSessionState(); // Should override clear behavior. 160 context_->Shutdown(); 161 context_ = NULL; 162 base::MessageLoop::current()->RunUntilIdle(); 163 164 VerifySingleOriginRemains(kSessionOnlyOrigin); 165 } 166 167 TEST_F(DOMStorageContextImplTest, PersistentIds) { 168 const int kFirstSessionStorageNamespaceId = 1; 169 const std::string kPersistentId = "persistent"; 170 context_->CreateSessionNamespace(kFirstSessionStorageNamespaceId, 171 kPersistentId); 172 DOMStorageNamespace* dom_namespace = 173 context_->GetStorageNamespace(kFirstSessionStorageNamespaceId); 174 ASSERT_TRUE(dom_namespace); 175 EXPECT_EQ(kPersistentId, dom_namespace->persistent_namespace_id()); 176 // Verify that the areas inherit the persistent ID. 177 DOMStorageArea* area = dom_namespace->OpenStorageArea(kOrigin); 178 EXPECT_EQ(kPersistentId, area->persistent_namespace_id_); 179 180 // Verify that the persistent IDs are handled correctly when cloning. 181 const int kClonedSessionStorageNamespaceId = 2; 182 const std::string kClonedPersistentId = "cloned"; 183 context_->CloneSessionNamespace(kFirstSessionStorageNamespaceId, 184 kClonedSessionStorageNamespaceId, 185 kClonedPersistentId); 186 DOMStorageNamespace* cloned_dom_namespace = 187 context_->GetStorageNamespace(kClonedSessionStorageNamespaceId); 188 ASSERT_TRUE(dom_namespace); 189 EXPECT_EQ(kClonedPersistentId, 190 cloned_dom_namespace->persistent_namespace_id()); 191 // Verify that the areas inherit the persistent ID. 192 DOMStorageArea* cloned_area = cloned_dom_namespace->OpenStorageArea(kOrigin); 193 EXPECT_EQ(kClonedPersistentId, cloned_area->persistent_namespace_id_); 194 } 195 196 TEST_F(DOMStorageContextImplTest, DeleteSessionStorage) { 197 // Create a DOMStorageContextImpl which will save sessionStorage on disk. 198 context_ = new DOMStorageContextImpl(temp_dir_.path(), 199 temp_dir_.path(), 200 storage_policy_.get(), 201 task_runner_.get()); 202 context_->SetSaveSessionStorageOnDisk(); 203 ASSERT_EQ(temp_dir_.path(), context_->sessionstorage_directory()); 204 205 // Write data. 206 const int kSessionStorageNamespaceId = 1; 207 const std::string kPersistentId = "persistent"; 208 context_->CreateSessionNamespace(kSessionStorageNamespaceId, 209 kPersistentId); 210 DOMStorageNamespace* dom_namespace = 211 context_->GetStorageNamespace(kSessionStorageNamespaceId); 212 DOMStorageArea* area = dom_namespace->OpenStorageArea(kOrigin); 213 const base::string16 kKey(ASCIIToUTF16("foo")); 214 const base::string16 kValue(ASCIIToUTF16("bar")); 215 base::NullableString16 old_nullable_value; 216 area->SetItem(kKey, kValue, &old_nullable_value); 217 dom_namespace->CloseStorageArea(area); 218 219 // Destroy and recreate the DOMStorageContextImpl. 220 context_->Shutdown(); 221 context_ = NULL; 222 base::MessageLoop::current()->RunUntilIdle(); 223 context_ = new DOMStorageContextImpl( 224 temp_dir_.path(), temp_dir_.path(), 225 storage_policy_.get(), task_runner_.get()); 226 context_->SetSaveSessionStorageOnDisk(); 227 228 // Read the data back. 229 context_->CreateSessionNamespace(kSessionStorageNamespaceId, 230 kPersistentId); 231 dom_namespace = context_->GetStorageNamespace(kSessionStorageNamespaceId); 232 area = dom_namespace->OpenStorageArea(kOrigin); 233 base::NullableString16 read_value; 234 read_value = area->GetItem(kKey); 235 EXPECT_EQ(kValue, read_value.string()); 236 dom_namespace->CloseStorageArea(area); 237 238 SessionStorageUsageInfo info; 239 info.origin = kOrigin; 240 info.persistent_namespace_id = kPersistentId; 241 context_->DeleteSessionStorage(info); 242 243 // Destroy and recreate again. 244 context_->Shutdown(); 245 context_ = NULL; 246 base::MessageLoop::current()->RunUntilIdle(); 247 context_ = new DOMStorageContextImpl( 248 temp_dir_.path(), temp_dir_.path(), 249 storage_policy_.get(), task_runner_.get()); 250 context_->SetSaveSessionStorageOnDisk(); 251 252 // Now there should be no data. 253 context_->CreateSessionNamespace(kSessionStorageNamespaceId, 254 kPersistentId); 255 dom_namespace = context_->GetStorageNamespace(kSessionStorageNamespaceId); 256 area = dom_namespace->OpenStorageArea(kOrigin); 257 read_value = area->GetItem(kKey); 258 EXPECT_TRUE(read_value.is_null()); 259 dom_namespace->CloseStorageArea(area); 260 context_->Shutdown(); 261 context_ = NULL; 262 base::MessageLoop::current()->RunUntilIdle(); 263 } 264 265 TEST_F(DOMStorageContextImplTest, SessionStorageAlias) { 266 const int kFirstSessionStorageNamespaceId = 1; 267 const std::string kPersistentId = "persistent"; 268 context_->CreateSessionNamespace(kFirstSessionStorageNamespaceId, 269 kPersistentId); 270 DOMStorageNamespace* dom_namespace1 = 271 context_->GetStorageNamespace(kFirstSessionStorageNamespaceId); 272 ASSERT_TRUE(dom_namespace1); 273 DOMStorageArea* area1 = dom_namespace1->OpenStorageArea(kOrigin); 274 base::NullableString16 old_value; 275 area1->SetItem(kKey, kValue, &old_value); 276 EXPECT_TRUE(old_value.is_null()); 277 base::NullableString16 read_value = area1->GetItem(kKey); 278 EXPECT_TRUE(!read_value.is_null() && read_value.string() == kValue); 279 280 // Create an alias. 281 const int kAliasSessionStorageNamespaceId = 2; 282 context_->CreateAliasSessionNamespace(kFirstSessionStorageNamespaceId, 283 kAliasSessionStorageNamespaceId, 284 kPersistentId); 285 DOMStorageNamespace* dom_namespace2 = 286 context_->GetStorageNamespace(kAliasSessionStorageNamespaceId); 287 ASSERT_TRUE(dom_namespace2); 288 ASSERT_TRUE(dom_namespace2->alias_master_namespace() == dom_namespace1); 289 290 // Verify that read values are identical. 291 DOMStorageArea* area2 = dom_namespace2->OpenStorageArea(kOrigin); 292 read_value = area2->GetItem(kKey); 293 EXPECT_TRUE(!read_value.is_null() && read_value.string() == kValue); 294 295 // Verify that writes are reflected in both namespaces. 296 const base::string16 kValue2(ASCIIToUTF16("value2")); 297 area2->SetItem(kKey, kValue2, &old_value); 298 read_value = area1->GetItem(kKey); 299 EXPECT_TRUE(!read_value.is_null() && read_value.string() == kValue2); 300 dom_namespace1->CloseStorageArea(area1); 301 dom_namespace2->CloseStorageArea(area2); 302 303 // When creating an alias of an alias, ensure that the master relationship 304 // is collapsed. 305 const int kAlias2SessionStorageNamespaceId = 3; 306 context_->CreateAliasSessionNamespace(kAliasSessionStorageNamespaceId, 307 kAlias2SessionStorageNamespaceId, 308 kPersistentId); 309 DOMStorageNamespace* dom_namespace3 = 310 context_->GetStorageNamespace(kAlias2SessionStorageNamespaceId); 311 ASSERT_TRUE(dom_namespace3); 312 ASSERT_TRUE(dom_namespace3->alias_master_namespace() == dom_namespace1); 313 } 314 315 TEST_F(DOMStorageContextImplTest, SessionStorageMerge) { 316 // Create a target namespace that we will merge into. 317 const int kTargetSessionStorageNamespaceId = 1; 318 const std::string kTargetPersistentId = "persistent"; 319 context_->CreateSessionNamespace(kTargetSessionStorageNamespaceId, 320 kTargetPersistentId); 321 DOMStorageNamespace* target_ns = 322 context_->GetStorageNamespace(kTargetSessionStorageNamespaceId); 323 ASSERT_TRUE(target_ns); 324 DOMStorageArea* target_ns_area = target_ns->OpenStorageArea(kOrigin); 325 base::NullableString16 old_value; 326 const base::string16 kKey2(ASCIIToUTF16("key2")); 327 const base::string16 kKey2Value(ASCIIToUTF16("key2value")); 328 target_ns_area->SetItem(kKey, kValue, &old_value); 329 target_ns_area->SetItem(kKey2, kKey2Value, &old_value); 330 331 // Create a source namespace & its alias. 332 const int kSourceSessionStorageNamespaceId = 2; 333 const int kAliasSessionStorageNamespaceId = 3; 334 const std::string kSourcePersistentId = "persistent_source"; 335 context_->CreateSessionNamespace(kSourceSessionStorageNamespaceId, 336 kSourcePersistentId); 337 context_->CreateAliasSessionNamespace(kSourceSessionStorageNamespaceId, 338 kAliasSessionStorageNamespaceId, 339 kSourcePersistentId); 340 DOMStorageNamespace* alias_ns = 341 context_->GetStorageNamespace(kAliasSessionStorageNamespaceId); 342 ASSERT_TRUE(alias_ns); 343 344 // Create a transaction log that can't be merged. 345 const int kPid1 = 10; 346 ASSERT_FALSE(alias_ns->IsLoggingRenderer(kPid1)); 347 alias_ns->AddTransactionLogProcessId(kPid1); 348 ASSERT_TRUE(alias_ns->IsLoggingRenderer(kPid1)); 349 const base::string16 kValue2(ASCIIToUTF16("value2")); 350 DOMStorageNamespace::TransactionRecord txn; 351 txn.origin = kOrigin; 352 txn.key = kKey; 353 txn.value = base::NullableString16(kValue2, false); 354 txn.transaction_type = DOMStorageNamespace::TRANSACTION_READ; 355 alias_ns->AddTransaction(kPid1, txn); 356 ASSERT_TRUE(alias_ns->Merge(false, kPid1, target_ns, NULL) == 357 SessionStorageNamespace::MERGE_RESULT_NOT_MERGEABLE); 358 359 // Create a transaction log that can be merged. 360 const int kPid2 = 20; 361 alias_ns->AddTransactionLogProcessId(kPid2); 362 txn.transaction_type = DOMStorageNamespace::TRANSACTION_WRITE; 363 alias_ns->AddTransaction(kPid2, txn); 364 ASSERT_TRUE(alias_ns->Merge(true, kPid2, target_ns, NULL) == 365 SessionStorageNamespace::MERGE_RESULT_MERGEABLE); 366 367 // Verify that the merge was successful. 368 ASSERT_TRUE(alias_ns->alias_master_namespace() == target_ns); 369 base::NullableString16 read_value = target_ns_area->GetItem(kKey); 370 EXPECT_TRUE(!read_value.is_null() && read_value.string() == kValue2); 371 DOMStorageArea* alias_ns_area = alias_ns->OpenStorageArea(kOrigin); 372 read_value = alias_ns_area->GetItem(kKey2); 373 EXPECT_TRUE(!read_value.is_null() && read_value.string() == kKey2Value); 374 } 375 376 } // namespace content 377