dom/src/storage/DOMStorage.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:6da8775b78de
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/. */
5
6 #include "DOMStorage.h"
7 #include "DOMStorageCache.h"
8 #include "DOMStorageManager.h"
9
10 #include "nsIDOMStorageEvent.h"
11 #include "nsIObserverService.h"
12 #include "nsIScriptSecurityManager.h"
13 #include "nsIPermissionManager.h"
14 #include "nsIPrincipal.h"
15 #include "nsICookiePermission.h"
16
17 #include "nsDOMClassInfoID.h"
18 #include "mozilla/Services.h"
19 #include "mozilla/Preferences.h"
20 #include "GeneratedEvents.h"
21 #include "nsThreadUtils.h"
22 #include "nsContentUtils.h"
23 #include "nsServiceManagerUtils.h"
24
25 DOMCI_DATA(Storage, mozilla::dom::DOMStorage)
26
27 namespace mozilla {
28 namespace dom {
29
30 NS_IMPL_ADDREF(DOMStorage)
31 NS_IMPL_RELEASE(DOMStorage)
32
33 NS_INTERFACE_MAP_BEGIN(DOMStorage)
34 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMStorage)
35 NS_INTERFACE_MAP_ENTRY(nsIDOMStorage)
36 NS_INTERFACE_MAP_ENTRY(nsPIDOMStorage)
37 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
38 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Storage)
39 NS_INTERFACE_MAP_END
40
41 DOMStorage::DOMStorage(DOMStorageManager* aManager,
42 DOMStorageCache* aCache,
43 const nsAString& aDocumentURI,
44 nsIPrincipal* aPrincipal,
45 bool aIsPrivate)
46 : mManager(aManager)
47 , mCache(aCache)
48 , mDocumentURI(aDocumentURI)
49 , mPrincipal(aPrincipal)
50 , mIsPrivate(aIsPrivate)
51 , mIsSessionOnly(false)
52 {
53 mCache->Preload();
54 }
55
56 DOMStorage::~DOMStorage()
57 {
58 mCache->KeepAlive();
59 }
60
61 // nsIDOMStorage (web content public API implementation)
62
63 NS_IMETHODIMP
64 DOMStorage::GetLength(uint32_t* aLength)
65 {
66 if (!CanUseStorage(this)) {
67 return NS_ERROR_DOM_SECURITY_ERR;
68 }
69
70 return mCache->GetLength(this, aLength);
71 }
72
73 NS_IMETHODIMP
74 DOMStorage::Key(uint32_t aIndex, nsAString& aRetval)
75 {
76 if (!CanUseStorage(this)) {
77 return NS_ERROR_DOM_SECURITY_ERR;
78 }
79
80 return mCache->GetKey(this, aIndex, aRetval);
81 }
82
83 NS_IMETHODIMP
84 DOMStorage::GetItem(const nsAString& aKey, nsAString& aRetval)
85 {
86 if (!CanUseStorage(this)) {
87 return NS_ERROR_DOM_SECURITY_ERR;
88 }
89
90 return mCache->GetItem(this, aKey, aRetval);
91 }
92
93 NS_IMETHODIMP
94 DOMStorage::SetItem(const nsAString& aKey, const nsAString& aData)
95 {
96 if (!CanUseStorage(this)) {
97 return NS_ERROR_DOM_SECURITY_ERR;
98 }
99
100 Telemetry::Accumulate(GetType() == LocalStorage
101 ? Telemetry::LOCALDOMSTORAGE_KEY_SIZE_BYTES
102 : Telemetry::SESSIONDOMSTORAGE_KEY_SIZE_BYTES, aKey.Length());
103 Telemetry::Accumulate(GetType() == LocalStorage
104 ? Telemetry::LOCALDOMSTORAGE_VALUE_SIZE_BYTES
105 : Telemetry::SESSIONDOMSTORAGE_VALUE_SIZE_BYTES, aData.Length());
106
107 nsString old;
108 nsresult rv = mCache->SetItem(this, aKey, nsString(aData), old);
109 if (NS_FAILED(rv)) {
110 return rv;
111 }
112
113 if (rv != NS_SUCCESS_DOM_NO_OPERATION) {
114 BroadcastChangeNotification(aKey, old, aData);
115 }
116
117 return NS_OK;
118 }
119
120 NS_IMETHODIMP
121 DOMStorage::RemoveItem(const nsAString& aKey)
122 {
123 if (!CanUseStorage(this)) {
124 return NS_ERROR_DOM_SECURITY_ERR;
125 }
126
127 nsAutoString old;
128 nsresult rv = mCache->RemoveItem(this, aKey, old);
129 if (NS_FAILED(rv)) {
130 return rv;
131 }
132
133 if (rv != NS_SUCCESS_DOM_NO_OPERATION) {
134 BroadcastChangeNotification(aKey, old, NullString());
135 }
136
137 return NS_OK;
138 }
139
140 NS_IMETHODIMP
141 DOMStorage::Clear()
142 {
143 if (!CanUseStorage(this)) {
144 return NS_ERROR_DOM_SECURITY_ERR;
145 }
146
147 nsresult rv = mCache->Clear(this);
148 if (NS_FAILED(rv)) {
149 return rv;
150 }
151
152 if (rv != NS_SUCCESS_DOM_NO_OPERATION) {
153 BroadcastChangeNotification(NullString(), NullString(), NullString());
154 }
155
156 return NS_OK;
157 }
158
159 namespace {
160
161 class StorageNotifierRunnable : public nsRunnable
162 {
163 public:
164 StorageNotifierRunnable(nsISupports* aSubject, const char16_t* aType)
165 : mSubject(aSubject), mType(aType)
166 { }
167
168 NS_DECL_NSIRUNNABLE
169
170 private:
171 nsCOMPtr<nsISupports> mSubject;
172 const char16_t* mType;
173 };
174
175 NS_IMETHODIMP
176 StorageNotifierRunnable::Run()
177 {
178 nsCOMPtr<nsIObserverService> observerService =
179 mozilla::services::GetObserverService();
180 if (observerService) {
181 observerService->NotifyObservers(mSubject, "dom-storage2-changed", mType);
182 }
183 return NS_OK;
184 }
185
186 } // anonymous namespace
187
188 void
189 DOMStorage::BroadcastChangeNotification(const nsSubstring& aKey,
190 const nsSubstring& aOldValue,
191 const nsSubstring& aNewValue)
192 {
193 nsCOMPtr<nsIDOMEvent> domEvent;
194 // Note, this DOM event should never reach JS. It is cloned later in
195 // nsGlobalWindow.
196 NS_NewDOMStorageEvent(getter_AddRefs(domEvent), nullptr, nullptr, nullptr);
197
198 nsCOMPtr<nsIDOMStorageEvent> event = do_QueryInterface(domEvent);
199 nsresult rv = event->InitStorageEvent(NS_LITERAL_STRING("storage"),
200 false,
201 false,
202 aKey,
203 aOldValue,
204 aNewValue,
205 mDocumentURI,
206 static_cast<nsIDOMStorage*>(this));
207 if (NS_FAILED(rv)) {
208 return;
209 }
210
211 nsRefPtr<StorageNotifierRunnable> r =
212 new StorageNotifierRunnable(event,
213 GetType() == LocalStorage
214 ? MOZ_UTF16("localStorage")
215 : MOZ_UTF16("sessionStorage"));
216 NS_DispatchToMainThread(r);
217 }
218
219 static const uint32_t ASK_BEFORE_ACCEPT = 1;
220 static const uint32_t ACCEPT_SESSION = 2;
221 static const uint32_t BEHAVIOR_REJECT = 2;
222
223 static const char kPermissionType[] = "cookie";
224 static const char kStorageEnabled[] = "dom.storage.enabled";
225 static const char kCookiesBehavior[] = "network.cookie.cookieBehavior";
226 static const char kCookiesLifetimePolicy[] = "network.cookie.lifetimePolicy";
227
228 // static, public
229 bool
230 DOMStorage::CanUseStorage(DOMStorage* aStorage)
231 {
232 // This method is responsible for correct setting of mIsSessionOnly.
233 // It doesn't work with mIsPrivate flag at all, since it is checked
234 // regardless mIsSessionOnly flag in DOMStorageCache code.
235 if (aStorage) {
236 aStorage->mIsSessionOnly = false;
237 }
238
239 if (!mozilla::Preferences::GetBool(kStorageEnabled)) {
240 return false;
241 }
242
243 // chrome can always use aStorage regardless of permission preferences
244 if (nsContentUtils::IsCallerChrome()) {
245 return true;
246 }
247
248 nsCOMPtr<nsIPrincipal> subjectPrincipal;
249 nsresult rv = nsContentUtils::GetSecurityManager()->
250 GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
251 NS_ENSURE_SUCCESS(rv, false);
252
253 // if subjectPrincipal were null we'd have returned after
254 // IsCallerChrome().
255
256 nsCOMPtr<nsIPermissionManager> permissionManager =
257 do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
258 if (!permissionManager) {
259 return false;
260 }
261
262 uint32_t perm;
263 permissionManager->TestPermissionFromPrincipal(subjectPrincipal,
264 kPermissionType, &perm);
265
266 if (perm == nsIPermissionManager::DENY_ACTION) {
267 return false;
268 }
269
270 if (perm == nsICookiePermission::ACCESS_SESSION) {
271 if (aStorage) {
272 aStorage->mIsSessionOnly = true;
273 }
274 } else if (perm != nsIPermissionManager::ALLOW_ACTION) {
275 uint32_t cookieBehavior = Preferences::GetUint(kCookiesBehavior);
276 uint32_t lifetimePolicy = Preferences::GetUint(kCookiesLifetimePolicy);
277
278 // Treat "ask every time" as "reject always".
279 if ((cookieBehavior == BEHAVIOR_REJECT || lifetimePolicy == ASK_BEFORE_ACCEPT)) {
280 return false;
281 }
282
283 if (lifetimePolicy == ACCEPT_SESSION && aStorage) {
284 aStorage->mIsSessionOnly = true;
285 }
286 }
287
288 if (aStorage) {
289 return aStorage->CanAccess(subjectPrincipal);
290 }
291
292 return true;
293 }
294
295 // nsPIDOMStorage
296
297 nsPIDOMStorage::StorageType
298 DOMStorage::GetType() const
299 {
300 return mManager->Type();
301 }
302
303 nsIPrincipal*
304 DOMStorage::GetPrincipal()
305 {
306 return mPrincipal;
307 }
308
309 // Defined in DOMStorageManager.cpp
310 extern bool
311 PrincipalsEqual(nsIPrincipal* aObjectPrincipal, nsIPrincipal* aSubjectPrincipal);
312
313 bool
314 DOMStorage::PrincipalEquals(nsIPrincipal* aPrincipal)
315 {
316 return PrincipalsEqual(mPrincipal, aPrincipal);
317 }
318
319 bool
320 DOMStorage::CanAccess(nsIPrincipal* aPrincipal)
321 {
322 return !aPrincipal || aPrincipal->Subsumes(mPrincipal);
323 }
324
325 nsTArray<nsString>*
326 DOMStorage::GetKeys()
327 {
328 if (!CanUseStorage(this)) {
329 return new nsTArray<nsString>(); // return just an empty array
330 }
331
332 return mCache->GetKeys(this);
333 }
334
335 } // ::dom
336 } // ::mozilla

mercurial