forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			277 lines
		
	
	
	
		
			7.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			277 lines
		
	
	
	
		
			7.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "LocalStorage.h"
 | |
| #include "LocalStorageCache.h"
 | |
| #include "LocalStorageManager.h"
 | |
| #include "StorageUtils.h"
 | |
| 
 | |
| #include "nsIObserverService.h"
 | |
| #include "nsIScriptSecurityManager.h"
 | |
| #include "nsIPermissionManager.h"
 | |
| #include "nsIPrincipal.h"
 | |
| #include "nsICookiePermission.h"
 | |
| 
 | |
| #include "mozilla/dom/ContentChild.h"
 | |
| #include "mozilla/dom/ContentParent.h"
 | |
| #include "mozilla/dom/PermissionMessageUtils.h"
 | |
| #include "mozilla/dom/StorageBinding.h"
 | |
| #include "mozilla/dom/StorageEvent.h"
 | |
| #include "mozilla/dom/StorageEventBinding.h"
 | |
| #include "mozilla/ipc/BackgroundChild.h"
 | |
| #include "mozilla/ipc/PBackgroundChild.h"
 | |
| #include "mozilla/Services.h"
 | |
| #include "mozilla/Preferences.h"
 | |
| #include "mozilla/EnumSet.h"
 | |
| #include "nsThreadUtils.h"
 | |
| #include "nsContentUtils.h"
 | |
| #include "nsServiceManagerUtils.h"
 | |
| 
 | |
