Bumping manifests a=b2g-bump
[gecko.git] / dom / storage / DOMStorage.cpp
blob121ed5486daf578bbcba017af5376423320e7b26
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"
25 namespace mozilla {
26 namespace dom {
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)
38 NS_INTERFACE_MAP_END
40 DOMStorage::DOMStorage(nsIDOMWindow* aWindow,
41 DOMStorageManager* aManager,
42 DOMStorageCache* aCache,
43 const nsAString& aDocumentURI,
44 nsIPrincipal* aPrincipal,
45 bool aIsPrivate)
46 : mWindow(aWindow)
47 , mManager(aManager)
48 , mCache(aCache)
49 , mDocumentURI(aDocumentURI)
50 , mPrincipal(aPrincipal)
51 , mIsPrivate(aIsPrivate)
52 , mIsSessionOnly(false)
54 mCache->Preload();
57 DOMStorage::~DOMStorage()
59 mCache->KeepAlive();
62 /* virtual */ JSObject*
63 DOMStorage::WrapObject(JSContext* aCx)
65 return StorageBinding::Wrap(aCx, this);
68 uint32_t
69 DOMStorage::GetLength(ErrorResult& aRv)
71 if (!CanUseStorage(this)) {
72 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
73 return 0;
76 uint32_t length;
77 aRv = mCache->GetLength(this, &length);
78 return length;
81 void
82 DOMStorage::Key(uint32_t aIndex, nsAString& aResult, ErrorResult& aRv)
84 if (!CanUseStorage(this)) {
85 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
86 return;
89 aRv = mCache->GetKey(this, aIndex, aResult);
92 void
93 DOMStorage::GetItem(const nsAString& aKey, nsAString& aResult, ErrorResult& aRv)
95 if (!CanUseStorage(this)) {
96 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
97 return;
100 aRv = mCache->GetItem(this, aKey, aResult);
103 void
104 DOMStorage::SetItem(const nsAString& aKey, const nsAString& aData,
105 ErrorResult& aRv)
107 if (!CanUseStorage(this)) {
108 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
109 return;
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());
119 nsString data;
120 bool ok = data.Assign(aData, fallible_t());
121 if (!ok) {
122 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
123 return;
126 nsString old;
127 aRv = mCache->SetItem(this, aKey, data, old);
128 if (aRv.Failed()) {
129 return;
132 if (aRv.ErrorCode() != NS_SUCCESS_DOM_NO_OPERATION) {
133 BroadcastChangeNotification(aKey, old, aData);
137 void
138 DOMStorage::RemoveItem(const nsAString& aKey, ErrorResult& aRv)
140 if (!CanUseStorage(this)) {
141 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
142 return;
145 nsAutoString old;
146 aRv = mCache->RemoveItem(this, aKey, old);
147 if (aRv.Failed()) {
148 return;
151 if (aRv.ErrorCode() != NS_SUCCESS_DOM_NO_OPERATION) {
152 BroadcastChangeNotification(aKey, old, NullString());
156 void
157 DOMStorage::Clear(ErrorResult& aRv)
159 if (!CanUseStorage(this)) {
160 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
161 return;
164 aRv = mCache->Clear(this);
165 if (NS_WARN_IF(aRv.Failed())) {
166 return;
169 if (aRv.ErrorCode() != NS_SUCCESS_DOM_NO_OPERATION) {
170 BroadcastChangeNotification(NullString(), NullString(), NullString());
174 namespace {
176 class StorageNotifierRunnable : public nsRunnable
178 public:
179 StorageNotifierRunnable(nsISupports* aSubject, const char16_t* aType)
180 : mSubject(aSubject), mType(aType)
183 NS_DECL_NSIRUNNABLE
185 private:
186 nsCOMPtr<nsISupports> mSubject;
187 const char16_t* mType;
190 NS_IMETHODIMP
191 StorageNotifierRunnable::Run()
193 nsCOMPtr<nsIObserverService> observerService =
194 mozilla::services::GetObserverService();
195 if (observerService) {
196 observerService->NotifyObservers(mSubject, "dom-storage2-changed", mType);
198 return NS_OK;
201 } // anonymous namespace
203 void
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;
211 dict.mKey = aKey;
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
218 // nsGlobalWindow.
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";
239 // static, public
240 bool
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.
246 if (aStorage) {
247 aStorage->mIsSessionOnly = false;
250 if (!mozilla::Preferences::GetBool(kStorageEnabled)) {
251 return false;
254 // chrome can always use aStorage regardless of permission preferences
255 nsCOMPtr<nsIPrincipal> subjectPrincipal =
256 nsContentUtils::SubjectPrincipal();
257 if (nsContentUtils::IsSystemPrincipal(subjectPrincipal)) {
258 return true;
261 nsCOMPtr<nsIPermissionManager> permissionManager =
262 services::GetPermissionManager();
263 if (!permissionManager) {
264 return false;
267 uint32_t perm;
268 permissionManager->TestPermissionFromPrincipal(subjectPrincipal,
269 kPermissionType, &perm);
271 if (perm == nsIPermissionManager::DENY_ACTION) {
272 return false;
275 if (perm == nsICookiePermission::ACCESS_SESSION) {
276 if (aStorage) {
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)) {
285 return false;
288 if (lifetimePolicy == ACCEPT_SESSION && aStorage) {
289 aStorage->mIsSessionOnly = true;
293 if (aStorage) {
294 return aStorage->CanAccess(subjectPrincipal);
297 return true;
300 DOMStorage::StorageType
301 DOMStorage::GetType() const
303 return mManager->Type();
306 nsIPrincipal*
307 DOMStorage::GetPrincipal()
309 return mPrincipal;
312 // Defined in DOMStorageManager.cpp
313 extern bool
314 PrincipalsEqual(nsIPrincipal* aObjectPrincipal, nsIPrincipal* aSubjectPrincipal);
316 bool
317 DOMStorage::PrincipalEquals(nsIPrincipal* aPrincipal)
319 return PrincipalsEqual(mPrincipal, aPrincipal);
322 bool
323 DOMStorage::CanAccess(nsIPrincipal* aPrincipal)
325 return !aPrincipal || aPrincipal->Subsumes(mPrincipal);
328 void
329 DOMStorage::GetSupportedNames(unsigned, nsTArray<nsString>& aKeys)
331 if (!CanUseStorage(this)) {
332 // return just an empty array
333 aKeys.Clear();
334 return;
337 mCache->GetKeys(this, aKeys);
340 } // ::dom
341 } // ::mozilla