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