forked from mirrors/gecko-dev
This patch adds a new IPDL protocol PBackgroundLocalStorageCache. It is used by LocalStorageCache object to broadcast changes in local storage cache to other content processes. Each origin has its own PBackgroundLocalStorageCache, so now we can notify content processes that actually have a local storage cache for given origin. This greatly improves performance and reduces memory footprint especialy when local storage changes carry big strings and/or happen very quickly (before this patch all child processes were blindly notified).
254 lines
6 KiB
C++
254 lines
6 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)) {
|
|
OnChange(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)) {
|
|
OnChange(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)) {
|
|
OnChange(VoidString(), VoidString(), VoidString());
|
|
}
|
|
}
|
|
|
|
void
|
|
LocalStorage::OnChange(const nsAString& aKey,
|
|
const nsAString& aOldValue,
|
|
const nsAString& aNewValue)
|
|
{
|
|
NotifyChange(/* aStorage */ this,
|
|
Principal(),
|
|
aKey,
|
|
aOldValue,
|
|
aNewValue,
|
|
/* aStorageType */ u"localStorage",
|
|
mDocumentURI,
|
|
mIsPrivate,
|
|
/* aImmediateDispatch */ false);
|
|
}
|
|
|
|
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
|