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 "DOMStorage.h"
7 #include "DOMStorageCache.h"
8 #include "DOMStorageManager.h"
10 #include "nsIDOMStorageEvent.h"
11 #include "nsIObserverService.h"
12 #include "nsIScriptSecurityManager.h"
13 #include "nsIPermissionManager.h"
14 #include "nsIPrincipal.h"
15 #include "nsICookiePermission.h"
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"
25 DOMCI_DATA(Storage, mozilla::dom::DOMStorage)
27 namespace mozilla {
28 namespace dom {
30 NS_IMPL_ADDREF(DOMStorage)
31 NS_IMPL_RELEASE(DOMStorage)
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
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 }
56 DOMStorage::~DOMStorage()
57 {
58 mCache->KeepAlive();
59 }
61 // nsIDOMStorage (web content public API implementation)
63 NS_IMETHODIMP
64 DOMStorage::GetLength(uint32_t* aLength)
65 {
66 if (!CanUseStorage(this)) {
67 return NS_ERROR_DOM_SECURITY_ERR;
68 }
70 return mCache->GetLength(this, aLength);
71 }
73 NS_IMETHODIMP
74 DOMStorage::Key(uint32_t aIndex, nsAString& aRetval)
75 {
76 if (!CanUseStorage(this)) {
77 return NS_ERROR_DOM_SECURITY_ERR;
78 }
80 return mCache->GetKey(this, aIndex, aRetval);
81 }
83 NS_IMETHODIMP
84 DOMStorage::GetItem(const nsAString& aKey, nsAString& aRetval)
85 {
86 if (!CanUseStorage(this)) {
87 return NS_ERROR_DOM_SECURITY_ERR;
88 }
90 return mCache->GetItem(this, aKey, aRetval);
91 }
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 }
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());
107 nsString old;
108 nsresult rv = mCache->SetItem(this, aKey, nsString(aData), old);
109 if (NS_FAILED(rv)) {
110 return rv;
111 }
113 if (rv != NS_SUCCESS_DOM_NO_OPERATION) {
114 BroadcastChangeNotification(aKey, old, aData);
115 }
117 return NS_OK;
118 }
120 NS_IMETHODIMP
121 DOMStorage::RemoveItem(const nsAString& aKey)
122 {
123 if (!CanUseStorage(this)) {
124 return NS_ERROR_DOM_SECURITY_ERR;
125 }
127 nsAutoString old;
128 nsresult rv = mCache->RemoveItem(this, aKey, old);
129 if (NS_FAILED(rv)) {
130 return rv;
131 }
133 if (rv != NS_SUCCESS_DOM_NO_OPERATION) {
134 BroadcastChangeNotification(aKey, old, NullString());
135 }
137 return NS_OK;
138 }
140 NS_IMETHODIMP
141 DOMStorage::Clear()
142 {
143 if (!CanUseStorage(this)) {
144 return NS_ERROR_DOM_SECURITY_ERR;
145 }
147 nsresult rv = mCache->Clear(this);
148 if (NS_FAILED(rv)) {
149 return rv;
150 }
152 if (rv != NS_SUCCESS_DOM_NO_OPERATION) {
153 BroadcastChangeNotification(NullString(), NullString(), NullString());
154 }
156 return NS_OK;
157 }
159 namespace {
161 class StorageNotifierRunnable : public nsRunnable
162 {
163 public:
164 StorageNotifierRunnable(nsISupports* aSubject, const char16_t* aType)
165 : mSubject(aSubject), mType(aType)
166 { }
168 NS_DECL_NSIRUNNABLE
170 private:
171 nsCOMPtr<nsISupports> mSubject;
172 const char16_t* mType;
173 };
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 }
186 } // anonymous namespace
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);
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 }
211 nsRefPtr<StorageNotifierRunnable> r =
212 new StorageNotifierRunnable(event,
213 GetType() == LocalStorage
214 ? MOZ_UTF16("localStorage")
215 : MOZ_UTF16("sessionStorage"));
216 NS_DispatchToMainThread(r);
217 }
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;
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";
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 }
239 if (!mozilla::Preferences::GetBool(kStorageEnabled)) {
240 return false;
241 }
243 // chrome can always use aStorage regardless of permission preferences
244 if (nsContentUtils::IsCallerChrome()) {
245 return true;
246 }
248 nsCOMPtr<nsIPrincipal> subjectPrincipal;
249 nsresult rv = nsContentUtils::GetSecurityManager()->
250 GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
251 NS_ENSURE_SUCCESS(rv, false);
253 // if subjectPrincipal were null we'd have returned after
254 // IsCallerChrome().
256 nsCOMPtr<nsIPermissionManager> permissionManager =
257 do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
258 if (!permissionManager) {
259 return false;
260 }
262 uint32_t perm;
263 permissionManager->TestPermissionFromPrincipal(subjectPrincipal,
264 kPermissionType, &perm);
266 if (perm == nsIPermissionManager::DENY_ACTION) {
267 return false;
268 }
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);
278 // Treat "ask every time" as "reject always".
279 if ((cookieBehavior == BEHAVIOR_REJECT || lifetimePolicy == ASK_BEFORE_ACCEPT)) {
280 return false;
281 }
283 if (lifetimePolicy == ACCEPT_SESSION && aStorage) {
284 aStorage->mIsSessionOnly = true;
285 }
286 }
288 if (aStorage) {
289 return aStorage->CanAccess(subjectPrincipal);
290 }
292 return true;
293 }
295 // nsPIDOMStorage
297 nsPIDOMStorage::StorageType
298 DOMStorage::GetType() const
299 {
300 return mManager->Type();
301 }
303 nsIPrincipal*
304 DOMStorage::GetPrincipal()
305 {
306 return mPrincipal;
307 }
309 // Defined in DOMStorageManager.cpp
310 extern bool
311 PrincipalsEqual(nsIPrincipal* aObjectPrincipal, nsIPrincipal* aSubjectPrincipal);
313 bool
314 DOMStorage::PrincipalEquals(nsIPrincipal* aPrincipal)
315 {
316 return PrincipalsEqual(mPrincipal, aPrincipal);
317 }
319 bool
320 DOMStorage::CanAccess(nsIPrincipal* aPrincipal)
321 {
322 return !aPrincipal || aPrincipal->Subsumes(mPrincipal);
323 }
325 nsTArray<nsString>*
326 DOMStorage::GetKeys()
327 {
328 if (!CanUseStorage(this)) {
329 return new nsTArray<nsString>(); // return just an empty array
330 }
332 return mCache->GetKeys(this);
333 }
335 } // ::dom
336 } // ::mozilla