| namespace mozilla {
 | |
| 
 | |
| using namespace ipc;
 | |
| 
 | |
| namespace dom {
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_INHERITED(LocalStorage, Storage, mManager);
 | |
| 
 | |
| NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LocalStorage)
 | |
|   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
 | |
| NS_INTERFACE_MAP_END_INHERITING(Storage)
 | |
| 
 | |
| NS_IMPL_ADDREF_INHERITED(LocalStorage, Storage)
 | |
| NS_IMPL_RELEASE_INHERITED(LocalStorage, Storage)
 | |
| 
 | |
| LocalStorage::LocalStorage(nsPIDOMWindowInner* aWindow,
 | |
|                            LocalStorageManager* aManager,
 | |
|                            LocalStorageCache* aCache,
 | |
|                            const nsAString& aDocumentURI,
 | |
|                            nsIPrincipal* aPrincipal,
 | |
|                            bool aIsPrivate)
 | |
|   : Storage(aWindow, aPrincipal)
 | |
|   , mManager(aManager)
 | |
|   , mCache(aCache)
 | |
|   , mDocumentURI(aDocumentURI)
 | |
|   , mIsPrivate(aIsPrivate)
 | |
| {
 | |
|   mCache->Preload();
 | |
| }
 | |
| 
 | |
| LocalStorage::~LocalStorage()
 | |
| {
 | |
| }
 | |
| 
 | |
| int64_t
 | |
| LocalStorage::GetOriginQuotaUsage() const
 | |
| {
 | |
|   return mCache->GetOriginQuotaUsage(this);
 | |
| }
 | |
| 
 | |
| uint32_t
 | |
| LocalStorage::GetLength(nsIPrincipal& aSubjectPrincipal,
 | |
|                         ErrorResult& aRv)
 | |
| {
 | |
|   if (!CanUseStorage(aSubjectPrincipal)) {
 | |
|     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   uint32_t length;
 | |
|   aRv = mCache->GetLength(this, &length);
 | |
|   return length;
 | |
| }
 | |
| 
 | |
| void
 | |
| LocalStorage::Key(uint32_t aIndex, nsAString& aResult,
 | |
|                   nsIPrincipal& aSubjectPrincipal,
 | |
|                   ErrorResult& aRv)
 | |
| {
 | |
|   if (!CanUseStorage(aSubjectPrincipal)) {
 | |
|     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   aRv = mCache->GetKey(this, aIndex, aResult);
 | |
| }
 | |
| 
 | |
| void
 | |
| LocalStorage::GetItem(const nsAString& aKey, nsAString& aResult,
 | |
|                       nsIPrincipal& aSubjectPrincipal,
 | |
|                       ErrorResult& aRv)
 | |
| {
 | |
|   if (!CanUseStorage(aSubjectPrincipal)) {
 | |
|     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   aRv = mCache->GetItem(this, aKey, aResult);
 | |
| }
 | |
| 
 | |
| void
 | |
| LocalStorage::SetItem(const nsAString& aKey, const nsAString& aData,
 | |
|                       nsIPrincipal& aSubjectPrincipal,
 | |
|                       ErrorResult& aRv)
 | |
| {
 | |
|   if (!CanUseStorage(aSubjectPrincipal)) {
 | |
|     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   nsString data;
 | |
|   bool ok = data.Assign(aData, fallible);
 | |
|   if (!ok) {
 | |
|     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   nsString old;
 | |
|   aRv = mCache->SetItem(this, aKey, data, old);
 | |
|   if (aRv.Failed()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (!aRv.ErrorCodeIs(NS_SUCCESS_DOM_NO_OPERATION)) {
 | |
|     BroadcastChangeNotification(aKey, old, aData);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| LocalStorage::RemoveItem(const nsAString& aKey, nsIPrincipal& aSubjectPrincipal,
 | |
|                          ErrorResult& aRv)
 | |
| {
 | |
|   if (!CanUseStorage(aSubjectPrincipal)) {
 | |
|     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   nsAutoString old;
 | |
|   aRv = mCache->RemoveItem(this, aKey, old);
 | |
|   if (aRv.Failed()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (!aRv.ErrorCodeIs(NS_SUCCESS_DOM_NO_OPERATION)) {
 | |
|     BroadcastChangeNotification(aKey, old, VoidString());
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| LocalStorage::Clear(nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv)
 | |
| {
 | |
|   if (!CanUseStorage(aSubjectPrincipal)) {
 | |
|     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   aRv = mCache->Clear(this);
 | |
|   if (NS_WARN_IF(aRv.Failed())) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (!aRv.ErrorCodeIs(NS_SUCCESS_DOM_NO_OPERATION)) {
 | |
|     BroadcastChangeNotification(VoidString(), VoidString(), VoidString());
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| LocalStorage::BroadcastChangeNotification(const nsAString& aKey,
 | |
|                                           const nsAString& aOldValue,
 | |
|                                           const nsAString& aNewValue)
 | |
| {
 | |
|   if (Principal()) {
 | |
|     // We want to send a message to the parent in order to broadcast the
 | |
|     // StorageEvent correctly to any child process.
 | |
| 
 | |
|     PBackgroundChild* actor = BackgroundChild::GetForCurrentThread();
 | |
|     MOZ_ASSERT(actor);
 | |
| 
 | |
|     PrincipalInfo principalInfo;
 | |
|     nsresult rv = PrincipalToPrincipalInfo(Principal(), &principalInfo);
 | |
|     if (!NS_WARN_IF(NS_FAILED(rv))) {
 | |
|       Unused << NS_WARN_IF(!actor->SendBroadcastLocalStorageChange(
 | |
|         mDocumentURI, nsString(aKey), nsString(aOldValue), nsString(aNewValue),
 | |
|         principalInfo, mIsPrivate));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   DispatchStorageEvent(mDocumentURI, aKey, aOldValue, aNewValue,
 | |
|                        Principal(), mIsPrivate, this, false);
 | |
| }
 | |
| 
 | |
| /* static */ void
 | |
| LocalStorage::DispatchStorageEvent(const nsAString& aDocumentURI,
 | |
|                                    const nsAString& aKey,
 | |
|                                    const nsAString& aOldValue,
 | |
|                                    const nsAString& aNewValue,
 | |
|                                    nsIPrincipal* aPrincipal,
 | |
|                                    bool aIsPrivate,
 | |
|                                    Storage* aStorage,
 | |
|                                    bool aImmediateDispatch)
 | |
| {
 | |
|   NotifyChange(aStorage, aPrincipal, aKey, aOldValue, aNewValue,
 | |
|                u"localStorage", aDocumentURI, aIsPrivate, aImmediateDispatch);
 | |
| }
 | |
| 
 | |
| void
 | |
| LocalStorage::ApplyEvent(StorageEvent* aStorageEvent)
 | |
| {
 | |
|   MOZ_ASSERT(aStorageEvent);
 | |
| 
 | |
|   nsAutoString key;
 | |
|   nsAutoString old;
 | |
|   nsAutoString value;
 | |
| 
 | |
|   aStorageEvent->GetKey(key);
 | |
|   aStorageEvent->GetNewValue(value);
 | |
| 
 | |
|   // No key means clearing the full storage.
 | |
|   if (key.IsVoid()) {
 | |
|     MOZ_ASSERT(value.IsVoid());
 | |
|     mCache->Clear(this, LocalStorageCache::E10sPropagated);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // No new value means removing the key.
 | |
|   if (value.IsVoid()) {
 | |
|     mCache->RemoveItem(this, key, old, LocalStorageCache::E10sPropagated);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Otherwise, we set the new value.
 | |
|   mCache->SetItem(this, key, value, old, LocalStorageCache::E10sPropagated);
 | |
| }
 | |
| 
 | |
| bool
 | |
| LocalStorage::PrincipalEquals(nsIPrincipal* aPrincipal)
 | |
| {
 | |
|   return StorageUtils::PrincipalsEqual(mPrincipal, aPrincipal);
 | |
| }
 | |
| 
 | |
| void
 | |
| LocalStorage::GetSupportedNames(nsTArray<nsString>& aKeys)
 | |
| {
 | |
|   if (!CanUseStorage(*nsContentUtils::SubjectPrincipal())) {
 | |
|     // return just an empty array
 | |
|     aKeys.Clear();
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   mCache->GetKeys(this, aKeys);
 | |
| }
 | |
| 
 | |
| bool
 | |
| LocalStorage::IsForkOf(const Storage* aOther) const
 | |
| {
 | |
|   MOZ_ASSERT(aOther);
 | |
|   if (aOther->Type() != eLocalStorage) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   return mCache == static_cast<const LocalStorage*>(aOther)->mCache;
 | |
| }
 | |
| 
 | |
| } // namespace dom
 | |
| } // namespace mozilla
 | 
