dom/src/storage/DOMStorage.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial