1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/src/storage/DOMStorage.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,336 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "DOMStorage.h" 1.10 +#include "DOMStorageCache.h" 1.11 +#include "DOMStorageManager.h" 1.12 + 1.13 +#include "nsIDOMStorageEvent.h" 1.14 +#include "nsIObserverService.h" 1.15 +#include "nsIScriptSecurityManager.h" 1.16 +#include "nsIPermissionManager.h" 1.17 +#include "nsIPrincipal.h" 1.18 +#include "nsICookiePermission.h" 1.19 + 1.20 +#include "nsDOMClassInfoID.h" 1.21 +#include "mozilla/Services.h" 1.22 +#include "mozilla/Preferences.h" 1.23 +#include "GeneratedEvents.h" 1.24 +#include "nsThreadUtils.h" 1.25 +#include "nsContentUtils.h" 1.26 +#include "nsServiceManagerUtils.h" 1.27 + 1.28 +DOMCI_DATA(Storage, mozilla::dom::DOMStorage) 1.29 + 1.30 +namespace mozilla { 1.31 +namespace dom { 1.32 + 1.33 +NS_IMPL_ADDREF(DOMStorage) 1.34 +NS_IMPL_RELEASE(DOMStorage) 1.35 + 1.36 +NS_INTERFACE_MAP_BEGIN(DOMStorage) 1.37 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMStorage) 1.38 + NS_INTERFACE_MAP_ENTRY(nsIDOMStorage) 1.39 + NS_INTERFACE_MAP_ENTRY(nsPIDOMStorage) 1.40 + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 1.41 + NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Storage) 1.42 +NS_INTERFACE_MAP_END 1.43 + 1.44 +DOMStorage::DOMStorage(DOMStorageManager* aManager, 1.45 + DOMStorageCache* aCache, 1.46 + const nsAString& aDocumentURI, 1.47 + nsIPrincipal* aPrincipal, 1.48 + bool aIsPrivate) 1.49 +: mManager(aManager) 1.50 +, mCache(aCache) 1.51 +, mDocumentURI(aDocumentURI) 1.52 +, mPrincipal(aPrincipal) 1.53 +, mIsPrivate(aIsPrivate) 1.54 +, mIsSessionOnly(false) 1.55 +{ 1.56 + mCache->Preload(); 1.57 +} 1.58 + 1.59 +DOMStorage::~DOMStorage() 1.60 +{ 1.61 + mCache->KeepAlive(); 1.62 +} 1.63 + 1.64 +// nsIDOMStorage (web content public API implementation) 1.65 + 1.66 +NS_IMETHODIMP 1.67 +DOMStorage::GetLength(uint32_t* aLength) 1.68 +{ 1.69 + if (!CanUseStorage(this)) { 1.70 + return NS_ERROR_DOM_SECURITY_ERR; 1.71 + } 1.72 + 1.73 + return mCache->GetLength(this, aLength); 1.74 +} 1.75 + 1.76 +NS_IMETHODIMP 1.77 +DOMStorage::Key(uint32_t aIndex, nsAString& aRetval) 1.78 +{ 1.79 + if (!CanUseStorage(this)) { 1.80 + return NS_ERROR_DOM_SECURITY_ERR; 1.81 + } 1.82 + 1.83 + return mCache->GetKey(this, aIndex, aRetval); 1.84 +} 1.85 + 1.86 +NS_IMETHODIMP 1.87 +DOMStorage::GetItem(const nsAString& aKey, nsAString& aRetval) 1.88 +{ 1.89 + if (!CanUseStorage(this)) { 1.90 + return NS_ERROR_DOM_SECURITY_ERR; 1.91 + } 1.92 + 1.93 + return mCache->GetItem(this, aKey, aRetval); 1.94 +} 1.95 + 1.96 +NS_IMETHODIMP 1.97 +DOMStorage::SetItem(const nsAString& aKey, const nsAString& aData) 1.98 +{ 1.99 + if (!CanUseStorage(this)) { 1.100 + return NS_ERROR_DOM_SECURITY_ERR; 1.101 + } 1.102 + 1.103 + Telemetry::Accumulate(GetType() == LocalStorage 1.104 + ? Telemetry::LOCALDOMSTORAGE_KEY_SIZE_BYTES 1.105 + : Telemetry::SESSIONDOMSTORAGE_KEY_SIZE_BYTES, aKey.Length()); 1.106 + Telemetry::Accumulate(GetType() == LocalStorage 1.107 + ? Telemetry::LOCALDOMSTORAGE_VALUE_SIZE_BYTES 1.108 + : Telemetry::SESSIONDOMSTORAGE_VALUE_SIZE_BYTES, aData.Length()); 1.109 + 1.110 + nsString old; 1.111 + nsresult rv = mCache->SetItem(this, aKey, nsString(aData), old); 1.112 + if (NS_FAILED(rv)) { 1.113 + return rv; 1.114 + } 1.115 + 1.116 + if (rv != NS_SUCCESS_DOM_NO_OPERATION) { 1.117 + BroadcastChangeNotification(aKey, old, aData); 1.118 + } 1.119 + 1.120 + return NS_OK; 1.121 +} 1.122 + 1.123 +NS_IMETHODIMP 1.124 +DOMStorage::RemoveItem(const nsAString& aKey) 1.125 +{ 1.126 + if (!CanUseStorage(this)) { 1.127 + return NS_ERROR_DOM_SECURITY_ERR; 1.128 + } 1.129 + 1.130 + nsAutoString old; 1.131 + nsresult rv = mCache->RemoveItem(this, aKey, old); 1.132 + if (NS_FAILED(rv)) { 1.133 + return rv; 1.134 + } 1.135 + 1.136 + if (rv != NS_SUCCESS_DOM_NO_OPERATION) { 1.137 + BroadcastChangeNotification(aKey, old, NullString()); 1.138 + } 1.139 + 1.140 + return NS_OK; 1.141 +} 1.142 + 1.143 +NS_IMETHODIMP 1.144 +DOMStorage::Clear() 1.145 +{ 1.146 + if (!CanUseStorage(this)) { 1.147 + return NS_ERROR_DOM_SECURITY_ERR; 1.148 + } 1.149 + 1.150 + nsresult rv = mCache->Clear(this); 1.151 + if (NS_FAILED(rv)) { 1.152 + return rv; 1.153 + } 1.154 + 1.155 + if (rv != NS_SUCCESS_DOM_NO_OPERATION) { 1.156 + BroadcastChangeNotification(NullString(), NullString(), NullString()); 1.157 + } 1.158 + 1.159 + return NS_OK; 1.160 +} 1.161 + 1.162 +namespace { 1.163 + 1.164 +class StorageNotifierRunnable : public nsRunnable 1.165 +{ 1.166 +public: 1.167 + StorageNotifierRunnable(nsISupports* aSubject, const char16_t* aType) 1.168 + : mSubject(aSubject), mType(aType) 1.169 + { } 1.170 + 1.171 + NS_DECL_NSIRUNNABLE 1.172 + 1.173 +private: 1.174 + nsCOMPtr<nsISupports> mSubject; 1.175 + const char16_t* mType; 1.176 +}; 1.177 + 1.178 +NS_IMETHODIMP 1.179 +StorageNotifierRunnable::Run() 1.180 +{ 1.181 + nsCOMPtr<nsIObserverService> observerService = 1.182 + mozilla::services::GetObserverService(); 1.183 + if (observerService) { 1.184 + observerService->NotifyObservers(mSubject, "dom-storage2-changed", mType); 1.185 + } 1.186 + return NS_OK; 1.187 +} 1.188 + 1.189 +} // anonymous namespace 1.190 + 1.191 +void 1.192 +DOMStorage::BroadcastChangeNotification(const nsSubstring& aKey, 1.193 + const nsSubstring& aOldValue, 1.194 + const nsSubstring& aNewValue) 1.195 +{ 1.196 + nsCOMPtr<nsIDOMEvent> domEvent; 1.197 + // Note, this DOM event should never reach JS. It is cloned later in 1.198 + // nsGlobalWindow. 1.199 + NS_NewDOMStorageEvent(getter_AddRefs(domEvent), nullptr, nullptr, nullptr); 1.200 + 1.201 + nsCOMPtr<nsIDOMStorageEvent> event = do_QueryInterface(domEvent); 1.202 + nsresult rv = event->InitStorageEvent(NS_LITERAL_STRING("storage"), 1.203 + false, 1.204 + false, 1.205 + aKey, 1.206 + aOldValue, 1.207 + aNewValue, 1.208 + mDocumentURI, 1.209 + static_cast<nsIDOMStorage*>(this)); 1.210 + if (NS_FAILED(rv)) { 1.211 + return; 1.212 + } 1.213 + 1.214 + nsRefPtr<StorageNotifierRunnable> r = 1.215 + new StorageNotifierRunnable(event, 1.216 + GetType() == LocalStorage 1.217 + ? MOZ_UTF16("localStorage") 1.218 + : MOZ_UTF16("sessionStorage")); 1.219 + NS_DispatchToMainThread(r); 1.220 +} 1.221 + 1.222 +static const uint32_t ASK_BEFORE_ACCEPT = 1; 1.223 +static const uint32_t ACCEPT_SESSION = 2; 1.224 +static const uint32_t BEHAVIOR_REJECT = 2; 1.225 + 1.226 +static const char kPermissionType[] = "cookie"; 1.227 +static const char kStorageEnabled[] = "dom.storage.enabled"; 1.228 +static const char kCookiesBehavior[] = "network.cookie.cookieBehavior"; 1.229 +static const char kCookiesLifetimePolicy[] = "network.cookie.lifetimePolicy"; 1.230 + 1.231 +// static, public 1.232 +bool 1.233 +DOMStorage::CanUseStorage(DOMStorage* aStorage) 1.234 +{ 1.235 + // This method is responsible for correct setting of mIsSessionOnly. 1.236 + // It doesn't work with mIsPrivate flag at all, since it is checked 1.237 + // regardless mIsSessionOnly flag in DOMStorageCache code. 1.238 + if (aStorage) { 1.239 + aStorage->mIsSessionOnly = false; 1.240 + } 1.241 + 1.242 + if (!mozilla::Preferences::GetBool(kStorageEnabled)) { 1.243 + return false; 1.244 + } 1.245 + 1.246 + // chrome can always use aStorage regardless of permission preferences 1.247 + if (nsContentUtils::IsCallerChrome()) { 1.248 + return true; 1.249 + } 1.250 + 1.251 + nsCOMPtr<nsIPrincipal> subjectPrincipal; 1.252 + nsresult rv = nsContentUtils::GetSecurityManager()-> 1.253 + GetSubjectPrincipal(getter_AddRefs(subjectPrincipal)); 1.254 + NS_ENSURE_SUCCESS(rv, false); 1.255 + 1.256 + // if subjectPrincipal were null we'd have returned after 1.257 + // IsCallerChrome(). 1.258 + 1.259 + nsCOMPtr<nsIPermissionManager> permissionManager = 1.260 + do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); 1.261 + if (!permissionManager) { 1.262 + return false; 1.263 + } 1.264 + 1.265 + uint32_t perm; 1.266 + permissionManager->TestPermissionFromPrincipal(subjectPrincipal, 1.267 + kPermissionType, &perm); 1.268 + 1.269 + if (perm == nsIPermissionManager::DENY_ACTION) { 1.270 + return false; 1.271 + } 1.272 + 1.273 + if (perm == nsICookiePermission::ACCESS_SESSION) { 1.274 + if (aStorage) { 1.275 + aStorage->mIsSessionOnly = true; 1.276 + } 1.277 + } else if (perm != nsIPermissionManager::ALLOW_ACTION) { 1.278 + uint32_t cookieBehavior = Preferences::GetUint(kCookiesBehavior); 1.279 + uint32_t lifetimePolicy = Preferences::GetUint(kCookiesLifetimePolicy); 1.280 + 1.281 + // Treat "ask every time" as "reject always". 1.282 + if ((cookieBehavior == BEHAVIOR_REJECT || lifetimePolicy == ASK_BEFORE_ACCEPT)) { 1.283 + return false; 1.284 + } 1.285 + 1.286 + if (lifetimePolicy == ACCEPT_SESSION && aStorage) { 1.287 + aStorage->mIsSessionOnly = true; 1.288 + } 1.289 + } 1.290 + 1.291 + if (aStorage) { 1.292 + return aStorage->CanAccess(subjectPrincipal); 1.293 + } 1.294 + 1.295 + return true; 1.296 +} 1.297 + 1.298 +// nsPIDOMStorage 1.299 + 1.300 +nsPIDOMStorage::StorageType 1.301 +DOMStorage::GetType() const 1.302 +{ 1.303 + return mManager->Type(); 1.304 +} 1.305 + 1.306 +nsIPrincipal* 1.307 +DOMStorage::GetPrincipal() 1.308 +{ 1.309 + return mPrincipal; 1.310 +} 1.311 + 1.312 +// Defined in DOMStorageManager.cpp 1.313 +extern bool 1.314 +PrincipalsEqual(nsIPrincipal* aObjectPrincipal, nsIPrincipal* aSubjectPrincipal); 1.315 + 1.316 +bool 1.317 +DOMStorage::PrincipalEquals(nsIPrincipal* aPrincipal) 1.318 +{ 1.319 + return PrincipalsEqual(mPrincipal, aPrincipal); 1.320 +} 1.321 + 1.322 +bool 1.323 +DOMStorage::CanAccess(nsIPrincipal* aPrincipal) 1.324 +{ 1.325 + return !aPrincipal || aPrincipal->Subsumes(mPrincipal); 1.326 +} 1.327 + 1.328 +nsTArray<nsString>* 1.329 +DOMStorage::GetKeys() 1.330 +{ 1.331 + if (!CanUseStorage(this)) { 1.332 + return new nsTArray<nsString>(); // return just an empty array 1.333 + } 1.334 + 1.335 + return mCache->GetKeys(this); 1.336 +} 1.337 + 1.338 +} // ::dom 1.339 +} // ::mozilla