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