Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "DOMStorageManager.h"
7 #include "DOMStorage.h"
8 #include "DOMStorageDBThread.h"
10 #include "nsIScriptSecurityManager.h"
11 #include "nsIEffectiveTLDService.h"
13 #include "nsNetUtil.h"
14 #include "nsPrintfCString.h"
15 #include "nsXULAppAPI.h"
16 #include "nsThreadUtils.h"
17 #include "nsIObserverService.h"
18 #include "mozIThirdPartyUtil.h"
19 #include "mozilla/Services.h"
20 #include "mozilla/Preferences.h"
22 // Only allow relatively small amounts of data since performance of
23 // the synchronous IO is very bad.
24 // We are enforcing simple per-origin quota only.
25 #define DEFAULT_QUOTA_LIMIT (5 * 1024)
27 namespace mozilla {
28 namespace dom {
30 namespace { // anon
32 int32_t gQuotaLimit = DEFAULT_QUOTA_LIMIT;
34 } // anon
36 DOMLocalStorageManager*
37 DOMLocalStorageManager::sSelf = nullptr;
39 // static
40 uint32_t
41 DOMStorageManager::GetQuota()
42 {
43 static bool preferencesInitialized = false;
44 if (!preferencesInitialized) {
45 mozilla::Preferences::AddIntVarCache(&gQuotaLimit, "dom.storage.default_quota",
46 DEFAULT_QUOTA_LIMIT);
47 preferencesInitialized = true;
48 }
50 return gQuotaLimit * 1024; // pref is in kBs
51 }
53 void
54 ReverseString(const nsCSubstring& aSource, nsCSubstring& aResult)
55 {
56 nsACString::const_iterator sourceBegin, sourceEnd;
57 aSource.BeginReading(sourceBegin);
58 aSource.EndReading(sourceEnd);
60 aResult.SetLength(aSource.Length());
61 nsACString::iterator destEnd;
62 aResult.EndWriting(destEnd);
64 while (sourceBegin != sourceEnd) {
65 *(--destEnd) = *sourceBegin;
66 ++sourceBegin;
67 }
68 }
70 nsresult
71 CreateReversedDomain(const nsACString& aAsciiDomain,
72 nsACString& aKey)
73 {
74 if (aAsciiDomain.IsEmpty()) {
75 return NS_ERROR_NOT_AVAILABLE;
76 }
78 ReverseString(aAsciiDomain, aKey);
80 aKey.AppendLiteral(".");
81 return NS_OK;
82 }
84 bool
85 PrincipalsEqual(nsIPrincipal* aObjectPrincipal, nsIPrincipal* aSubjectPrincipal)
86 {
87 if (!aSubjectPrincipal) {
88 return true;
89 }
91 if (!aObjectPrincipal) {
92 return false;
93 }
95 return aSubjectPrincipal->Equals(aObjectPrincipal);
96 }
98 NS_IMPL_ISUPPORTS(DOMStorageManager,
99 nsIDOMStorageManager)
101 DOMStorageManager::DOMStorageManager(nsPIDOMStorage::StorageType aType)
102 : mCaches(10)
103 , mType(aType)
104 , mLowDiskSpace(false)
105 {
106 DOMStorageObserver* observer = DOMStorageObserver::Self();
107 NS_ASSERTION(observer, "No DOMStorageObserver, cannot observe private data delete notifications!");
109 if (observer) {
110 observer->AddSink(this);
111 }
112 }
114 DOMStorageManager::~DOMStorageManager()
115 {
116 DOMStorageObserver* observer = DOMStorageObserver::Self();
117 if (observer) {
118 observer->RemoveSink(this);
119 }
120 }
122 namespace { // anon
124 nsresult
125 AppendFirstPartyToKey(nsIURI* aFirstPartyIsolationURI, nsACString& aKey)
126 {
127 if (aFirstPartyIsolationURI) {
128 nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
129 do_GetService(THIRDPARTYUTIL_CONTRACTID);
130 if (!thirdPartyUtil)
131 return NS_ERROR_FAILURE;
133 nsAutoCString firstPartyHost;
134 nsresult rv = thirdPartyUtil->GetFirstPartyHostForIsolation(aFirstPartyIsolationURI,
135 firstPartyHost);
136 NS_ENSURE_SUCCESS(rv, rv);
138 aKey.AppendLiteral("&");
139 aKey.Append(firstPartyHost);
140 }
142 return NS_OK;
143 }
145 nsresult
146 CreateScopeKey(nsIURI* aFirstPartyIsolationURI, nsIPrincipal* aPrincipal,
147 nsACString& aKey)
148 {
149 nsCOMPtr<nsIURI> uri;
150 nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
151 NS_ENSURE_SUCCESS(rv, rv);
152 if (!uri) {
153 return NS_ERROR_UNEXPECTED;
154 }
156 nsAutoCString domainScope;
157 rv = uri->GetAsciiHost(domainScope);
158 NS_ENSURE_SUCCESS(rv, rv);
160 if (domainScope.IsEmpty()) {
161 // For the file:/// protocol use the exact directory as domain.
162 bool isScheme = false;
163 if (NS_SUCCEEDED(uri->SchemeIs("file", &isScheme)) && isScheme) {
164 nsCOMPtr<nsIURL> url = do_QueryInterface(uri, &rv);
165 NS_ENSURE_SUCCESS(rv, rv);
166 rv = url->GetDirectory(domainScope);
167 NS_ENSURE_SUCCESS(rv, rv);
168 }
169 }
171 nsAutoCString key;
173 rv = CreateReversedDomain(domainScope, key);
174 if (NS_FAILED(rv)) {
175 return rv;
176 }
178 nsAutoCString scheme;
179 rv = uri->GetScheme(scheme);
180 NS_ENSURE_SUCCESS(rv, rv);
182 key.Append(NS_LITERAL_CSTRING(":") + scheme);
184 int32_t port = NS_GetRealPort(uri);
185 if (port != -1) {
186 key.Append(nsPrintfCString(":%d", port));
187 }
189 bool unknownAppId;
190 rv = aPrincipal->GetUnknownAppId(&unknownAppId);
191 NS_ENSURE_SUCCESS(rv, rv);
193 if (!unknownAppId) {
194 uint32_t appId;
195 rv = aPrincipal->GetAppId(&appId);
196 NS_ENSURE_SUCCESS(rv, rv);
198 bool isInBrowserElement;
199 rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
200 NS_ENSURE_SUCCESS(rv, rv);
202 if (appId == nsIScriptSecurityManager::NO_APP_ID && !isInBrowserElement) {
203 aKey.Assign(key);
204 } else {
205 aKey.Truncate();
206 aKey.AppendInt(appId);
207 aKey.Append(NS_LITERAL_CSTRING(":") + (isInBrowserElement ?
208 NS_LITERAL_CSTRING("t") : NS_LITERAL_CSTRING("f")) +
209 NS_LITERAL_CSTRING(":") + key);
210 }
211 }
213 // Isolate scope keys to the URL bar domain by appending &firstPartyHost
214 // if available.
215 return AppendFirstPartyToKey(aFirstPartyIsolationURI, aKey);
216 }
218 nsresult
219 CreateQuotaDBKey(nsIURI* aFirstPartyIsolationURI, nsIPrincipal* aPrincipal,
220 nsACString& aKey)
221 {
222 nsresult rv;
224 nsAutoCString subdomainsDBKey;
225 nsCOMPtr<nsIEffectiveTLDService> eTLDService(do_GetService(
226 NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv));
227 NS_ENSURE_SUCCESS(rv, rv);
229 nsCOMPtr<nsIURI> uri;
230 rv = aPrincipal->GetURI(getter_AddRefs(uri));
231 NS_ENSURE_SUCCESS(rv, rv);
232 NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
234 nsAutoCString eTLDplusOne;
235 rv = eTLDService->GetBaseDomain(uri, 0, eTLDplusOne);
236 if (NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS == rv) {
237 // XXX bug 357323 - what to do for localhost/file exactly?
238 rv = uri->GetAsciiHost(eTLDplusOne);
239 }
240 NS_ENSURE_SUCCESS(rv, rv);
242 CreateReversedDomain(eTLDplusOne, subdomainsDBKey);
244 bool unknownAppId;
245 rv = aPrincipal->GetUnknownAppId(&unknownAppId);
246 NS_ENSURE_SUCCESS(rv, rv);
248 if (!unknownAppId) {
249 uint32_t appId;
250 rv = aPrincipal->GetAppId(&appId);
251 NS_ENSURE_SUCCESS(rv, rv);
253 bool isInBrowserElement;
254 rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
255 NS_ENSURE_SUCCESS(rv, rv);
257 if (appId == nsIScriptSecurityManager::NO_APP_ID && !isInBrowserElement) {
258 aKey.Assign(subdomainsDBKey);
259 } else {
260 aKey.Truncate();
261 aKey.AppendInt(appId);
262 aKey.Append(NS_LITERAL_CSTRING(":") + (isInBrowserElement ?
263 NS_LITERAL_CSTRING("t") : NS_LITERAL_CSTRING("f")) +
264 NS_LITERAL_CSTRING(":") + subdomainsDBKey);
265 }
266 }
268 // Isolate scope keys to the URL bar domain by appending &firstPartyHost
269 // if available.
270 return AppendFirstPartyToKey(aFirstPartyIsolationURI, aKey);
271 }
273 } // anon
275 DOMStorageCache*
276 DOMStorageManager::GetCache(const nsACString& aScope) const
277 {
278 DOMStorageCacheHashKey* entry = mCaches.GetEntry(aScope);
279 if (!entry) {
280 return nullptr;
281 }
283 return entry->cache();
284 }
286 already_AddRefed<DOMStorageUsage>
287 DOMStorageManager::GetScopeUsage(const nsACString& aScope)
288 {
289 nsRefPtr<DOMStorageUsage> usage;
290 if (mUsages.Get(aScope, &usage)) {
291 return usage.forget();
292 }
294 usage = new DOMStorageUsage(aScope);
296 if (mType == LocalStorage) {
297 DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
298 if (db) {
299 db->AsyncGetUsage(usage);
300 }
301 }
303 mUsages.Put(aScope, usage);
305 return usage.forget();
306 }
308 already_AddRefed<DOMStorageCache>
309 DOMStorageManager::PutCache(const nsACString& aScope,
310 nsIURI* aFirstPartyIsolationURI,
311 nsIPrincipal* aPrincipal)
312 {
313 DOMStorageCacheHashKey* entry = mCaches.PutEntry(aScope);
314 nsRefPtr<DOMStorageCache> cache = entry->cache();
316 nsAutoCString quotaScope;
317 CreateQuotaDBKey(aFirstPartyIsolationURI, aPrincipal, quotaScope);
319 // To avoid ever persisting session storage to disk, initialize LocalStorage
320 // like SessionStorage.
321 switch (mType) {
322 case SessionStorage:
323 case LocalStorage:
324 // Lifetime handled by the manager, don't persist
325 entry->HardRef();
326 cache->Init(this, false, aFirstPartyIsolationURI, aPrincipal, quotaScope);
327 break;
329 default:
330 MOZ_ASSERT(false);
331 }
333 return cache.forget();
334 }
336 void
337 DOMStorageManager::DropCache(DOMStorageCache* aCache)
338 {
339 if (!NS_IsMainThread()) {
340 NS_WARNING("DOMStorageManager::DropCache called on a non-main thread, shutting down?");
341 }
343 mCaches.RemoveEntry(aCache->Scope());
344 }
346 nsresult
347 DOMStorageManager::GetStorageInternal(bool aCreate,
348 nsIURI* aFirstPartyIsolationURI,
349 nsIPrincipal* aPrincipal,
350 const nsAString& aDocumentURI,
351 bool aPrivate,
352 nsIDOMStorage** aRetval)
353 {
354 nsresult rv;
356 nsAutoCString scope;
357 rv = CreateScopeKey(aFirstPartyIsolationURI, aPrincipal, scope);
358 if (NS_FAILED(rv)) {
359 return NS_ERROR_NOT_AVAILABLE;
360 }
362 nsRefPtr<DOMStorageCache> cache = GetCache(scope);
364 // Get or create a cache for the given scope
365 if (!cache) {
366 if (!aCreate) {
367 *aRetval = nullptr;
368 return NS_OK;
369 }
371 if (!aRetval) {
372 // This is demand to just preload the cache, if the scope has
373 // no data stored, bypass creation and preload of the cache.
374 DOMStorageDBBridge* db = DOMStorageCache::GetDatabase();
375 if (db) {
376 if (!db->ShouldPreloadScope(scope)) {
377 return NS_OK;
378 }
379 } else {
380 if (scope.Equals(NS_LITERAL_CSTRING("knalb.:about"))) {
381 return NS_OK;
382 }
383 }
384 }
386 // There is always a single instance of a cache per scope
387 // in a single instance of a DOM storage manager.
388 cache = PutCache(scope, aFirstPartyIsolationURI, aPrincipal);
389 } else if (mType == SessionStorage) {
390 if (!cache->CheckPrincipal(aPrincipal)) {
391 return NS_ERROR_DOM_SECURITY_ERR;
392 }
393 }
395 if (aRetval) {
396 *aRetval = new DOMStorage(this, cache, aDocumentURI, aPrincipal, aPrivate);
397 NS_ADDREF(*aRetval);
398 }
400 return NS_OK;
401 }
403 NS_IMETHODIMP
404 DOMStorageManager::PrecacheStorage(nsIPrincipal* aPrincipal)
405 {
406 return GetStorageInternal(true, nullptr, aPrincipal, EmptyString(),
407 false, nullptr);
408 }
410 NS_IMETHODIMP
411 DOMStorageManager::PrecacheStorageForFirstParty(nsIURI* aFirstPartyIsolationURI,
412 nsIPrincipal* aPrincipal)
413 {
414 return GetStorageInternal(true, aFirstPartyIsolationURI, aPrincipal, EmptyString(),
415 false, nullptr);
416 }
418 NS_IMETHODIMP
419 DOMStorageManager::CreateStorage(nsIPrincipal* aPrincipal,
420 const nsAString& aDocumentURI,
421 bool aPrivate,
422 nsIDOMStorage** aRetval)
423 {
424 return GetStorageInternal(true, nullptr, aPrincipal, aDocumentURI,
425 aPrivate, aRetval);
426 }
428 NS_IMETHODIMP
429 DOMStorageManager::CreateStorageForFirstParty(nsIURI* aFirstPartyIsolationURI,
430 nsIPrincipal* aPrincipal,
431 const nsAString& aDocumentURI,
432 bool aPrivate,
433 nsIDOMStorage** aRetval)
434 {
435 return GetStorageInternal(true, aFirstPartyIsolationURI, aPrincipal, aDocumentURI,
436 aPrivate, aRetval);
437 }
439 NS_IMETHODIMP
440 DOMStorageManager::GetStorage(nsIPrincipal* aPrincipal,
441 bool aPrivate,
442 nsIDOMStorage** aRetval)
443 {
444 return GetStorageInternal(false, nullptr, aPrincipal, EmptyString(),
445 aPrivate, aRetval);
446 }
448 NS_IMETHODIMP
449 DOMStorageManager::GetStorageForFirstParty(nsIURI* aFirstPartyIsolationURI,
450 nsIPrincipal* aPrincipal,
451 bool aPrivate,
452 nsIDOMStorage** aRetval)
453 {
454 return GetStorageInternal(false, aFirstPartyIsolationURI, aPrincipal,
455 EmptyString(), aPrivate, aRetval);
456 }
458 NS_IMETHODIMP
459 DOMStorageManager::CloneStorage(nsIDOMStorage* aStorage)
460 {
461 if (mType != SessionStorage) {
462 // Cloning is supported only for sessionStorage
463 return NS_ERROR_NOT_IMPLEMENTED;
464 }
466 nsCOMPtr<nsPIDOMStorage> pstorage = do_QueryInterface(aStorage);
467 if (!pstorage) {
468 return NS_ERROR_UNEXPECTED;
469 }
471 const DOMStorageCache* origCache = pstorage->GetCache();
473 DOMStorageCache* existingCache = GetCache(origCache->Scope());
474 if (existingCache) {
475 // Do not replace an existing sessionStorage.
476 return NS_ERROR_NOT_AVAILABLE;
477 }
479 // Since this manager is sessionStorage manager, PutCache hard references
480 // the cache in our hashtable.
481 nsRefPtr<DOMStorageCache> newCache = PutCache(origCache->Scope(),
482 origCache->FirstPartyIsolationURI(),
483 origCache->Principal());
485 newCache->CloneFrom(origCache);
486 return NS_OK;
487 }
489 NS_IMETHODIMP
490 DOMStorageManager::CheckStorage(nsIPrincipal* aPrincipal,
491 nsIDOMStorage* aStorage,
492 bool* aRetval)
493 {
494 return CheckStorageForFirstParty(nullptr, aPrincipal, aStorage, aRetval);
495 }
497 NS_IMETHODIMP
498 DOMStorageManager::CheckStorageForFirstParty(nsIURI* aFirstPartyIsolationURI,
499 nsIPrincipal* aPrincipal,
500 nsIDOMStorage* aStorage,
501 bool* aRetval)
502 {
503 nsCOMPtr<nsPIDOMStorage> pstorage = do_QueryInterface(aStorage);
504 if (!pstorage) {
505 return NS_ERROR_UNEXPECTED;
506 }
508 *aRetval = false;
510 if (!aPrincipal) {
511 return NS_ERROR_NOT_AVAILABLE;
512 }
514 nsAutoCString scope;
515 nsresult rv = CreateScopeKey(aFirstPartyIsolationURI, aPrincipal, scope);
516 if (NS_FAILED(rv)) {
517 return rv;
518 }
520 DOMStorageCache* cache = GetCache(scope);
521 if (cache != pstorage->GetCache()) {
522 return NS_OK;
523 }
525 if (!pstorage->PrincipalEquals(aPrincipal)) {
526 return NS_OK;
527 }
529 *aRetval = true;
530 return NS_OK;
531 }
533 // Obsolete nsIDOMStorageManager methods
535 NS_IMETHODIMP
536 DOMStorageManager::GetLocalStorageForPrincipal(nsIPrincipal* aPrincipal,
537 const nsAString& aDocumentURI,
538 bool aPrivate,
539 nsIDOMStorage** aRetval)
540 {
541 if (mType != LocalStorage) {
542 return NS_ERROR_UNEXPECTED;
543 }
545 return CreateStorage(aPrincipal, aDocumentURI, aPrivate, aRetval);
546 }
548 namespace { // anon
550 class ClearCacheEnumeratorData
551 {
552 public:
553 ClearCacheEnumeratorData(uint32_t aFlags)
554 : mUnloadFlags(aFlags)
555 {}
557 uint32_t mUnloadFlags;
558 nsCString mKeyPrefix;
559 };
561 } // anon
563 PLDHashOperator
564 DOMStorageManager::ClearCacheEnumerator(DOMStorageCacheHashKey* aEntry, void* aClosure)
565 {
566 DOMStorageCache* cache = aEntry->cache();
567 nsCString& key = const_cast<nsCString&>(cache->Scope());
569 ClearCacheEnumeratorData* data = static_cast<ClearCacheEnumeratorData*>(aClosure);
571 if (data->mKeyPrefix.IsEmpty() || StringBeginsWith(key, data->mKeyPrefix)) {
572 cache->UnloadItems(data->mUnloadFlags);
573 }
575 return PL_DHASH_NEXT;
576 }
578 nsresult
579 DOMStorageManager::Observe(const char* aTopic, const nsACString& aScopePrefix)
580 {
581 // Clear everything, caches + database
582 if (!strcmp(aTopic, "cookie-cleared")) {
583 ClearCacheEnumeratorData data(DOMStorageCache::kUnloadComplete);
584 mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
586 return NS_OK;
587 }
589 // Clear from caches everything that has been stored
590 // while in session-only mode
591 if (!strcmp(aTopic, "session-only-cleared")) {
592 ClearCacheEnumeratorData data(DOMStorageCache::kUnloadSession);
593 data.mKeyPrefix = aScopePrefix;
594 mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
596 return NS_OK;
597 }
599 // Clear everything (including so and pb data) from caches and database
600 // for the gived domain and subdomains.
601 if (!strcmp(aTopic, "domain-data-cleared")) {
602 ClearCacheEnumeratorData data(DOMStorageCache::kUnloadComplete);
603 data.mKeyPrefix = aScopePrefix;
604 mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
606 return NS_OK;
607 }
609 // Clear all private-browsing caches
610 if (!strcmp(aTopic, "private-browsing-data-cleared")) {
611 ClearCacheEnumeratorData data(DOMStorageCache::kUnloadPrivate);
612 mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
614 return NS_OK;
615 }
617 // Clear localStorage data beloging to an app.
618 if (!strcmp(aTopic, "app-data-cleared")) {
620 // sessionStorage is expected to stay
621 if (mType == SessionStorage) {
622 return NS_OK;
623 }
625 ClearCacheEnumeratorData data(DOMStorageCache::kUnloadComplete);
626 data.mKeyPrefix = aScopePrefix;
627 mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
629 return NS_OK;
630 }
632 if (!strcmp(aTopic, "profile-change")) {
633 // For case caches are still referenced - clear them completely
634 ClearCacheEnumeratorData data(DOMStorageCache::kUnloadComplete);
635 mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
637 mCaches.Clear();
638 return NS_OK;
639 }
641 if (!strcmp(aTopic, "low-disk-space")) {
642 if (mType == LocalStorage) {
643 mLowDiskSpace = true;
644 }
646 return NS_OK;
647 }
649 if (!strcmp(aTopic, "no-low-disk-space")) {
650 if (mType == LocalStorage) {
651 mLowDiskSpace = false;
652 }
654 return NS_OK;
655 }
657 #ifdef DOM_STORAGE_TESTS
658 if (!strcmp(aTopic, "test-reload")) {
659 if (mType != LocalStorage) {
660 return NS_OK;
661 }
663 // This immediately completely reloads all caches from the database.
664 ClearCacheEnumeratorData data(DOMStorageCache::kTestReload);
665 mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
666 return NS_OK;
667 }
669 if (!strcmp(aTopic, "test-flushed")) {
670 if (XRE_GetProcessType() != GeckoProcessType_Default) {
671 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
672 if (obs) {
673 obs->NotifyObservers(nullptr, "domstorage-test-flushed", nullptr);
674 }
675 }
677 return NS_OK;
678 }
679 #endif
681 NS_ERROR("Unexpected topic");
682 return NS_ERROR_UNEXPECTED;
683 }
685 // DOMLocalStorageManager
687 DOMLocalStorageManager::DOMLocalStorageManager()
688 : DOMStorageManager(LocalStorage)
689 {
690 NS_ASSERTION(!sSelf, "Somebody is trying to do_CreateInstance(\"@mozilla/dom/localStorage-manager;1\"");
691 sSelf = this;
693 if (XRE_GetProcessType() != GeckoProcessType_Default) {
694 // Do this only on the child process. The thread IPC bridge
695 // is also used to communicate chrome observer notifications.
696 // Note: must be called after we set sSelf
697 DOMStorageCache::StartDatabase();
698 }
699 }
701 DOMLocalStorageManager::~DOMLocalStorageManager()
702 {
703 sSelf = nullptr;
704 }
706 // DOMSessionStorageManager
708 DOMSessionStorageManager::DOMSessionStorageManager()
709 : DOMStorageManager(SessionStorage)
710 {
711 if (XRE_GetProcessType() != GeckoProcessType_Default) {
712 // Do this only on the child process. The thread IPC bridge
713 // is also used to communicate chrome observer notifications.
714 DOMStorageCache::StartDatabase();
715 }
716 }
718 } // ::dom
719 } // ::mozilla