1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Mozilla Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 2006
20 * the Initial Developer. All Rights Reserved.
23 * Neil Deakin <enndeakin@sympatico.ca>
24 * Johnny Stenback <jst@mozilla.com>
25 * Ehsan Akhgari <ehsan.akhgari@gmail.com>
26 * Honza Bambas <honzab@firemni.cz>
28 * Alternatively, the contents of this file may be used under the terms of
29 * either of the GNU General Public License Version 2 or later (the "GPL"),
30 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
44 #include "nsDOMError.h"
45 #include "nsDOMClassInfo.h"
46 #include "nsUnicharUtils.h"
47 #include "nsIDocument.h"
48 #include "nsDOMStorage.h"
50 #include "nsContentUtils.h"
51 #include "nsIScriptSecurityManager.h"
52 #include "nsIPrincipal.h"
54 #include "nsReadableUtils.h"
55 #include "nsIObserverService.h"
56 #include "nsNetUtil.h"
57 #include "nsIPrefBranch.h"
58 #include "nsICookiePermission.h"
59 #include "nsIPermission.h"
60 #include "nsIPermissionManager.h"
61 #include "nsCycleCollectionParticipant.h"
62 #include "nsIOfflineCacheUpdate.h"
63 #include "nsIJSContextStack.h"
64 #include "nsIPrivateBrowsingService.h"
65 #include "nsDOMString.h"
67 #include "nsIProxyObjectManager.h"
69 static const PRUint32 ASK_BEFORE_ACCEPT
= 1;
70 static const PRUint32 ACCEPT_SESSION
= 2;
71 static const PRUint32 BEHAVIOR_REJECT
= 2;
73 static const PRUint32 DEFAULT_QUOTA
= 5 * 1024;
74 // Be generous with offline apps by default...
75 static const PRUint32 DEFAULT_OFFLINE_APP_QUOTA
= 200 * 1024;
76 // ... but warn if it goes over this amount
77 static const PRUint32 DEFAULT_OFFLINE_WARN_QUOTA
= 50 * 1024;
79 static const char kPermissionType
[] = "cookie";
80 static const char kStorageEnabled
[] = "dom.storage.enabled";
81 static const char kDefaultQuota
[] = "dom.storage.default_quota";
82 static const char kCookiesBehavior
[] = "network.cookie.cookieBehavior";
83 static const char kCookiesLifetimePolicy
[] = "network.cookie.lifetimePolicy";
84 static const char kOfflineAppWarnQuota
[] = "offline-apps.quota.warn";
85 static const char kOfflineAppQuota
[] = "offline-apps.quota.max";
87 // The URI returned is the innermost URI that should be used for
88 // security-check-like stuff. aHost is its hostname, correctly canonicalized.
90 GetPrincipalURIAndHost(nsIPrincipal
* aPrincipal
, nsIURI
** aURI
, nsCString
& aHost
)
92 nsresult rv
= aPrincipal
->GetDomain(aURI
);
93 NS_ENSURE_SUCCESS(rv
, rv
);
96 rv
= aPrincipal
->GetURI(aURI
);
97 NS_ENSURE_SUCCESS(rv
, rv
);
104 nsCOMPtr
<nsIURI
> innerURI
= NS_GetInnermostURI(*aURI
);
106 return NS_ERROR_UNEXPECTED
;
109 rv
= innerURI
->GetAsciiHost(aHost
);
111 return NS_ERROR_DOM_SECURITY_ERR
;
114 innerURI
.swap(*aURI
);
120 // Helper that tells us whether the caller is secure or not.
126 nsCOMPtr
<nsIPrincipal
> subjectPrincipal
;
127 nsContentUtils::GetSecurityManager()->
128 GetSubjectPrincipal(getter_AddRefs(subjectPrincipal
));
130 if (!subjectPrincipal
) {
131 // No subject principal means no code is running. Default to not
132 // being secure in that case.
137 nsCOMPtr
<nsIURI
> codebase
;
138 subjectPrincipal
->GetURI(getter_AddRefs(codebase
));
144 nsCOMPtr
<nsIURI
> innerUri
= NS_GetInnermostURI(codebase
);
150 PRBool isHttps
= PR_FALSE
;
151 nsresult rv
= innerUri
->SchemeIs("https", &isHttps
);
153 return NS_SUCCEEDED(rv
) && isHttps
;
157 GetOfflinePermission(const nsACString
&aDomain
)
159 // Fake a URI for the permission manager
160 nsCOMPtr
<nsIURI
> uri
;
161 NS_NewURI(getter_AddRefs(uri
), NS_LITERAL_CSTRING("http://") + aDomain
);
165 nsCOMPtr
<nsIPermissionManager
> permissionManager
=
166 do_GetService(NS_PERMISSIONMANAGER_CONTRACTID
);
168 if (permissionManager
&&
169 NS_SUCCEEDED(permissionManager
->TestPermission(uri
, "offline-app", &perm
)))
173 return nsIPermissionManager::UNKNOWN_ACTION
;
177 IsOfflineAllowed(const nsACString
&aDomain
)
179 PRInt32 perm
= GetOfflinePermission(aDomain
);
180 return IS_PERMISSION_ALLOWED(perm
);
183 // Returns two quotas - A hard limit for which adding data will be an error,
184 // and a limit after which a warning event will be sent to the observer
185 // service. The warn limit may be -1, in which case there will be no warning.
186 // If aOverrideQuota is set, the larger offline apps quota is used and no
189 GetQuota(const nsACString
&aDomain
, PRInt32
*aQuota
, PRInt32
*aWarnQuota
,
192 PRUint32 perm
= GetOfflinePermission(aDomain
);
193 if (IS_PERMISSION_ALLOWED(perm
) || aOverrideQuota
) {
194 // This is an offline app, give more space by default.
195 *aQuota
= ((PRInt32
)nsContentUtils::GetIntPref(kOfflineAppQuota
,
196 DEFAULT_OFFLINE_APP_QUOTA
) * 1024);
198 if (perm
== nsIOfflineCacheUpdateService::ALLOW_NO_WARN
||
202 *aWarnQuota
= ((PRInt32
)nsContentUtils::GetIntPref(kOfflineAppWarnQuota
,
203 DEFAULT_OFFLINE_WARN_QUOTA
) * 1024);
208 // FIXME: per-domain quotas?
209 *aQuota
= ((PRInt32
)nsContentUtils::GetIntPref(kDefaultQuota
,
210 DEFAULT_QUOTA
) * 1024);
216 nsSessionStorageEntry::nsSessionStorageEntry(KeyTypePointer aStr
)
217 : nsStringHashKey(aStr
), mItem(nsnull
)
221 nsSessionStorageEntry::nsSessionStorageEntry(const nsSessionStorageEntry
& aToCopy
)
222 : nsStringHashKey(aToCopy
), mItem(nsnull
)
224 NS_ERROR("We're horked.");
227 nsSessionStorageEntry::~nsSessionStorageEntry()
232 // nsDOMStorageManager
235 nsDOMStorageManager
* nsDOMStorageManager::gStorageManager
;
237 nsDOMStorageManager::nsDOMStorageManager()
238 : mInPrivateBrowsing(PR_FALSE
)
242 NS_IMPL_ISUPPORTS2(nsDOMStorageManager
,
243 nsIDOMStorageManager
,
248 nsDOMStorageManager::Initialize()
250 gStorageManager
= new nsDOMStorageManager();
251 if (!gStorageManager
)
252 return NS_ERROR_OUT_OF_MEMORY
;
254 if (!gStorageManager
->mStorages
.Init()) {
255 delete gStorageManager
;
256 gStorageManager
= nsnull
;
257 return NS_ERROR_OUT_OF_MEMORY
;
260 NS_ADDREF(gStorageManager
);
262 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
266 os
->AddObserver(gStorageManager
, "cookie-changed", PR_FALSE
);
267 os
->AddObserver(gStorageManager
, "offline-app-removed", PR_FALSE
);
268 os
->AddObserver(gStorageManager
, NS_PRIVATE_BROWSING_SWITCH_TOPIC
, PR_FALSE
);
269 os
->AddObserver(gStorageManager
, "profile-after-change", PR_FALSE
);
270 os
->AddObserver(gStorageManager
, "perm-changed", PR_FALSE
);
277 nsDOMStorageManager::GetInstance()
279 NS_ASSERTION(gStorageManager
,
280 "nsDOMStorageManager::GetInstance() called before Initialize()");
281 NS_IF_ADDREF(gStorageManager
);
282 return gStorageManager
;
287 nsDOMStorageManager::Shutdown()
289 NS_IF_RELEASE(gStorageManager
);
290 gStorageManager
= nsnull
;
293 delete nsDOMStorage::gStorageDB
;
294 nsDOMStorage::gStorageDB
= nsnull
;
298 static PLDHashOperator
299 ClearStorage(nsDOMStorageEntry
* aEntry
, void* userArg
)
301 aEntry
->mStorage
->ClearAll();
302 return PL_DHASH_REMOVE
;
306 GetOfflineDomains(nsTArray
<nsString
>& aDomains
)
308 nsCOMPtr
<nsIPermissionManager
> permissionManager
=
309 do_GetService(NS_PERMISSIONMANAGER_CONTRACTID
);
310 if (permissionManager
) {
311 nsCOMPtr
<nsISimpleEnumerator
> enumerator
;
312 nsresult rv
= permissionManager
->GetEnumerator(getter_AddRefs(enumerator
));
313 NS_ENSURE_SUCCESS(rv
, rv
);
316 while (NS_SUCCEEDED(enumerator
->HasMoreElements(&hasMore
)) && hasMore
) {
317 nsCOMPtr
<nsISupports
> supp
;
318 rv
= enumerator
->GetNext(getter_AddRefs(supp
));
319 NS_ENSURE_SUCCESS(rv
, rv
);
321 nsCOMPtr
<nsIPermission
> perm(do_QueryInterface(supp
, &rv
));
322 NS_ENSURE_SUCCESS(rv
, rv
);
325 rv
= perm
->GetCapability(&capability
);
326 NS_ENSURE_SUCCESS(rv
, rv
);
327 if (capability
!= nsIPermissionManager::DENY_ACTION
) {
329 rv
= perm
->GetType(type
);
330 NS_ENSURE_SUCCESS(rv
, rv
);
332 if (type
.EqualsLiteral("offline-app")) {
334 rv
= perm
->GetHost(host
);
335 NS_ENSURE_SUCCESS(rv
, rv
);
337 aDomains
.AppendElement(NS_ConvertUTF8toUTF16(host
));
347 nsDOMStorageManager::Observe(nsISupports
*aSubject
,
349 const PRUnichar
*aData
)
351 if (!strcmp(aTopic
, "profile-after-change")) {
352 nsCOMPtr
<nsIPrivateBrowsingService
> pbs
=
353 do_GetService(NS_PRIVATE_BROWSING_SERVICE_CONTRACTID
);
355 pbs
->GetPrivateBrowsingEnabled(&gStorageManager
->mInPrivateBrowsing
);
357 else if (!strcmp(aTopic
, "offline-app-removed")) {
359 nsresult rv
= nsDOMStorage::InitDB();
360 NS_ENSURE_SUCCESS(rv
, rv
);
361 return nsDOMStorage::gStorageDB
->RemoveOwner(NS_ConvertUTF16toUTF8(aData
),
364 } else if (!strcmp(aTopic
, "cookie-changed") &&
365 !nsCRT::strcmp(aData
, NS_LITERAL_STRING("cleared").get())) {
366 mStorages
.EnumerateEntries(ClearStorage
, nsnull
);
369 nsresult rv
= nsDOMStorage::InitDB();
370 NS_ENSURE_SUCCESS(rv
, rv
);
372 // Remove global storage for domains that aren't marked for offline use.
373 nsTArray
<nsString
> domains
;
374 rv
= GetOfflineDomains(domains
);
375 NS_ENSURE_SUCCESS(rv
, rv
);
376 return nsDOMStorage::gStorageDB
->RemoveOwners(domains
, PR_TRUE
, PR_FALSE
);
378 } else if (!strcmp(aTopic
, NS_PRIVATE_BROWSING_SWITCH_TOPIC
)) {
379 mStorages
.EnumerateEntries(ClearStorage
, nsnull
);
380 if (!nsCRT::strcmp(aData
, NS_LITERAL_STRING(NS_PRIVATE_BROWSING_ENTER
).get()))
381 mInPrivateBrowsing
= PR_TRUE
;
382 else if (!nsCRT::strcmp(aData
, NS_LITERAL_STRING(NS_PRIVATE_BROWSING_LEAVE
).get()))
383 mInPrivateBrowsing
= PR_FALSE
;
385 nsresult rv
= nsDOMStorage::InitDB();
386 NS_ENSURE_SUCCESS(rv
, rv
);
388 return nsDOMStorage::gStorageDB
->DropPrivateBrowsingStorages();
390 } else if (!strcmp(aTopic
, "perm-changed")) {
391 // Check for cookie permission change
392 nsCOMPtr
<nsIPermission
> perm(do_QueryInterface(aSubject
));
396 if (type
!= NS_LITERAL_CSTRING("cookie"))
400 perm
->GetCapability(&cap
);
401 if (!(cap
& nsICookiePermission::ACCESS_SESSION
) ||
402 nsDependentString(aData
) != NS_LITERAL_STRING("deleted"))
411 nsresult rv
= nsDOMStorage::InitDB();
412 NS_ENSURE_SUCCESS(rv
, rv
);
414 return nsDOMStorage::gStorageDB
->DropSessionOnlyStoragesForHost(host
);
423 nsDOMStorageManager::GetUsage(const nsAString
& aDomain
,
426 nsresult rv
= nsDOMStorage::InitDB();
427 NS_ENSURE_SUCCESS(rv
, rv
);
429 return nsDOMStorage::gStorageDB
->GetUsage(NS_ConvertUTF16toUTF8(aDomain
),
434 nsDOMStorageManager::ClearOfflineApps()
436 nsresult rv
= nsDOMStorage::InitDB();
437 NS_ENSURE_SUCCESS(rv
, rv
);
439 nsTArray
<nsString
> domains
;
440 rv
= GetOfflineDomains(domains
);
441 NS_ENSURE_SUCCESS(rv
, rv
);
442 return nsDOMStorage::gStorageDB
->RemoveOwners(domains
, PR_TRUE
, PR_TRUE
);
446 nsDOMStorageManager::GetLocalStorageForPrincipal(nsIPrincipal
*aPrincipal
,
447 const nsSubstring
&aDocumentURI
,
448 nsIDOMStorage
**aResult
)
450 NS_ENSURE_ARG_POINTER(aPrincipal
);
455 nsRefPtr
<nsDOMStorage2
> storage
= new nsDOMStorage2();
457 return NS_ERROR_OUT_OF_MEMORY
;
459 rv
= storage
->InitAsLocalStorage(aPrincipal
, aDocumentURI
);
463 *aResult
= storage
.get();
470 nsDOMStorageManager::AddToStoragesHash(nsDOMStorage
* aStorage
)
472 nsDOMStorageEntry
* entry
= mStorages
.PutEntry(aStorage
);
474 entry
->mStorage
= aStorage
;
478 nsDOMStorageManager::RemoveFromStoragesHash(nsDOMStorage
* aStorage
)
480 nsDOMStorageEntry
* entry
= mStorages
.GetEntry(aStorage
);
482 mStorages
.RemoveEntry(aStorage
);
490 nsDOMStorageDBWrapper
* nsDOMStorage::gStorageDB
= nsnull
;
493 nsDOMStorageEntry::nsDOMStorageEntry(KeyTypePointer aStr
)
494 : nsVoidPtrHashKey(aStr
), mStorage(nsnull
)
498 nsDOMStorageEntry::nsDOMStorageEntry(const nsDOMStorageEntry
& aToCopy
)
499 : nsVoidPtrHashKey(aToCopy
), mStorage(nsnull
)
501 NS_ERROR("DOMStorage horked.");
504 nsDOMStorageEntry::~nsDOMStorageEntry()
509 SessionStorageTraverser(nsSessionStorageEntry
* aEntry
, void* userArg
) {
510 nsCycleCollectionTraversalCallback
*cb
=
511 static_cast<nsCycleCollectionTraversalCallback
*>(userArg
);
513 cb
->NoteXPCOMChild((nsIDOMStorageItem
*) aEntry
->mItem
);
515 return PL_DHASH_NEXT
;
518 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMStorage
)
519 NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsDOMStorage
)
520 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMStorage
)
522 if (tmp
->mItems
.IsInitialized()) {
523 tmp
->mItems
.EnumerateEntries(SessionStorageTraverser
, &cb
);
526 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
528 DOMCI_DATA(StorageObsolete
, nsDOMStorage
)
530 NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsDOMStorage
, nsIDOMStorageObsolete
)
531 NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsDOMStorage
, nsIDOMStorageObsolete
)
532 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMStorage
)
533 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIDOMStorageObsolete
)
534 NS_INTERFACE_MAP_ENTRY(nsIDOMStorageObsolete
)
535 NS_INTERFACE_MAP_ENTRY(nsPIDOMStorage
)
536 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StorageObsolete
)
540 NS_NewDOMStorage(nsISupports
* aOuter
, REFNSIID aIID
, void** aResult
)
542 nsDOMStorage
* storage
= new nsDOMStorage();
544 return NS_ERROR_OUT_OF_MEMORY
;
546 return storage
->QueryInterface(aIID
, aResult
);
550 NS_NewDOMStorage2(nsISupports
* aOuter
, REFNSIID aIID
, void** aResult
)
552 nsDOMStorage2
* storage
= new nsDOMStorage2();
554 return NS_ERROR_OUT_OF_MEMORY
;
556 return storage
->QueryInterface(aIID
, aResult
);
559 nsDOMStorage::nsDOMStorage()
561 , mSessionOnly(PR_TRUE
)
562 , mStorageType(nsPIDOMStorage::Unknown
)
563 , mItemsCached(PR_FALSE
)
564 , mEventBroadcaster(nsnull
)
565 , mCanUseChromePersist(false)
567 mSecurityChecker
= this;
569 if (nsDOMStorageManager::gStorageManager
)
570 nsDOMStorageManager::gStorageManager
->AddToStoragesHash(this);
573 nsDOMStorage::nsDOMStorage(nsDOMStorage
& aThat
)
574 : mUseDB(PR_FALSE
) // Any clone is not using the database
575 , mDomain(aThat
.mDomain
)
576 , mSessionOnly(PR_TRUE
)
577 , mStorageType(aThat
.mStorageType
)
578 , mItemsCached(PR_FALSE
)
580 , mScopeDBKey(aThat
.mScopeDBKey
)
582 , mEventBroadcaster(nsnull
)
583 , mCanUseChromePersist(aThat
.mCanUseChromePersist
)
585 mSecurityChecker
= this;
588 if (nsDOMStorageManager::gStorageManager
)
589 nsDOMStorageManager::gStorageManager
->AddToStoragesHash(this);
592 nsDOMStorage::~nsDOMStorage()
594 if (nsDOMStorageManager::gStorageManager
)
595 nsDOMStorageManager::gStorageManager
->RemoveFromStoragesHash(this);
600 GetDomainURI(nsIPrincipal
*aPrincipal
, PRBool aIncludeDomain
, nsIURI
**_domain
)
602 nsCOMPtr
<nsIURI
> uri
;
604 if (aIncludeDomain
) {
605 nsresult rv
= aPrincipal
->GetDomain(getter_AddRefs(uri
));
606 NS_ENSURE_SUCCESS(rv
, rv
);
610 nsresult rv
= aPrincipal
->GetURI(getter_AddRefs(uri
));
611 NS_ENSURE_SUCCESS(rv
, rv
);
614 // Check if we really got any URI. System principal doesn't return a URI
615 // instance and we would crash in NS_GetInnermostURI below.
617 return NS_ERROR_NOT_AVAILABLE
;
619 nsCOMPtr
<nsIURI
> innerURI
= NS_GetInnermostURI(uri
);
621 return NS_ERROR_UNEXPECTED
;
622 innerURI
.forget(_domain
);
628 nsDOMStorage::InitAsSessionStorage(nsIPrincipal
*aPrincipal
, const nsSubstring
&aDocumentURI
)
630 nsCOMPtr
<nsIURI
> domainURI
;
631 nsresult rv
= GetDomainURI(aPrincipal
, PR_TRUE
, getter_AddRefs(domainURI
));
632 NS_ENSURE_SUCCESS(rv
, rv
);
634 // No need to check for a return value. If this would fail we would not get
635 // here as we call GetPrincipalURIAndHost (nsDOMStorage.cpp:88) from
636 // nsDOMStorage::CanUseStorage before we query the storage manager for a new
637 // sessionStorage. It calls GetAsciiHost on innermost URI. If it fails, we
638 // won't get to InitAsSessionStorage.
639 domainURI
->GetAsciiHost(mDomain
);
641 mDocumentURI
= aDocumentURI
;
645 mScopeDBKey
.Truncate();
646 mQuotaDomainDBKey
.Truncate();
649 mStorageType
= SessionStorage
;
654 nsDOMStorage::InitAsLocalStorage(nsIPrincipal
*aPrincipal
, const nsSubstring
&aDocumentURI
)
656 nsCOMPtr
<nsIURI
> domainURI
;
657 nsresult rv
= GetDomainURI(aPrincipal
, PR_FALSE
, getter_AddRefs(domainURI
));
658 NS_ENSURE_SUCCESS(rv
, rv
);
660 // No need to check for a return value. If this would fail we would not get
661 // here as we call GetPrincipalURIAndHost (nsDOMStorage.cpp:88) from
662 // nsDOMStorage::CanUseStorage before we query the storage manager for a new
663 // localStorage. It calls GetAsciiHost on innermost URI. If it fails, we won't
664 // get to InitAsLocalStorage. Actually, mDomain will get replaced with
665 // mPrincipal in bug 455070. It is not even used for localStorage.
666 domainURI
->GetAsciiHost(mDomain
);
668 mDocumentURI
= aDocumentURI
;
671 nsDOMStorageDBWrapper::CreateOriginScopeDBKey(domainURI
, mScopeDBKey
);
673 // XXX Bug 357323, we have to solve the issue how to define
674 // origin for file URLs. In that case CreateOriginScopeDBKey
675 // fails (the result is empty) and we must avoid database use
676 // in that case because it produces broken entries w/o owner.
677 mUseDB
= !mScopeDBKey
.IsEmpty();
679 nsDOMStorageDBWrapper::CreateQuotaDomainDBKey(mDomain
,
680 PR_TRUE
, PR_FALSE
, mQuotaDomainDBKey
);
681 nsDOMStorageDBWrapper::CreateQuotaDomainDBKey(mDomain
,
682 PR_TRUE
, PR_TRUE
, mQuotaETLDplus1DomainDBKey
);
685 mStorageType
= LocalStorage
;
687 nsCOMPtr
<nsIURI
> URI
;
688 if (NS_SUCCEEDED(aPrincipal
->GetURI(getter_AddRefs(URI
))) && URI
) {
689 mCanUseChromePersist
= URICanUseChromePersist(URI
);
696 nsDOMStorage::InitAsGlobalStorage(const nsACString
&aDomainDemanded
)
698 mDomain
= aDomainDemanded
;
700 nsDOMStorageDBWrapper::CreateDomainScopeDBKey(aDomainDemanded
, mScopeDBKey
);
702 // XXX Bug 357323, we have to solve the issue how to define
703 // origin for file URLs. In that case CreateOriginScopeDBKey
704 // fails (the result is empty) and we must avoid database use
705 // in that case because it produces broken entries w/o owner.
706 if (!(mUseDB
= !mScopeDBKey
.IsEmpty()))
707 mScopeDBKey
.AppendLiteral(":");
709 nsDOMStorageDBWrapper::CreateQuotaDomainDBKey(aDomainDemanded
,
710 PR_TRUE
, PR_FALSE
, mQuotaDomainDBKey
);
711 nsDOMStorageDBWrapper::CreateQuotaDomainDBKey(aDomainDemanded
,
712 PR_TRUE
, PR_TRUE
, mQuotaETLDplus1DomainDBKey
);
715 mStorageType
= GlobalStorage
;
716 mEventBroadcaster
= this;
720 static PLDHashOperator
721 CopyStorageItems(nsSessionStorageEntry
* aEntry
, void* userArg
)
723 nsDOMStorage
* newstorage
= static_cast<nsDOMStorage
*>(userArg
);
725 newstorage
->SetItem(aEntry
->GetKey(), aEntry
->mItem
->GetValueInternal());
727 if (aEntry
->mItem
->IsSecure()) {
728 newstorage
->SetSecure(aEntry
->GetKey(), PR_TRUE
);
731 return PL_DHASH_NEXT
;
735 nsDOMStorage::CloneFrom(nsDOMStorage
* aThat
)
737 aThat
->mItems
.EnumerateEntries(CopyStorageItems
, this);
743 nsDOMStorage::CanUseStorage(PRPackedBool
* aSessionOnly
)
745 // check if the calling domain can use storage. Downgrade to session
746 // only if only session storage may be used.
747 NS_ASSERTION(aSessionOnly
, "null session flag");
748 *aSessionOnly
= PR_FALSE
;
750 if (!nsContentUtils::GetBoolPref(kStorageEnabled
))
753 // chrome can always use storage regardless of permission preferences
754 if (nsContentUtils::IsCallerChrome())
757 nsCOMPtr
<nsIPrincipal
> subjectPrincipal
;
758 nsContentUtils::GetSecurityManager()->
759 GetSubjectPrincipal(getter_AddRefs(subjectPrincipal
));
761 // if subjectPrincipal were null we'd have returned after
764 nsCOMPtr
<nsIURI
> subjectURI
;
765 nsCAutoString unused
;
766 if (NS_FAILED(GetPrincipalURIAndHost(subjectPrincipal
,
767 getter_AddRefs(subjectURI
),
772 nsCOMPtr
<nsIPermissionManager
> permissionManager
=
773 do_GetService(NS_PERMISSIONMANAGER_CONTRACTID
);
774 if (!permissionManager
)
778 permissionManager
->TestPermission(subjectURI
, kPermissionType
, &perm
);
780 if (perm
== nsIPermissionManager::DENY_ACTION
)
783 // In private browsing mode we ougth to behave as in session-only cookies
784 // mode to prevent detection of being in private browsing mode and ensuring
785 // that there will be no traces left.
786 if (perm
== nsICookiePermission::ACCESS_SESSION
||
787 nsDOMStorageManager::gStorageManager
->InPrivateBrowsingMode()) {
788 *aSessionOnly
= PR_TRUE
;
790 else if (perm
!= nsIPermissionManager::ALLOW_ACTION
) {
791 PRUint32 cookieBehavior
= nsContentUtils::GetIntPref(kCookiesBehavior
);
792 PRUint32 lifetimePolicy
= nsContentUtils::GetIntPref(kCookiesLifetimePolicy
);
794 // Treat "ask every time" as "reject always".
795 // Chrome persistent pages can bypass this check.
796 if ((cookieBehavior
== BEHAVIOR_REJECT
|| lifetimePolicy
== ASK_BEFORE_ACCEPT
) &&
797 !URICanUseChromePersist(subjectURI
))
800 if (lifetimePolicy
== ACCEPT_SESSION
)
801 *aSessionOnly
= PR_TRUE
;
808 nsDOMStorage::CacheStoragePermissions()
810 // Bug 488446, disallowing storage use when in session only mode.
811 // This is temporary fix before we find complete solution for storage
812 // behavior in private browsing mode or session-only cookies mode.
813 if (!CanUseStorage(&mSessionOnly
))
816 nsIScriptSecurityManager
* ssm
= nsContentUtils::GetSecurityManager();
820 nsCOMPtr
<nsIPrincipal
> subjectPrincipal
;
821 ssm
->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal
));
823 NS_ASSERTION(mSecurityChecker
, "Has non-null mSecurityChecker");
824 return mSecurityChecker
->CanAccess(subjectPrincipal
);
829 nsDOMStorage::URICanUseChromePersist(nsIURI
* aURI
) {
832 (NS_SUCCEEDED(aURI
->SchemeIs("moz-safe-about", &isAbout
)) && isAbout
) ||
833 (NS_SUCCEEDED(aURI
->SchemeIs("about", &isAbout
)) && isAbout
);
837 nsDOMStorage::CanUseChromePersist()
839 return mCanUseChromePersist
;
842 class ItemCounterState
845 ItemCounterState(PRBool aIsCallerSecure
)
846 : mIsCallerSecure(aIsCallerSecure
), mCount(0)
850 PRBool mIsCallerSecure
;
853 ItemCounterState(); // Not to be implemented
856 static PLDHashOperator
857 ItemCounter(nsSessionStorageEntry
* aEntry
, void* userArg
)
859 ItemCounterState
*state
= (ItemCounterState
*)userArg
;
861 if (state
->mIsCallerSecure
|| !aEntry
->mItem
->IsSecure()) {
865 return PL_DHASH_NEXT
;
869 nsDOMStorage::GetLength(PRUint32
*aLength
)
871 if (!CacheStoragePermissions())
872 return NS_ERROR_DOM_SECURITY_ERR
;
874 // Force reload of items from database. This ensures sync localStorages for
875 // same origins among different windows.
876 mItemsCached
= PR_FALSE
;
880 ItemCounterState
state(IsCallerSecure());
882 mItems
.EnumerateEntries(ItemCounter
, &state
);
884 *aLength
= state
.mCount
;
889 class IndexFinderData
892 IndexFinderData(PRBool aIsCallerSecure
, PRUint32 aWantedIndex
)
893 : mIsCallerSecure(aIsCallerSecure
), mIndex(0), mWantedIndex(aWantedIndex
),
898 PRBool mIsCallerSecure
;
900 PRUint32 mWantedIndex
;
901 nsSessionStorageEntry
*mItem
;
904 IndexFinderData(); // Not to be implemented
907 static PLDHashOperator
908 IndexFinder(nsSessionStorageEntry
* aEntry
, void* userArg
)
910 IndexFinderData
*data
= (IndexFinderData
*)userArg
;
912 if (data
->mIndex
== data
->mWantedIndex
&&
913 (data
->mIsCallerSecure
|| !aEntry
->mItem
->IsSecure())) {
914 data
->mItem
= aEntry
;
916 return PL_DHASH_STOP
;
921 return PL_DHASH_NEXT
;
925 nsDOMStorage::Key(PRUint32 aIndex
, nsAString
& aKey
)
927 // XXXjst: This is as retarded as the DOM spec is, takes an unsigned
928 // int, but the spec talks about what to do if a negative value is
931 // XXX: This does a linear search for the key at index, which would
932 // suck if there's a large numer of indexes. Do we care? If so,
933 // maybe we need to have a lazily populated key array here or
936 if (!CacheStoragePermissions())
937 return NS_ERROR_DOM_SECURITY_ERR
;
942 IndexFinderData
data(IsCallerSecure(), aIndex
);
943 mItems
.EnumerateEntries(IndexFinder
, &data
);
946 // aIndex was larger than the number of accessible keys. Throw.
947 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
950 aKey
= data
.mItem
->GetKey();
956 nsDOMStorage::GetNamedItem(const nsAString
& aKey
, nsresult
* aResult
)
958 if (!CacheStoragePermissions()) {
959 *aResult
= NS_ERROR_DOM_SECURITY_ERR
;
967 nsSessionStorageEntry
*entry
= mItems
.GetEntry(aKey
);
968 nsIDOMStorageItem
* item
= nsnull
;
970 if (IsCallerSecure() || !entry
->mItem
->IsSecure()) {
977 nsresult rv
= GetDBValue(aKey
, value
, &secure
);
978 // return null if access isn't allowed or the key wasn't found
979 if (rv
== NS_ERROR_DOM_SECURITY_ERR
|| rv
== NS_ERROR_DOM_NOT_FOUND_ERR
)
983 NS_ENSURE_SUCCESS(rv
, nsnull
);
985 nsRefPtr
<nsDOMStorageItem
> newitem
=
986 new nsDOMStorageItem(this, aKey
, value
, secure
);
987 if (newitem
&& (entry
= mItems
.PutEntry(aKey
))) {
988 item
= entry
->mItem
= newitem
;
991 *aResult
= NS_ERROR_OUT_OF_MEMORY
;
999 nsDOMStorage::GetItem(const nsAString
& aKey
, nsAString
&aData
)
1004 // CacheStoragePermissions() is called inside of
1005 // GetItem(nsAString, nsIDOMStorageItem)
1006 // To call it particularly in this method would just duplicate
1007 // the call. If the code changes, make sure that call to
1008 // CacheStoragePermissions() is put here!
1010 nsCOMPtr
<nsIDOMStorageItem
> item
;
1011 rv
= GetItem(aKey
, getter_AddRefs(item
));
1016 rv
= item
->GetValue(aData
);
1017 NS_ENSURE_SUCCESS(rv
, rv
);
1020 SetDOMStringToNull(aData
);
1026 nsDOMStorage::GetItem(const nsAString
& aKey
, nsIDOMStorageItem
**aItem
)
1030 NS_IF_ADDREF(*aItem
= GetNamedItem(aKey
, &rv
));
1036 nsDOMStorage::SetItem(const nsAString
& aKey
, const nsAString
& aData
)
1038 if (!CacheStoragePermissions())
1039 return NS_ERROR_DOM_SECURITY_ERR
;
1045 SetDOMStringToNull(oldValue
);
1048 nsRefPtr
<nsDOMStorageItem
> newitem
= nsnull
;
1049 nsSessionStorageEntry
*entry
= mItems
.GetEntry(aKey
);
1051 if (entry
->mItem
->IsSecure() && !IsCallerSecure()) {
1052 return NS_ERROR_DOM_SECURITY_ERR
;
1054 oldValue
= entry
->mItem
->GetValueInternal();
1055 entry
->mItem
->SetValueInternal(aData
);
1058 newitem
= new nsDOMStorageItem(this, aKey
, aData
, IsCallerSecure());
1060 return NS_ERROR_OUT_OF_MEMORY
;
1064 rv
= SetDBValue(aKey
, aData
, IsCallerSecure());
1065 NS_ENSURE_SUCCESS(rv
, rv
);
1069 entry
= mItems
.PutEntry(aKey
);
1070 NS_ENSURE_TRUE(entry
, NS_ERROR_OUT_OF_MEMORY
);
1071 entry
->mItem
= newitem
;
1074 if ((oldValue
!= aData
|| mStorageType
== GlobalStorage
) && mEventBroadcaster
)
1075 mEventBroadcaster
->BroadcastChangeNotification(aKey
, oldValue
, aData
);
1080 NS_IMETHODIMP
nsDOMStorage::RemoveItem(const nsAString
& aKey
)
1082 if (!CacheStoragePermissions())
1083 return NS_ERROR_DOM_SECURITY_ERR
;
1089 nsSessionStorageEntry
*entry
= mItems
.GetEntry(aKey
);
1091 if (entry
&& entry
->mItem
->IsSecure() && !IsCallerSecure()) {
1092 return NS_ERROR_DOM_SECURITY_ERR
;
1097 nsresult rv
= InitDB();
1098 NS_ENSURE_SUCCESS(rv
, rv
);
1102 rv
= GetDBValue(aKey
, value
, &secureItem
);
1103 if (rv
== NS_ERROR_DOM_NOT_FOUND_ERR
)
1105 NS_ENSURE_SUCCESS(rv
, rv
);
1109 rv
= gStorageDB
->RemoveKey(this, aKey
, !IsOfflineAllowed(mDomain
),
1110 aKey
.Length() + value
.Length());
1111 NS_ENSURE_SUCCESS(rv
, rv
);
1113 mItemsCached
= PR_FALSE
;
1117 // clear string as StorageItems may be referencing this item
1118 oldValue
= entry
->mItem
->GetValueInternal();
1119 entry
->mItem
->ClearValue();
1123 mItems
.RawRemoveEntry(entry
);
1126 if ((!oldValue
.IsEmpty() && mStorageType
!= GlobalStorage
) && mEventBroadcaster
) {
1127 nsAutoString nullString
;
1128 SetDOMStringToNull(nullString
);
1129 mEventBroadcaster
->BroadcastChangeNotification(aKey
, oldValue
, nullString
);
1135 PR_STATIC_CALLBACK(PLDHashOperator
)
1136 CheckSecure(nsSessionStorageEntry
* aEntry
, void* userArg
)
1138 PRBool
* secure
= (PRBool
*)userArg
;
1139 if (aEntry
->mItem
->IsSecure()) {
1141 return PL_DHASH_STOP
;
1144 return PL_DHASH_NEXT
;
1148 nsDOMStorage::Clear()
1150 if (!CacheStoragePermissions())
1151 return NS_ERROR_DOM_SECURITY_ERR
;
1156 PRInt32 oldCount
= mItems
.Count();
1158 PRBool foundSecureItem
= PR_FALSE
;
1159 mItems
.EnumerateEntries(CheckSecure
, &foundSecureItem
);
1161 if (foundSecureItem
&& !IsCallerSecure()) {
1162 return NS_ERROR_DOM_SECURITY_ERR
;
1167 nsresult rv
= InitDB();
1168 NS_ENSURE_SUCCESS(rv
, rv
);
1170 rv
= gStorageDB
->ClearStorage(this);
1171 NS_ENSURE_SUCCESS(rv
, rv
);
1177 if (oldCount
&& mEventBroadcaster
) {
1178 nsAutoString nullString
;
1179 SetDOMStringToNull(nullString
);
1180 mEventBroadcaster
->BroadcastChangeNotification(nullString
, nullString
, nullString
);
1187 nsDOMStorage::InitDB()
1191 gStorageDB
= new nsDOMStorageDBWrapper();
1193 return NS_ERROR_OUT_OF_MEMORY
;
1195 nsresult rv
= gStorageDB
->Init();
1196 if (NS_FAILED(rv
)) {
1197 // Failed to initialize the DB, delete it and null out the
1198 // pointer so we don't end up attempting to use an
1199 // un-initialized DB later on.
1202 gStorageDB
= nsnull
;
1213 nsDOMStorage::CacheKeysFromDB()
1216 // cache all the keys in the hash. This is used by the Length and Key methods
1217 // use this cache for better performance. The disadvantage is that the
1218 // order may break if someone changes the keys in the database directly.
1219 if (!mItemsCached
) {
1220 nsresult rv
= InitDB();
1221 NS_ENSURE_SUCCESS(rv
, rv
);
1225 rv
= gStorageDB
->GetAllKeys(this, &mItems
);
1226 NS_ENSURE_SUCCESS(rv
, rv
);
1228 mItemsCached
= PR_TRUE
;
1236 nsDOMStorage::GetDBValue(const nsAString
& aKey
, nsAString
& aValue
,
1245 nsresult rv
= InitDB();
1246 NS_ENSURE_SUCCESS(rv
, rv
);
1249 rv
= gStorageDB
->GetKeyValue(this, aKey
, value
, aSecure
);
1251 if (rv
== NS_ERROR_DOM_NOT_FOUND_ERR
&& mStorageType
!= GlobalStorage
) {
1252 SetDOMStringToNull(aValue
);
1258 if (!IsCallerSecure() && *aSecure
) {
1259 return NS_ERROR_DOM_SECURITY_ERR
;
1262 aValue
.Assign(value
);
1269 nsDOMStorage::SetDBValue(const nsAString
& aKey
,
1270 const nsAString
& aValue
,
1277 nsresult rv
= InitDB();
1278 NS_ENSURE_SUCCESS(rv
, rv
);
1280 PRInt32 offlineAppPermission
;
1283 offlineAppPermission
= GetQuota(mDomain
, "a
, &warnQuota
,
1284 CanUseChromePersist());
1287 rv
= gStorageDB
->SetKey(this, aKey
, aValue
, aSecure
, quota
,
1288 !IS_PERMISSION_ALLOWED(offlineAppPermission
),
1290 NS_ENSURE_SUCCESS(rv
, rv
);
1292 mItemsCached
= PR_FALSE
;
1294 if (warnQuota
>= 0 && usage
> warnQuota
) {
1295 // try to include the window that exceeded the warn quota
1296 nsCOMPtr
<nsIDOMWindow
> window
;
1298 nsCOMPtr
<nsIJSContextStack
> stack
=
1299 do_GetService("@mozilla.org/js/xpc/ContextStack;1");
1300 if (stack
&& NS_SUCCEEDED(stack
->Peek(&cx
)) && cx
) {
1301 nsCOMPtr
<nsIScriptContext
> scriptContext
;
1302 scriptContext
= GetScriptContextFromJSContext(cx
);
1303 if (scriptContext
) {
1304 window
= do_QueryInterface(scriptContext
->GetGlobalObject());
1308 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
1309 os
->NotifyObservers(window
, "dom-storage-warn-quota-exceeded",
1310 NS_ConvertUTF8toUTF16(mDomain
).get());
1319 nsDOMStorage::SetSecure(const nsAString
& aKey
, PRBool aSecure
)
1323 nsresult rv
= InitDB();
1324 NS_ENSURE_SUCCESS(rv
, rv
);
1326 return gStorageDB
->SetSecure(this, aKey
, aSecure
);
1329 return NS_ERROR_NOT_IMPLEMENTED
;
1332 nsSessionStorageEntry
*entry
= mItems
.GetEntry(aKey
);
1333 NS_ASSERTION(entry
, "Don't use SetSecure() with nonexistent keys!");
1336 entry
->mItem
->SetSecureInternal(aSecure
);
1342 static PLDHashOperator
1343 ClearStorageItem(nsSessionStorageEntry
* aEntry
, void* userArg
)
1345 aEntry
->mItem
->SetValueInternal(EmptyString());
1346 return PL_DHASH_NEXT
;
1350 nsDOMStorage::ClearAll()
1352 mItems
.EnumerateEntries(ClearStorageItem
, nsnull
);
1353 mItemsCached
= PR_FALSE
;
1356 already_AddRefed
<nsIDOMStorage
>
1357 nsDOMStorage::Clone()
1359 NS_ASSERTION(PR_FALSE
, "Old DOMStorage doesn't implement cloning");
1363 already_AddRefed
<nsIDOMStorage
>
1364 nsDOMStorage::Fork(const nsSubstring
&aDocumentURI
)
1366 NS_ASSERTION(PR_FALSE
, "Old DOMStorage doesn't implement forking");
1370 PRBool
nsDOMStorage::IsForkOf(nsIDOMStorage
* aThat
)
1372 NS_ASSERTION(PR_FALSE
, "Old DOMStorage doesn't implement forking");
1376 struct KeysArrayBuilderStruct
1378 PRBool callerIsSecure
;
1379 nsTArray
<nsString
> *keys
;
1382 static PLDHashOperator
1383 KeysArrayBuilder(nsSessionStorageEntry
* aEntry
, void* userArg
)
1385 KeysArrayBuilderStruct
*keystruct
= (KeysArrayBuilderStruct
*)userArg
;
1387 if (keystruct
->callerIsSecure
|| !aEntry
->mItem
->IsSecure())
1388 keystruct
->keys
->AppendElement(aEntry
->GetKey());
1390 return PL_DHASH_NEXT
;
1393 nsTArray
<nsString
> *
1394 nsDOMStorage::GetKeys()
1399 KeysArrayBuilderStruct keystruct
;
1400 keystruct
.callerIsSecure
= IsCallerSecure();
1401 keystruct
.keys
= new nsTArray
<nsString
>();
1403 mItems
.EnumerateEntries(KeysArrayBuilder
, &keystruct
);
1405 return keystruct
.keys
;
1409 nsDOMStorage::Principal()
1415 nsDOMStorage::CanAccessSystem(nsIPrincipal
*aPrincipal
)
1420 nsIScriptSecurityManager
* ssm
= nsContentUtils::GetSecurityManager();
1425 nsresult rv
= ssm
->IsSystemPrincipal(aPrincipal
, &isSystem
);
1427 return NS_SUCCEEDED(rv
) && isSystem
;
1431 nsDOMStorage::CanAccess(nsIPrincipal
*aPrincipal
)
1433 // Allow C++/system callers to access the storage
1434 if (CanAccessSystem(aPrincipal
))
1437 nsCAutoString domain
;
1438 nsCOMPtr
<nsIURI
> unused
;
1439 nsresult rv
= GetPrincipalURIAndHost(aPrincipal
,
1440 getter_AddRefs(unused
), domain
);
1441 NS_ENSURE_SUCCESS(rv
, PR_FALSE
);
1443 return domain
.Equals(mDomain
);
1446 nsPIDOMStorage::nsDOMStorageType
1447 nsDOMStorage::StorageType()
1449 return mStorageType
;
1453 nsDOMStorage::BroadcastChangeNotification(const nsSubstring
&aKey
,
1454 const nsSubstring
&aOldValue
,
1455 const nsSubstring
&aNewValue
)
1457 nsCOMPtr
<nsIObserverService
> observerService
=
1458 mozilla::services::GetObserverService();
1459 if (!observerService
) {
1463 // Fire off a notification that a storage object changed. If the
1464 // storage object is a session storage object, we don't pass a
1465 // domain, but if it's a global storage object we do.
1466 observerService
->NotifyObservers((nsIDOMStorageObsolete
*)this,
1467 "dom-storage-changed",
1468 NS_ConvertUTF8toUTF16(mDomain
).get());
1475 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMStorage2
)
1476 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMStorage2
)
1477 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mStorage
)
1478 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1479 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMStorage2
)
1480 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mStorage
, nsIDOMStorageObsolete
)
1481 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1483 DOMCI_DATA(Storage
, nsDOMStorage2
)
1485 NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsDOMStorage2
, nsIDOMStorage
)
1486 NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsDOMStorage2
, nsIDOMStorage
)
1487 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMStorage2
)
1488 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIDOMStorage
)
1489 NS_INTERFACE_MAP_ENTRY(nsIDOMStorage
)
1490 NS_INTERFACE_MAP_ENTRY(nsPIDOMStorage
)
1491 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Storage
)
1492 NS_INTERFACE_MAP_END
1494 nsDOMStorage2::nsDOMStorage2()
1498 nsDOMStorage2::nsDOMStorage2(nsDOMStorage2
& aThat
)
1500 mStorage
= new nsDOMStorage(*aThat
.mStorage
.get());
1501 mStorage
->mSecurityChecker
= mStorage
;
1502 mPrincipal
= aThat
.mPrincipal
;
1506 nsDOMStorage2::InitAsSessionStorage(nsIPrincipal
*aPrincipal
, const nsSubstring
&aDocumentURI
)
1508 mStorage
= new nsDOMStorage();
1510 return NS_ERROR_OUT_OF_MEMORY
;
1512 // Leave security checks only for domain (nsDOMStorage implementation)
1513 mStorage
->mSecurityChecker
= mStorage
;
1514 mPrincipal
= aPrincipal
;
1515 mDocumentURI
= aDocumentURI
;
1517 return mStorage
->InitAsSessionStorage(aPrincipal
, aDocumentURI
);
1521 nsDOMStorage2::InitAsLocalStorage(nsIPrincipal
*aPrincipal
, const nsSubstring
&aDocumentURI
)
1523 mStorage
= new nsDOMStorage();
1525 return NS_ERROR_OUT_OF_MEMORY
;
1527 mStorage
->mSecurityChecker
= this;
1528 mPrincipal
= aPrincipal
;
1529 mDocumentURI
= aDocumentURI
;
1531 return mStorage
->InitAsLocalStorage(aPrincipal
, aDocumentURI
);
1535 nsDOMStorage2::InitAsGlobalStorage(const nsACString
&aDomainDemanded
)
1537 NS_ASSERTION(PR_FALSE
, "Should not initialize nsDOMStorage2 as global storage.");
1538 return NS_ERROR_NOT_IMPLEMENTED
;
1541 already_AddRefed
<nsIDOMStorage
>
1542 nsDOMStorage2::Clone()
1544 nsDOMStorage2
* storage
= new nsDOMStorage2(*this);
1548 storage
->mStorage
->CloneFrom(mStorage
);
1554 already_AddRefed
<nsIDOMStorage
>
1555 nsDOMStorage2::Fork(const nsSubstring
&aDocumentURI
)
1557 nsRefPtr
<nsDOMStorage2
> storage
= new nsDOMStorage2();
1561 nsresult rv
= storage
->InitAsSessionStorageFork(mPrincipal
, aDocumentURI
, mStorage
);
1565 nsIDOMStorage
* result
= static_cast<nsIDOMStorage
*>(storage
.get());
1570 PRBool
nsDOMStorage2::IsForkOf(nsIDOMStorage
* aThat
)
1575 nsDOMStorage2
* storage
= static_cast<nsDOMStorage2
*>(aThat
);
1576 return mStorage
== storage
->mStorage
;
1580 nsDOMStorage2::InitAsSessionStorageFork(nsIPrincipal
*aPrincipal
, const nsSubstring
&aDocumentURI
, nsIDOMStorageObsolete
* aStorage
)
1582 mPrincipal
= aPrincipal
;
1583 mDocumentURI
= aDocumentURI
;
1584 mStorage
= static_cast<nsDOMStorage
*>(aStorage
);
1589 nsTArray
<nsString
> *
1590 nsDOMStorage2::GetKeys()
1592 return mStorage
->GetKeys();
1596 nsDOMStorage2::Principal()
1602 nsDOMStorage2::CanAccess(nsIPrincipal
*aPrincipal
)
1604 if (mStorage
->mSecurityChecker
!= this)
1605 return mStorage
->mSecurityChecker
->CanAccess(aPrincipal
);
1607 // Allow C++ callers to access the storage
1611 // Allow more powerful principals (e.g. system) to access the storage
1613 nsresult rv
= aPrincipal
->Subsumes(mPrincipal
, &subsumes
);
1620 nsPIDOMStorage::nsDOMStorageType
1621 nsDOMStorage2::StorageType()
1624 return mStorage
->StorageType();
1626 return nsPIDOMStorage::Unknown
;
1630 nsDOMStorage2::BroadcastChangeNotification(const nsSubstring
&aKey
,
1631 const nsSubstring
&aOldValue
,
1632 const nsSubstring
&aNewValue
)
1635 nsCOMPtr
<nsIDOMStorageEvent
> event
= new nsDOMStorageEvent();
1636 rv
= event
->InitStorageEvent(NS_LITERAL_STRING("storage"),
1643 static_cast<nsIDOMStorage
*>(this));
1644 if (NS_FAILED(rv
)) {
1648 nsCOMPtr
<nsIObserverService
> observerService
=
1649 mozilla::services::GetObserverService();
1650 if (!observerService
) {
1654 nsCOMPtr
<nsIObserverService
> observerServiceProxy
;
1655 rv
= NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD
,
1656 NS_GET_IID(nsIObserverService
),
1658 NS_PROXY_ASYNC
| NS_PROXY_ALWAYS
,
1659 getter_AddRefs(observerServiceProxy
));
1660 if (NS_FAILED(rv
)) {
1664 // Fire off a notification that a storage object changed.
1665 observerServiceProxy
->NotifyObservers(event
,
1666 "dom-storage2-changed",
1671 nsDOMStorage2::GetLength(PRUint32
*aLength
)
1673 return mStorage
->GetLength(aLength
);
1677 nsDOMStorage2::Key(PRUint32 aIndex
, nsAString
& aKey
)
1679 return mStorage
->Key(aIndex
, aKey
);
1683 nsDOMStorage2::GetItem(const nsAString
& aKey
, nsAString
&aData
)
1685 return mStorage
->GetItem(aKey
, aData
);
1689 nsDOMStorage2::SetItem(const nsAString
& aKey
, const nsAString
& aData
)
1691 mStorage
->mEventBroadcaster
= this;
1692 return mStorage
->SetItem(aKey
, aData
);
1696 nsDOMStorage2::RemoveItem(const nsAString
& aKey
)
1698 mStorage
->mEventBroadcaster
= this;
1699 return mStorage
->RemoveItem(aKey
);
1703 nsDOMStorage2::Clear()
1705 mStorage
->mEventBroadcaster
= this;
1706 return mStorage
->Clear();
1713 DOMCI_DATA(StorageList
, nsDOMStorageList
)
1715 NS_INTERFACE_MAP_BEGIN(nsDOMStorageList
)
1716 NS_INTERFACE_MAP_ENTRY(nsISupports
)
1717 NS_INTERFACE_MAP_ENTRY(nsIDOMStorageList
)
1718 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StorageList
)
1719 NS_INTERFACE_MAP_END
1721 NS_IMPL_ADDREF(nsDOMStorageList
)
1722 NS_IMPL_RELEASE(nsDOMStorageList
)
1724 nsIDOMStorageObsolete
*
1725 nsDOMStorageList::GetNamedItem(const nsAString
& aDomain
, nsresult
* aResult
)
1727 nsCAutoString requestedDomain
;
1729 // Normalize the requested domain
1730 nsCOMPtr
<nsIIDNService
> idn
= do_GetService(NS_IDNSERVICE_CONTRACTID
);
1732 *aResult
= idn
->ConvertUTF8toACE(NS_ConvertUTF16toUTF8(aDomain
),
1734 NS_ENSURE_SUCCESS(*aResult
, nsnull
);
1736 // Don't have the IDN service, best we can do is URL escape.
1737 NS_EscapeURL(NS_ConvertUTF16toUTF8(aDomain
),
1738 esc_OnlyNonASCII
| esc_AlwaysCopy
,
1741 ToLowerCase(requestedDomain
);
1743 nsIScriptSecurityManager
* ssm
= nsContentUtils::GetSecurityManager();
1745 *aResult
= NS_ERROR_FAILURE
;
1749 nsCOMPtr
<nsIPrincipal
> subjectPrincipal
;
1750 *aResult
= ssm
->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal
));
1751 NS_ENSURE_SUCCESS(*aResult
, nsnull
);
1753 nsCAutoString currentDomain
;
1754 if (subjectPrincipal
) {
1755 nsCOMPtr
<nsIURI
> unused
;
1756 *aResult
= GetPrincipalURIAndHost(subjectPrincipal
, getter_AddRefs(unused
),
1758 NS_ENSURE_SUCCESS(*aResult
, nsnull
);
1760 PRPackedBool sessionOnly
;
1761 if (!nsDOMStorage::CanUseStorage(&sessionOnly
)) {
1762 *aResult
= NS_ERROR_DOM_SECURITY_ERR
;
1767 PRBool isSystem
= nsContentUtils::IsCallerTrustedForRead();
1768 if (currentDomain
.IsEmpty() && !isSystem
) {
1769 *aResult
= NS_ERROR_DOM_SECURITY_ERR
;
1773 return GetStorageForDomain(requestedDomain
,
1774 currentDomain
, isSystem
, aResult
);
1778 nsDOMStorageList::NamedItem(const nsAString
& aDomain
,
1779 nsIDOMStorageObsolete
** aStorage
)
1782 NS_IF_ADDREF(*aStorage
= GetNamedItem(aDomain
, &rv
));
1788 nsDOMStorageList::CanAccessDomain(const nsACString
& aRequestedDomain
,
1789 const nsACString
& aCurrentDomain
)
1791 return aRequestedDomain
.Equals(aCurrentDomain
);
1794 nsIDOMStorageObsolete
*
1795 nsDOMStorageList::GetStorageForDomain(const nsACString
& aRequestedDomain
,
1796 const nsACString
& aCurrentDomain
,
1797 PRBool aNoCurrentDomainCheck
,
1800 nsTArray
<nsCString
> requestedDomainArray
;
1801 if ((!aNoCurrentDomainCheck
&&
1802 !CanAccessDomain(aRequestedDomain
, aCurrentDomain
)) ||
1803 !ConvertDomainToArray(aRequestedDomain
, &requestedDomainArray
)) {
1804 *aResult
= NS_ERROR_DOM_SECURITY_ERR
;
1809 // now rebuild a string for the domain.
1810 nsCAutoString usedDomain
;
1811 PRUint32 requestedPos
= 0;
1812 for (requestedPos
= 0; requestedPos
< requestedDomainArray
.Length();
1814 if (!usedDomain
.IsEmpty())
1815 usedDomain
.Append('.');
1816 usedDomain
.Append(requestedDomainArray
[requestedPos
]);
1821 // now have a valid domain, so look it up in the storage table
1822 nsIDOMStorageObsolete
* storage
= mStorages
.GetWeak(usedDomain
);
1824 nsRefPtr
<nsDOMStorage
> newstorage
;
1825 newstorage
= new nsDOMStorage();
1826 if (newstorage
&& mStorages
.Put(usedDomain
, newstorage
)) {
1827 *aResult
= newstorage
->InitAsGlobalStorage(usedDomain
);
1828 if (NS_FAILED(*aResult
)) {
1829 mStorages
.Remove(usedDomain
);
1832 storage
= newstorage
;
1835 *aResult
= NS_ERROR_OUT_OF_MEMORY
;
1844 nsDOMStorageList::ConvertDomainToArray(const nsACString
& aDomain
,
1845 nsTArray
<nsCString
> *aArray
)
1847 PRInt32 length
= aDomain
.Length();
1849 while (n
< length
) {
1850 PRInt32 dotpos
= aDomain
.FindChar('.', n
);
1851 nsCAutoString domain
;
1853 if (dotpos
== -1) // no more dots
1854 domain
.Assign(Substring(aDomain
, n
));
1855 else if (dotpos
- n
== 0) // no point continuing in this case
1857 else if (dotpos
>= 0)
1858 domain
.Assign(Substring(aDomain
, n
, dotpos
- n
));
1860 ToLowerCase(domain
);
1861 aArray
->AppendElement(domain
);
1869 // if n equals the length, there is a dot at the end, so treat it as invalid
1870 return (n
!= length
);
1874 NS_NewDOMStorageList(nsIDOMStorageList
** aResult
)
1876 *aResult
= new nsDOMStorageList();
1878 return NS_ERROR_OUT_OF_MEMORY
;
1880 NS_ADDREF(*aResult
);
1888 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMStorageItem
)
1889 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMStorageItem
)
1891 tmp
->mStorage
= nsnull
;
1893 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1894 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMStorageItem
)
1896 cb
.NoteXPCOMChild((nsIDOMStorageObsolete
*) tmp
->mStorage
);
1898 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1900 NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsDOMStorageItem
, nsIDOMStorageItem
)
1901 NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsDOMStorageItem
, nsIDOMStorageItem
)
1903 DOMCI_DATA(StorageItem
, nsDOMStorageItem
)
1905 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMStorageItem
)
1906 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIDOMStorageItem
)
1907 NS_INTERFACE_MAP_ENTRY(nsIDOMStorageItem
)
1908 NS_INTERFACE_MAP_ENTRY(nsIDOMToString
)
1909 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StorageItem
)
1910 NS_INTERFACE_MAP_END
1912 nsDOMStorageItem::nsDOMStorageItem(nsDOMStorage
* aStorage
,
1913 const nsAString
& aKey
,
1914 const nsAString
& aValue
,
1923 nsDOMStorageItem::~nsDOMStorageItem()
1928 nsDOMStorageItem::GetSecure(PRBool
* aSecure
)
1930 if (!mStorage
->CacheStoragePermissions() || !IsCallerSecure()) {
1931 return NS_ERROR_DOM_INVALID_ACCESS_ERR
;
1934 if (mStorage
->UseDB()) {
1936 return mStorage
->GetDBValue(mKey
, value
, aSecure
);
1939 *aSecure
= IsSecure();
1944 nsDOMStorageItem::SetSecure(PRBool aSecure
)
1946 if (!mStorage
->CacheStoragePermissions() || !IsCallerSecure()) {
1947 return NS_ERROR_DOM_INVALID_ACCESS_ERR
;
1950 if (mStorage
->UseDB()) {
1951 nsresult rv
= mStorage
->SetSecure(mKey
, aSecure
);
1952 NS_ENSURE_SUCCESS(rv
, rv
);
1960 nsDOMStorageItem::GetValue(nsAString
& aValue
)
1962 if (!mStorage
->CacheStoragePermissions())
1963 return NS_ERROR_DOM_INVALID_ACCESS_ERR
;
1965 if (mStorage
->UseDB()) {
1966 // GetDBValue checks the secure state so no need to do it here
1968 nsresult rv
= mStorage
->GetDBValue(mKey
, aValue
, &secure
);
1969 if (rv
== NS_ERROR_DOM_NOT_FOUND_ERR
)
1974 if (IsSecure() && !IsCallerSecure()) {
1975 return NS_ERROR_DOM_SECURITY_ERR
;
1983 nsDOMStorageItem::SetValue(const nsAString
& aValue
)
1985 if (!mStorage
->CacheStoragePermissions())
1986 return NS_ERROR_DOM_INVALID_ACCESS_ERR
;
1988 PRBool secureCaller
= IsCallerSecure();
1990 if (mStorage
->UseDB()) {
1991 // SetDBValue() does the security checks for us.
1992 return mStorage
->SetDBValue(mKey
, aValue
, secureCaller
);
1995 PRBool secureItem
= IsSecure();
1997 if (!secureCaller
&& secureItem
) {
1998 // The item is secure, but the caller isn't. Throw.
1999 return NS_ERROR_DOM_SECURITY_ERR
;
2003 mSecure
= secureCaller
;
2008 nsDOMStorageItem::ToString(nsAString
& aStr
)
2010 return GetValue(aStr
);
2013 // Cycle collection implementation for nsDOMStorageEvent
2014 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMStorageEvent
)
2016 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMStorageEvent
, nsDOMEvent
)
2017 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mStorageArea
)
2018 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2020 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMStorageEvent
, nsDOMEvent
)
2021 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mStorageArea
)
2022 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2024 NS_IMPL_ADDREF_INHERITED(nsDOMStorageEvent
, nsDOMEvent
)
2025 NS_IMPL_RELEASE_INHERITED(nsDOMStorageEvent
, nsDOMEvent
)
2027 DOMCI_DATA(StorageEvent
, nsDOMStorageEvent
)
2029 // QueryInterface implementation for nsDOMStorageEvent
2030 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMStorageEvent
)
2031 NS_INTERFACE_MAP_ENTRY(nsIDOMStorageEvent
)
2032 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StorageEvent
)
2033 NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent
)
2036 /* readonly attribute DOMString key; */
2037 NS_IMETHODIMP
nsDOMStorageEvent::GetKey(nsAString
& aKey
)
2043 /* readonly attribute DOMString oldValue; */
2044 NS_IMETHODIMP
nsDOMStorageEvent::GetOldValue(nsAString
& aOldValue
)
2046 aOldValue
= mOldValue
;
2050 /* readonly attribute DOMString newValue; */
2051 NS_IMETHODIMP
nsDOMStorageEvent::GetNewValue(nsAString
& aNewValue
)
2053 aNewValue
= mNewValue
;
2057 /* readonly attribute DOMString url; */
2058 NS_IMETHODIMP
nsDOMStorageEvent::GetUrl(nsAString
& aUrl
)
2064 /* readonly attribute nsIDOMStorage storageArea; */
2065 NS_IMETHODIMP
nsDOMStorageEvent::GetStorageArea(nsIDOMStorage
* *aStorageArea
)
2067 NS_ENSURE_ARG_POINTER(aStorageArea
);
2069 NS_ADDREF(*aStorageArea
= mStorageArea
);
2073 /* void initStorageEvent (in DOMString typeArg, in boolean canBubbleArg, in boolean cancelableArg, in DOMString keyArg, in DOMString oldValueArg, in DOMString newValueArg, in DOMString urlArg, in nsIDOMStorage storageAreaArg); */
2074 NS_IMETHODIMP
nsDOMStorageEvent::InitStorageEvent(const nsAString
& typeArg
,
2075 PRBool canBubbleArg
,
2076 PRBool cancelableArg
,
2077 const nsAString
& keyArg
,
2078 const nsAString
& oldValueArg
,
2079 const nsAString
& newValueArg
,
2080 const nsAString
& urlArg
,
2081 nsIDOMStorage
*storageAreaArg
)
2085 rv
= InitEvent(typeArg
, canBubbleArg
, cancelableArg
);
2086 NS_ENSURE_SUCCESS(rv
, rv
);
2089 mOldValue
= oldValueArg
;
2090 mNewValue
= newValueArg
;
2092 mStorageArea
= storageAreaArg
;
2097 // Obsolete globalStorage event
2099 DOMCI_DATA(StorageEventObsolete
, nsDOMStorageEventObsolete
)
2101 // QueryInterface implementation for nsDOMStorageEventObsolete
2102 NS_INTERFACE_MAP_BEGIN(nsDOMStorageEventObsolete
)
2103 NS_INTERFACE_MAP_ENTRY(nsIDOMStorageEventObsolete
)
2104 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StorageEventObsolete
)
2105 NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent
)
2107 NS_IMPL_ADDREF_INHERITED(nsDOMStorageEventObsolete
, nsDOMEvent
)
2108 NS_IMPL_RELEASE_INHERITED(nsDOMStorageEventObsolete
, nsDOMEvent
)
2112 nsDOMStorageEventObsolete::GetDomain(nsAString
& aDomain
)
2114 // mDomain will be #session for session storage for events that fire
2115 // due to a change in a session storage object.
2122 nsDOMStorageEventObsolete::InitStorageEvent(const nsAString
& aTypeArg
,
2123 PRBool aCanBubbleArg
,
2124 PRBool aCancelableArg
,
2125 const nsAString
& aDomainArg
)
2127 nsresult rv
= InitEvent(aTypeArg
, aCanBubbleArg
, aCancelableArg
);
2128 NS_ENSURE_SUCCESS(rv
, rv
);
2130 mDomain
= aDomainArg
;