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 "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 virtual void SetUp() {
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 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
<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_
->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());
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());
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();
111 base::MessageLoop::current()->RunUntilIdle();
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(),
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
);
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
);
130 TEST_F(DOMStorageContextImplTest
, SessionOnly
) {
131 const GURL
kSessionOnlyOrigin("http://www.sessiononly.com/");
132 storage_policy_
->AddSessionOnly(kSessionOnlyOrigin
);
134 // Store data for a normal and a session-only origin and then
135 // invoke Shutdown() which should delete data for session-only
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();
144 base::MessageLoop::current()->RunUntilIdle();
146 // Verify that the session-only origin data is gone.
147 VerifySingleOriginRemains(kOrigin
);
150 TEST_F(DOMStorageContextImplTest
, SetForceKeepSessionState
) {
151 const GURL
kSessionOnlyOrigin("http://www.sessiononly.com/");
152 storage_policy_
->AddSessionOnly(kSessionOnlyOrigin
);
154 // Store data for a session-only origin, setup to save session data, then
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();
162 base::MessageLoop::current()->RunUntilIdle();
164 VerifySingleOriginRemains(kSessionOnlyOrigin
);
167 TEST_F(DOMStorageContextImplTest
, PersistentIds
) {
168 const int kFirstSessionStorageNamespaceId
= 1;
169 const std::string kPersistentId
= "persistent";
170 context_
->CreateSessionNamespace(kFirstSessionStorageNamespaceId
,
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_
);
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_
);
196 TEST_F(DOMStorageContextImplTest
, DeleteSessionStorage
) {
197 // Create a DOMStorageContextImpl which will save sessionStorage on disk.
198 context_
= new DOMStorageContextImpl(temp_dir_
.path(),
200 storage_policy_
.get(),
202 context_
->SetSaveSessionStorageOnDisk();
203 ASSERT_EQ(temp_dir_
.path(), context_
->sessionstorage_directory());
206 const int kSessionStorageNamespaceId
= 1;
207 const std::string kPersistentId
= "persistent";
208 context_
->CreateSessionNamespace(kSessionStorageNamespaceId
,
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
);
219 // Destroy and recreate the DOMStorageContextImpl.
220 context_
->Shutdown();
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();
228 // Read the data back.
229 context_
->CreateSessionNamespace(kSessionStorageNamespaceId
,
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
);
238 SessionStorageUsageInfo info
;
239 info
.origin
= kOrigin
;
240 info
.persistent_namespace_id
= kPersistentId
;
241 context_
->DeleteSessionStorage(info
);
243 // Destroy and recreate again.
244 context_
->Shutdown();
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();
252 // Now there should be no data.
253 context_
->CreateSessionNamespace(kSessionStorageNamespaceId
,
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();
262 base::MessageLoop::current()->RunUntilIdle();
265 TEST_F(DOMStorageContextImplTest
, SessionStorageAlias
) {
266 const int kFirstSessionStorageNamespaceId
= 1;
267 const std::string kPersistentId
= "persistent";
268 context_
->CreateSessionNamespace(kFirstSessionStorageNamespaceId
,
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
);
281 const int kAliasSessionStorageNamespaceId
= 2;
282 context_
->CreateAliasSessionNamespace(kFirstSessionStorageNamespaceId
,
283 kAliasSessionStorageNamespaceId
,
285 DOMStorageNamespace
* dom_namespace2
=
286 context_
->GetStorageNamespace(kAliasSessionStorageNamespaceId
);
287 ASSERT_TRUE(dom_namespace2
);
288 ASSERT_TRUE(dom_namespace2
->alias_master_namespace() == dom_namespace1
);
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
);
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
);
303 // When creating an alias of an alias, ensure that the master relationship
305 const int kAlias2SessionStorageNamespaceId
= 3;
306 context_
->CreateAliasSessionNamespace(kAliasSessionStorageNamespaceId
,
307 kAlias2SessionStorageNamespaceId
,
309 DOMStorageNamespace
* dom_namespace3
=
310 context_
->GetStorageNamespace(kAlias2SessionStorageNamespaceId
);
311 ASSERT_TRUE(dom_namespace3
);
312 ASSERT_TRUE(dom_namespace3
->alias_master_namespace() == dom_namespace1
);
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
);
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
);
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
;
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
);
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
);
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
);
376 } // namespace content