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 "DOMStorage.h"
7 #include "DOMStorageCache.h"
8 #include "DOMStorageManager.h"
10 #include "nsIObserverService.h"
11 #include "nsIScriptSecurityManager.h"
12 #include "nsIPermissionManager.h"
13 #include "nsIPrincipal.h"
14 #include "nsICookiePermission.h"
16 #include "mozilla/dom/StorageBinding.h"
17 #include "mozilla/dom/StorageEvent.h"
18 #include "mozilla/dom/StorageEventBinding.h"
19 #include "mozilla/Services.h"
20 #include "mozilla/Preferences.h"
21 #include "nsThreadUtils.h"
22 #include "nsContentUtils.h"
23 #include "nsServiceManagerUtils.h"
28 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMStorage
, mManager
, mPrincipal
, mWindow
)
30 NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMStorage
)
31 NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMStorage
)
33 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMStorage
)
34 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
35 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIDOMStorage
)
36 NS_INTERFACE_MAP_ENTRY(nsIDOMStorage
)
37 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
40 DOMStorage::DOMStorage(nsIDOMWindow
* aWindow
,
41 DOMStorageManager
* aManager
,
42 DOMStorageCache
* aCache
,
43 const nsAString
& aDocumentURI
,
44 nsIPrincipal
* aPrincipal
,
49 , mDocumentURI(aDocumentURI
)
50 , mPrincipal(aPrincipal
)
51 , mIsPrivate(aIsPrivate
)
52 , mIsSessionOnly(false)
57 DOMStorage::~DOMStorage()
62 /* virtual */ JSObject
*
63 DOMStorage::WrapObject(JSContext
* aCx
)
65 return StorageBinding::Wrap(aCx
, this);
69 DOMStorage::GetLength(ErrorResult
& aRv
)
71 if (!CanUseStorage(this)) {
72 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
77 aRv
= mCache
->GetLength(this, &length
);
82 DOMStorage::Key(uint32_t aIndex
, nsAString
& aResult
, ErrorResult
& aRv
)
84 if (!CanUseStorage(this)) {
85 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
89 aRv
= mCache
->GetKey(this, aIndex
, aResult
);
93 DOMStorage::GetItem(const nsAString
& aKey
, nsAString
& aResult
, ErrorResult
& aRv
)
95 if (!CanUseStorage(this)) {
96 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
100 aRv
= mCache
->GetItem(this, aKey
, aResult
);
104 DOMStorage::SetItem(const nsAString
& aKey
, const nsAString
& aData
,
107 if (!CanUseStorage(this)) {
108 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
112 Telemetry::Accumulate(GetType() == LocalStorage
113 ? Telemetry::LOCALDOMSTORAGE_KEY_SIZE_BYTES
114 : Telemetry::SESSIONDOMSTORAGE_KEY_SIZE_BYTES
, aKey
.Length());
115 Telemetry::Accumulate(GetType() == LocalStorage
116 ? Telemetry::LOCALDOMSTORAGE_VALUE_SIZE_BYTES
117 : Telemetry::SESSIONDOMSTORAGE_VALUE_SIZE_BYTES
, aData
.Length());
120 bool ok
= data
.Assign(aData
, fallible_t());
122 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
127 aRv
= mCache
->SetItem(this, aKey
, data
, old
);
132 if (aRv
.ErrorCode() != NS_SUCCESS_DOM_NO_OPERATION
) {
133 BroadcastChangeNotification(aKey
, old
, aData
);
138 DOMStorage::RemoveItem(const nsAString
& aKey
, ErrorResult
& aRv
)
140 if (!CanUseStorage(this)) {
141 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
146 aRv
= mCache
->RemoveItem(this, aKey
, old
);
151 if (aRv
.ErrorCode() != NS_SUCCESS_DOM_NO_OPERATION
) {
152 BroadcastChangeNotification(aKey
, old
, NullString());
157 DOMStorage::Clear(ErrorResult
& aRv
)
159 if (!CanUseStorage(this)) {
160 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
164 aRv
= mCache
->Clear(this);
165 if (NS_WARN_IF(aRv
.Failed())) {
169 if (aRv
.ErrorCode() != NS_SUCCESS_DOM_NO_OPERATION
) {
170 BroadcastChangeNotification(NullString(), NullString(), NullString());
176 class StorageNotifierRunnable
: public nsRunnable
179 StorageNotifierRunnable(nsISupports
* aSubject
, const char16_t
* aType
)
180 : mSubject(aSubject
), mType(aType
)
186 nsCOMPtr
<nsISupports
> mSubject
;
187 const char16_t
* mType
;
191 StorageNotifierRunnable::Run()
193 nsCOMPtr
<nsIObserverService
> observerService
=
194 mozilla::services::GetObserverService();
195 if (observerService
) {
196 observerService
->NotifyObservers(mSubject
, "dom-storage2-changed", mType
);
201 } // anonymous namespace
204 DOMStorage::BroadcastChangeNotification(const nsSubstring
& aKey
,
205 const nsSubstring
& aOldValue
,
206 const nsSubstring
& aNewValue
)
208 StorageEventInit dict
;
209 dict
.mBubbles
= false;
210 dict
.mCancelable
= false;
212 dict
.mNewValue
= aNewValue
;
213 dict
.mOldValue
= aOldValue
;
214 dict
.mStorageArea
= this;
215 dict
.mUrl
= mDocumentURI
;
217 // Note, this DOM event should never reach JS. It is cloned later in
219 nsRefPtr
<StorageEvent
> event
=
220 StorageEvent::Constructor(nullptr, NS_LITERAL_STRING("storage"), dict
);
222 nsRefPtr
<StorageNotifierRunnable
> r
=
223 new StorageNotifierRunnable(event
,
224 GetType() == LocalStorage
225 ? MOZ_UTF16("localStorage")
226 : MOZ_UTF16("sessionStorage"));
227 NS_DispatchToMainThread(r
);
230 static const uint32_t ASK_BEFORE_ACCEPT
= 1;
231 static const uint32_t ACCEPT_SESSION
= 2;
232 static const uint32_t BEHAVIOR_REJECT
= 2;
234 static const char kPermissionType
[] = "cookie";
235 static const char kStorageEnabled
[] = "dom.storage.enabled";
236 static const char kCookiesBehavior
[] = "network.cookie.cookieBehavior";
237 static const char kCookiesLifetimePolicy
[] = "network.cookie.lifetimePolicy";
241 DOMStorage::CanUseStorage(DOMStorage
* aStorage
)
243 // This method is responsible for correct setting of mIsSessionOnly.
244 // It doesn't work with mIsPrivate flag at all, since it is checked
245 // regardless mIsSessionOnly flag in DOMStorageCache code.
247 aStorage
->mIsSessionOnly
= false;
250 if (!mozilla::Preferences::GetBool(kStorageEnabled
)) {
254 // chrome can always use aStorage regardless of permission preferences
255 nsCOMPtr
<nsIPrincipal
> subjectPrincipal
=
256 nsContentUtils::SubjectPrincipal();
257 if (nsContentUtils::IsSystemPrincipal(subjectPrincipal
)) {
261 nsCOMPtr
<nsIPermissionManager
> permissionManager
=
262 services::GetPermissionManager();
263 if (!permissionManager
) {
268 permissionManager
->TestPermissionFromPrincipal(subjectPrincipal
,
269 kPermissionType
, &perm
);
271 if (perm
== nsIPermissionManager::DENY_ACTION
) {
275 if (perm
== nsICookiePermission::ACCESS_SESSION
) {
277 aStorage
->mIsSessionOnly
= true;
279 } else if (perm
!= nsIPermissionManager::ALLOW_ACTION
) {
280 uint32_t cookieBehavior
= Preferences::GetUint(kCookiesBehavior
);
281 uint32_t lifetimePolicy
= Preferences::GetUint(kCookiesLifetimePolicy
);
283 // Treat "ask every time" as "reject always".
284 if ((cookieBehavior
== BEHAVIOR_REJECT
|| lifetimePolicy
== ASK_BEFORE_ACCEPT
)) {
288 if (lifetimePolicy
== ACCEPT_SESSION
&& aStorage
) {
289 aStorage
->mIsSessionOnly
= true;
294 return aStorage
->CanAccess(subjectPrincipal
);
300 DOMStorage::StorageType
301 DOMStorage::GetType() const
303 return mManager
->Type();
307 DOMStorage::GetPrincipal()
312 // Defined in DOMStorageManager.cpp
314 PrincipalsEqual(nsIPrincipal
* aObjectPrincipal
, nsIPrincipal
* aSubjectPrincipal
);
317 DOMStorage::PrincipalEquals(nsIPrincipal
* aPrincipal
)
319 return PrincipalsEqual(mPrincipal
, aPrincipal
);
323 DOMStorage::CanAccess(nsIPrincipal
* aPrincipal
)
325 return !aPrincipal
|| aPrincipal
->Subsumes(mPrincipal
);
329 DOMStorage::GetSupportedNames(unsigned, nsTArray
<nsString
>& aKeys
)
331 if (!CanUseStorage(this)) {
332 // return just an empty array
337 mCache
->GetKeys(this, aKeys
);