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/files/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"
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 void SetUp() override
{
44 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
45 storage_policy_
= new MockSpecialStoragePolicy
;
47 new MockDOMStorageTaskRunner(base::MessageLoopProxy::current().get());
48 context_
= new DOMStorageContextImpl(temp_dir_
.path(),
50 storage_policy_
.get(),
54 void TearDown() override
{ base::MessageLoop::current()->RunUntilIdle(); }
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(),
61 std::vector
<LocalStorageUsageInfo
> infos
;
62 context
->GetLocalStorageUsage(&infos
, kDontIncludeFileInfo
);
63 ASSERT_EQ(1u, infos
.size());
64 EXPECT_EQ(origin
, infos
[0].origin
);
68 base::MessageLoop message_loop_
;
69 base::ScopedTempDir temp_dir_
;
70 scoped_refptr
<MockSpecialStoragePolicy
> storage_policy_
;
71 scoped_refptr
<MockDOMStorageTaskRunner
> task_runner_
;
72 scoped_refptr
<DOMStorageContextImpl
> context_
;
73 DISALLOW_COPY_AND_ASSIGN(DOMStorageContextImplTest
);
76 TEST_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_
->DeleteLocalStorage(GURL("http://chromium.org/"));
84 const int kFirstSessionStorageNamespaceId
= 1;
85 EXPECT_TRUE(context_
->GetStorageNamespace(kLocalStorageNamespaceId
));
86 EXPECT_FALSE(context_
->GetStorageNamespace(kFirstSessionStorageNamespaceId
));
87 EXPECT_EQ(kFirstSessionStorageNamespaceId
, context_
->AllocateSessionId());
88 std::vector
<LocalStorageUsageInfo
> infos
;
89 context_
->GetLocalStorageUsage(&infos
, kDontIncludeFileInfo
);
90 EXPECT_TRUE(infos
.empty());
94 TEST_F(DOMStorageContextImplTest
, UsageInfo
) {
95 // Should be empty initially
96 std::vector
<LocalStorageUsageInfo
> infos
;
97 context_
->GetLocalStorageUsage(&infos
, kDontIncludeFileInfo
);
98 EXPECT_TRUE(infos
.empty());
99 context_
->GetLocalStorageUsage(&infos
, kDoIncludeFileInfo
);
100 EXPECT_TRUE(infos
.empty());
102 // Put some data into local storage and shutdown the context
103 // to ensure data is written to disk.
104 base::NullableString16 old_value
;
105 EXPECT_TRUE(context_
->GetStorageNamespace(kLocalStorageNamespaceId
)->
106 OpenStorageArea(kOrigin
)->SetItem(kKey
, kValue
, &old_value
));
107 context_
->Shutdown();
109 base::MessageLoop::current()->RunUntilIdle();
111 // Create a new context that points to the same directory, see that
112 // it knows about the origin that we stored data for.
113 context_
= new DOMStorageContextImpl(temp_dir_
.path(), base::FilePath(),
115 context_
->GetLocalStorageUsage(&infos
, kDontIncludeFileInfo
);
116 EXPECT_EQ(1u, infos
.size());
117 EXPECT_EQ(kOrigin
, infos
[0].origin
);
118 EXPECT_EQ(0u, infos
[0].data_size
);
119 EXPECT_EQ(base::Time(), infos
[0].last_modified
);
121 context_
->GetLocalStorageUsage(&infos
, kDoIncludeFileInfo
);
122 EXPECT_EQ(1u, infos
.size());
123 EXPECT_EQ(kOrigin
, infos
[0].origin
);
124 EXPECT_NE(0u, infos
[0].data_size
);
125 EXPECT_NE(base::Time(), infos
[0].last_modified
);
128 TEST_F(DOMStorageContextImplTest
, SessionOnly
) {
129 const GURL
kSessionOnlyOrigin("http://www.sessiononly.com/");
130 storage_policy_
->AddSessionOnly(kSessionOnlyOrigin
);
132 // Store data for a normal and a session-only origin and then
133 // invoke Shutdown() which should delete data for session-only
135 base::NullableString16 old_value
;
136 EXPECT_TRUE(context_
->GetStorageNamespace(kLocalStorageNamespaceId
)->
137 OpenStorageArea(kOrigin
)->SetItem(kKey
, kValue
, &old_value
));
138 EXPECT_TRUE(context_
->GetStorageNamespace(kLocalStorageNamespaceId
)->
139 OpenStorageArea(kSessionOnlyOrigin
)->SetItem(kKey
, kValue
, &old_value
));
140 context_
->Shutdown();
142 base::MessageLoop::current()->RunUntilIdle();
144 // Verify that the session-only origin data is gone.
145 VerifySingleOriginRemains(kOrigin
);
148 TEST_F(DOMStorageContextImplTest
, SetForceKeepSessionState
) {
149 const GURL
kSessionOnlyOrigin("http://www.sessiononly.com/");
150 storage_policy_
->AddSessionOnly(kSessionOnlyOrigin
);
152 // Store data for a session-only origin, setup to save session data, then
154 base::NullableString16 old_value
;
155 EXPECT_TRUE(context_
->GetStorageNamespace(kLocalStorageNamespaceId
)->
156 OpenStorageArea(kSessionOnlyOrigin
)->SetItem(kKey
, kValue
, &old_value
));
157 context_
->SetForceKeepSessionState(); // Should override clear behavior.
158 context_
->Shutdown();
160 base::MessageLoop::current()->RunUntilIdle();
162 VerifySingleOriginRemains(kSessionOnlyOrigin
);
165 TEST_F(DOMStorageContextImplTest
, PersistentIds
) {
166 const int kFirstSessionStorageNamespaceId
= 1;
167 const std::string kPersistentId
= "persistent";
168 context_
->CreateSessionNamespace(kFirstSessionStorageNamespaceId
,
170 DOMStorageNamespace
* dom_namespace
=
171 context_
->GetStorageNamespace(kFirstSessionStorageNamespaceId
);
172 ASSERT_TRUE(dom_namespace
);
173 EXPECT_EQ(kPersistentId
, dom_namespace
->persistent_namespace_id());
174 // Verify that the areas inherit the persistent ID.
175 DOMStorageArea
* area
= dom_namespace
->OpenStorageArea(kOrigin
);
176 EXPECT_EQ(kPersistentId
, area
->persistent_namespace_id_
);
178 // Verify that the persistent IDs are handled correctly when cloning.
179 const int kClonedSessionStorageNamespaceId
= 2;
180 const std::string kClonedPersistentId
= "cloned";
181 context_
->CloneSessionNamespace(kFirstSessionStorageNamespaceId
,
182 kClonedSessionStorageNamespaceId
,
183 kClonedPersistentId
);
184 DOMStorageNamespace
* cloned_dom_namespace
=
185 context_
->GetStorageNamespace(kClonedSessionStorageNamespaceId
);
186 ASSERT_TRUE(dom_namespace
);
187 EXPECT_EQ(kClonedPersistentId
,
188 cloned_dom_namespace
->persistent_namespace_id());
189 // Verify that the areas inherit the persistent ID.
190 DOMStorageArea
* cloned_area
= cloned_dom_namespace
->OpenStorageArea(kOrigin
);
191 EXPECT_EQ(kClonedPersistentId
, cloned_area
->persistent_namespace_id_
);
194 TEST_F(DOMStorageContextImplTest
, DeleteSessionStorage
) {
195 // Create a DOMStorageContextImpl which will save sessionStorage on disk.
196 context_
= new DOMStorageContextImpl(temp_dir_
.path(),
198 storage_policy_
.get(),
200 context_
->SetSaveSessionStorageOnDisk();
201 ASSERT_EQ(temp_dir_
.path(), context_
->sessionstorage_directory());
204 const int kSessionStorageNamespaceId
= 1;
205 const std::string kPersistentId
= "persistent";
206 context_
->CreateSessionNamespace(kSessionStorageNamespaceId
,
208 DOMStorageNamespace
* dom_namespace
=
209 context_
->GetStorageNamespace(kSessionStorageNamespaceId
);
210 DOMStorageArea
* area
= dom_namespace
->OpenStorageArea(kOrigin
);
211 const base::string16
kKey(ASCIIToUTF16("foo"));
212 const base::string16
kValue(ASCIIToUTF16("bar"));
213 base::NullableString16 old_nullable_value
;
214 area
->SetItem(kKey
, kValue
, &old_nullable_value
);
215 dom_namespace
->CloseStorageArea(area
);
217 // Destroy and recreate the DOMStorageContextImpl.
218 context_
->Shutdown();
220 base::MessageLoop::current()->RunUntilIdle();
221 context_
= new DOMStorageContextImpl(
222 temp_dir_
.path(), temp_dir_
.path(),
223 storage_policy_
.get(), task_runner_
.get());
224 context_
->SetSaveSessionStorageOnDisk();
226 // Read the data back.
227 context_
->CreateSessionNamespace(kSessionStorageNamespaceId
,
229 dom_namespace
= context_
->GetStorageNamespace(kSessionStorageNamespaceId
);
230 area
= dom_namespace
->OpenStorageArea(kOrigin
);
231 base::NullableString16 read_value
;
232 read_value
= area
->GetItem(kKey
);
233 EXPECT_EQ(kValue
, read_value
.string());
234 dom_namespace
->CloseStorageArea(area
);
236 SessionStorageUsageInfo info
;
237 info
.origin
= kOrigin
;
238 info
.persistent_namespace_id
= kPersistentId
;
239 context_
->DeleteSessionStorage(info
);
241 // Destroy and recreate again.
242 context_
->Shutdown();
244 base::MessageLoop::current()->RunUntilIdle();
245 context_
= new DOMStorageContextImpl(
246 temp_dir_
.path(), temp_dir_
.path(),
247 storage_policy_
.get(), task_runner_
.get());
248 context_
->SetSaveSessionStorageOnDisk();
250 // Now there should be no data.
251 context_
->CreateSessionNamespace(kSessionStorageNamespaceId
,
253 dom_namespace
= context_
->GetStorageNamespace(kSessionStorageNamespaceId
);
254 area
= dom_namespace
->OpenStorageArea(kOrigin
);
255 read_value
= area
->GetItem(kKey
);
256 EXPECT_TRUE(read_value
.is_null());
257 dom_namespace
->CloseStorageArea(area
);
258 context_
->Shutdown();
260 base::MessageLoop::current()->RunUntilIdle();
263 TEST_F(DOMStorageContextImplTest
, SessionStorageAlias
) {
264 const int kFirstSessionStorageNamespaceId
= 1;
265 const std::string kPersistentId
= "persistent";
266 context_
->CreateSessionNamespace(kFirstSessionStorageNamespaceId
,
268 DOMStorageNamespace
* dom_namespace1
=
269 context_
->GetStorageNamespace(kFirstSessionStorageNamespaceId
);
270 ASSERT_TRUE(dom_namespace1
);
271 DOMStorageArea
* area1
= dom_namespace1
->OpenStorageArea(kOrigin
);
272 base::NullableString16 old_value
;
273 area1
->SetItem(kKey
, kValue
, &old_value
);
274 EXPECT_TRUE(old_value
.is_null());
275 base::NullableString16 read_value
= area1
->GetItem(kKey
);
276 EXPECT_TRUE(!read_value
.is_null() && read_value
.string() == kValue
);
279 const int kAliasSessionStorageNamespaceId
= 2;
280 context_
->CreateAliasSessionNamespace(kFirstSessionStorageNamespaceId
,
281 kAliasSessionStorageNamespaceId
,
283 DOMStorageNamespace
* dom_namespace2
=
284 context_
->GetStorageNamespace(kAliasSessionStorageNamespaceId
);
285 ASSERT_TRUE(dom_namespace2
);
286 ASSERT_TRUE(dom_namespace2
->alias_master_namespace() == dom_namespace1
);
288 // Verify that read values are identical.
289 DOMStorageArea
* area2
= dom_namespace2
->OpenStorageArea(kOrigin
);
290 read_value
= area2
->GetItem(kKey
);
291 EXPECT_TRUE(!read_value
.is_null() && read_value
.string() == kValue
);
293 // Verify that writes are reflected in both namespaces.
294 const base::string16
kValue2(ASCIIToUTF16("value2"));
295 area2
->SetItem(kKey
, kValue2
, &old_value
);
296 read_value
= area1
->GetItem(kKey
);
297 EXPECT_TRUE(!read_value
.is_null() && read_value
.string() == kValue2
);
298 dom_namespace1
->CloseStorageArea(area1
);
299 dom_namespace2
->CloseStorageArea(area2
);
301 // When creating an alias of an alias, ensure that the master relationship
303 const int kAlias2SessionStorageNamespaceId
= 3;
304 context_
->CreateAliasSessionNamespace(kAliasSessionStorageNamespaceId
,
305 kAlias2SessionStorageNamespaceId
,
307 DOMStorageNamespace
* dom_namespace3
=
308 context_
->GetStorageNamespace(kAlias2SessionStorageNamespaceId
);
309 ASSERT_TRUE(dom_namespace3
);
310 ASSERT_TRUE(dom_namespace3
->alias_master_namespace() == dom_namespace1
);
313 TEST_F(DOMStorageContextImplTest
, SessionStorageMerge
) {
314 // Create a target namespace that we will merge into.
315 const int kTargetSessionStorageNamespaceId
= 1;
316 const std::string kTargetPersistentId
= "persistent";
317 context_
->CreateSessionNamespace(kTargetSessionStorageNamespaceId
,
318 kTargetPersistentId
);
319 DOMStorageNamespace
* target_ns
=
320 context_
->GetStorageNamespace(kTargetSessionStorageNamespaceId
);
321 ASSERT_TRUE(target_ns
);
322 DOMStorageArea
* target_ns_area
= target_ns
->OpenStorageArea(kOrigin
);
323 base::NullableString16 old_value
;
324 const base::string16
kKey2(ASCIIToUTF16("key2"));
325 const base::string16
kKey2Value(ASCIIToUTF16("key2value"));
326 target_ns_area
->SetItem(kKey
, kValue
, &old_value
);
327 target_ns_area
->SetItem(kKey2
, kKey2Value
, &old_value
);
329 // Create a source namespace & its alias.
330 const int kSourceSessionStorageNamespaceId
= 2;
331 const int kAliasSessionStorageNamespaceId
= 3;
332 const std::string kSourcePersistentId
= "persistent_source";
333 context_
->CreateSessionNamespace(kSourceSessionStorageNamespaceId
,
334 kSourcePersistentId
);
335 context_
->CreateAliasSessionNamespace(kSourceSessionStorageNamespaceId
,
336 kAliasSessionStorageNamespaceId
,
337 kSourcePersistentId
);
338 DOMStorageNamespace
* alias_ns
=
339 context_
->GetStorageNamespace(kAliasSessionStorageNamespaceId
);
340 ASSERT_TRUE(alias_ns
);
342 // Create a transaction log that can't be merged.
343 const int kPid1
= 10;
344 ASSERT_FALSE(alias_ns
->IsLoggingRenderer(kPid1
));
345 alias_ns
->AddTransactionLogProcessId(kPid1
);
346 ASSERT_TRUE(alias_ns
->IsLoggingRenderer(kPid1
));
347 const base::string16
kValue2(ASCIIToUTF16("value2"));
348 DOMStorageNamespace::TransactionRecord txn
;
349 txn
.origin
= kOrigin
;
351 txn
.value
= base::NullableString16(kValue2
, false);
352 txn
.transaction_type
= DOMStorageNamespace::TRANSACTION_READ
;
353 alias_ns
->AddTransaction(kPid1
, txn
);
354 ASSERT_TRUE(alias_ns
->Merge(false, kPid1
, target_ns
, NULL
) ==
355 SessionStorageNamespace::MERGE_RESULT_NOT_MERGEABLE
);
357 // Create a transaction log that can be merged.
358 const int kPid2
= 20;
359 alias_ns
->AddTransactionLogProcessId(kPid2
);
360 txn
.transaction_type
= DOMStorageNamespace::TRANSACTION_WRITE
;
361 alias_ns
->AddTransaction(kPid2
, txn
);
362 ASSERT_TRUE(alias_ns
->Merge(true, kPid2
, target_ns
, NULL
) ==
363 SessionStorageNamespace::MERGE_RESULT_MERGEABLE
);
365 // Verify that the merge was successful.
366 ASSERT_TRUE(alias_ns
->alias_master_namespace() == target_ns
);
367 base::NullableString16 read_value
= target_ns_area
->GetItem(kKey
);
368 EXPECT_TRUE(!read_value
.is_null() && read_value
.string() == kValue2
);
369 DOMStorageArea
* alias_ns_area
= alias_ns
->OpenStorageArea(kOrigin
);
370 read_value
= alias_ns_area
->GetItem(kKey2
);
371 EXPECT_TRUE(!read_value
.is_null() && read_value
.string() == kKey2Value
);
374 } // namespace content